custom-provider-pi 0.1.6 → 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/custom-provider.ts +20 -18
- package/package.json +1 -1
package/custom-provider.ts
CHANGED
|
@@ -36,10 +36,10 @@ const UA_PRESETS: Record<string, string> = {
|
|
|
36
36
|
|
|
37
37
|
// 请求头模板(借鉴 LiveAgent:按客户端/CLI 预设整组请求头,而非只预设 UA):
|
|
38
38
|
// 不同客户端携带的头集合不同(Claude Code 有 x-app/anthropic-version/X-Stainless-* 等)。
|
|
39
|
-
//
|
|
39
|
+
// Apply template headers; or select"custom"逐头输入。
|
|
40
40
|
interface HeaderPreset {
|
|
41
41
|
label: string;
|
|
42
|
-
key?: string; // undefined =
|
|
42
|
+
key?: string; // undefined = custom
|
|
43
43
|
headers: Record<string, string>;
|
|
44
44
|
}
|
|
45
45
|
const HEADER_PRESETS: HeaderPreset[] = [
|
|
@@ -69,7 +69,7 @@ const HEADER_PRESETS: HeaderPreset[] = [
|
|
|
69
69
|
headers: { "User-Agent": UA_PRESETS.openwebui, "accept": "application/json" },
|
|
70
70
|
},
|
|
71
71
|
{ label: "ChatGPT Desktop", key: "chatgpt", headers: { "User-Agent": UA_PRESETS.chatgpt } },
|
|
72
|
-
{ label: "
|
|
72
|
+
{ label: "custom(逐头输入)", key: undefined, headers: {} },
|
|
73
73
|
];
|
|
74
74
|
|
|
75
75
|
// 头名校验:HTTP token 字符集(含 ASCII 特殊符号,无空格/换行)
|
|
@@ -91,8 +91,8 @@ function hasHeader(headers: Record<string, string> | undefined, name: string): b
|
|
|
91
91
|
// ---- 远程规格查询(OpenRouter 公开目录,带磁盘缓存)----
|
|
92
92
|
|
|
93
93
|
// 输出上限处置(借鉴 LiveAgent normalizeModelLimits)
|
|
94
|
-
//
|
|
95
|
-
// 照单全收会把"
|
|
94
|
+
// Community catalogs may provide degraded values for models"output==window",
|
|
95
|
+
// 照单全收会把"window-output-reserve"的输入预算挤成零。处理:钳到保守上限,
|
|
96
96
|
// 并保底留 3/4 窗口给输入。
|
|
97
97
|
const MAX_OUTPUT_TOKEN_CAP = 32000;
|
|
98
98
|
|
|
@@ -1146,6 +1146,8 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
1146
1146
|
// ---- LB key 轮询 ----
|
|
1147
1147
|
const poolName = event.headers["X-LB-POOL"] as string | undefined;
|
|
1148
1148
|
if (!poolName) return;
|
|
1149
|
+
// 保留 pool name 到响应阶段(after_provider_response 需要它做冷却回调)
|
|
1150
|
+
event.headers["X-LB-POOL-ID"] = poolName;
|
|
1149
1151
|
delete event.headers["X-LB-POOL"];
|
|
1150
1152
|
|
|
1151
1153
|
const pool = lbPools.get(poolName);
|
|
@@ -1167,8 +1169,8 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
1167
1169
|
|
|
1168
1170
|
pi.on("after_provider_response", (event) => {
|
|
1169
1171
|
// 从 headers 中读取 key 索引(before_provider_headers 中设置)
|
|
1170
|
-
const poolName = event.headers["
|
|
1171
|
-
const keyIdxStr = event.headers["
|
|
1172
|
+
const poolName = event.headers["X-LB-POOL-ID"];
|
|
1173
|
+
const keyIdxStr = event.headers["X-LB-KEY-INDEX"];
|
|
1172
1174
|
if (!poolName || !keyIdxStr) return;
|
|
1173
1175
|
|
|
1174
1176
|
const pool = lbPools.get(poolName);
|
|
@@ -1265,10 +1267,10 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
1265
1267
|
}
|
|
1266
1268
|
}
|
|
1267
1269
|
|
|
1268
|
-
// ---- 选择添加方式:简单(自动探测+模型)/
|
|
1270
|
+
// ---- 选择添加方式:简单(自动探测+模型)/ custom(完整配置)----
|
|
1269
1271
|
const addMode = await ctx.ui.select(
|
|
1270
1272
|
"添加方式?",
|
|
1271
|
-
["简单添加(自动探测协议 + 模型列表)", "
|
|
1273
|
+
["简单添加(自动探测协议 + 模型列表)", "custom配置(代理/LB/请求头模板/高级选项)"]
|
|
1272
1274
|
);
|
|
1273
1275
|
if (!addMode) return;
|
|
1274
1276
|
|
|
@@ -1335,7 +1337,7 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
1335
1337
|
return;
|
|
1336
1338
|
}
|
|
1337
1339
|
|
|
1338
|
-
// =================
|
|
1340
|
+
// ================= custom配置路径(完整向导 2-10 步)=================
|
|
1339
1341
|
// ---- 2. 端点 ----
|
|
1340
1342
|
const baseUrl = await ctx.ui.input(
|
|
1341
1343
|
"API 端点 URL(完整地址,通常含 /v1;粘贴 /v1/models 也会被自动清理)",
|
|
@@ -1370,7 +1372,7 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
1370
1372
|
|
|
1371
1373
|
// ---- 4. 协议类型 ----
|
|
1372
1374
|
const apiType = await ctx.ui.select(
|
|
1373
|
-
"API
|
|
1375
|
+
"API protocol type (auto-detect recommended)",
|
|
1374
1376
|
["自动推断", "openai-completions", "openai-responses", "anthropic-messages", "google-generative-ai"]
|
|
1375
1377
|
);
|
|
1376
1378
|
if (!apiType) return;
|
|
@@ -1438,7 +1440,7 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
1438
1440
|
"若服务支持模型列表端点可自动获取;否则将手动输入"
|
|
1439
1441
|
);
|
|
1440
1442
|
|
|
1441
|
-
// ---- 6. 请求头模板(预设整组请求头,不同 CLI 头集合不同;选"
|
|
1443
|
+
// ---- 6. 请求头模板(预设整组请求头,不同 CLI 头集合不同;选"custom"逐头输入)----
|
|
1442
1444
|
const presetLabels = HEADER_PRESETS.map((p) => p.label);
|
|
1443
1445
|
const presetChoice = await ctx.ui.select(
|
|
1444
1446
|
"请求头模板?",
|
|
@@ -1477,9 +1479,9 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
1477
1479
|
);
|
|
1478
1480
|
} // 浏览器模板 = 不写头,由 pi 自动补充浏览器 UA
|
|
1479
1481
|
} else {
|
|
1480
|
-
//
|
|
1482
|
+
// custom:JSON 对象,或每行 "Key: Value"(值支持 $ENV)
|
|
1481
1483
|
const headersInput = await ctx.ui.input(
|
|
1482
|
-
'
|
|
1484
|
+
'custom请求头(JSON 对象,或每行 "Key: Value":值支持 $ENV;留空取消)',
|
|
1483
1485
|
'{"X-API-Key": "$MY_API_KEY"}'
|
|
1484
1486
|
);
|
|
1485
1487
|
if (headersInput && headersInput.trim()) {
|
|
@@ -1487,7 +1489,7 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
1487
1489
|
try {
|
|
1488
1490
|
const parsed = JSON.parse(t);
|
|
1489
1491
|
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
|
1490
|
-
applyHeaderSet({ ...parsed }, "
|
|
1492
|
+
applyHeaderSet({ ...parsed }, "custom请求头");
|
|
1491
1493
|
} else {
|
|
1492
1494
|
ctx.ui.notify("JSON 需为对象({...})", "error");
|
|
1493
1495
|
}
|
|
@@ -1507,13 +1509,13 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
1507
1509
|
}
|
|
1508
1510
|
kv[line.slice(0, idx).trim()] = line.slice(idx + 1).trim();
|
|
1509
1511
|
}
|
|
1510
|
-
if (parseOk) applyHeaderSet(kv, "
|
|
1512
|
+
if (parseOk) applyHeaderSet(kv, "custom请求头");
|
|
1511
1513
|
}
|
|
1512
1514
|
}
|
|
1513
1515
|
}
|
|
1514
1516
|
}
|
|
1515
1517
|
|
|
1516
|
-
// ---- 7.
|
|
1518
|
+
// ---- 7. 补充/覆盖请求头(在模板/custom基础上追加,可覆盖;无需直接跳过)----
|
|
1517
1519
|
const needMore = await ctx.ui.confirm(
|
|
1518
1520
|
"需要补充/覆盖请求头吗?",
|
|
1519
1521
|
"在现有头部上追加或覆盖(如 X-API-Key),值支持 $ENV 插值"
|
|
@@ -2442,7 +2444,7 @@ export default function customProviderExtension(pi: ExtensionAPI) {
|
|
|
2442
2444
|
description: "管理第三方 Provider:add / remove / refresh / list / test / help",
|
|
2443
2445
|
getArgumentCompletions: (prefix: string) => {
|
|
2444
2446
|
// 注意: pi 会用 item.value 替换整个参数段(命令名后的全部文本),
|
|
2445
|
-
//
|
|
2447
|
+
// So value must be"subcommand + full-name"的完整参数,label 才是用于显示的名称。
|
|
2446
2448
|
const trimmed = prefix.trim();
|
|
2447
2449
|
const match = trimmed.match(/^(\S+)(?:\s+(.*))?$/);
|
|
2448
2450
|
const first = (match?.[1] ?? "").toLowerCase();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "custom-provider-pi",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.8",
|
|
4
4
|
"description": "pi 扩展:统一管理第三方模型 Provider。子命令体系 /custom-provider add|remove|refresh|list|test|config|enable|disable|prune,支持交互向导、flags/JSON 非交互添加、双协议混用(OpenAI + Anthropic)、模型关键字过滤与修剪、per-provider 代理配置、负载均衡",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|