@goodandready/dsh-opencode-provider 0.5.1 → 0.5.2
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/CHANGELOG.md +10 -0
- package/README.md +306 -301
- package/README.ru.md +207 -202
- package/README.zh.md +183 -177
- package/lib/client.js +223 -4
- package/lib/index.d.ts +31 -1
- package/lib/index.js +188 -32
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -38,6 +38,10 @@ window.__ModuleLoader__.load({
|
|
|
38
38
|
accountRef: "Credential ref",
|
|
39
39
|
accountSource: "Source",
|
|
40
40
|
addAccount: "Add account",
|
|
41
|
+
importAllAuth: "Import all from auth.json",
|
|
42
|
+
checkAllAccounts: "Check all accounts",
|
|
43
|
+
importedAll: "Imported all auth.json keys; review the accounts and save.",
|
|
44
|
+
accountsImported: "Imported keys",
|
|
41
45
|
removeAccount: "Remove",
|
|
42
46
|
pinAccount: "Pin",
|
|
43
47
|
activeAccount: "Pinned account",
|
|
@@ -107,6 +111,8 @@ window.__ModuleLoader__.load({
|
|
|
107
111
|
heatmapByHour: "By hour (all days)",
|
|
108
112
|
heatmapAllModels: "All models",
|
|
109
113
|
heatmapModel: "Model",
|
|
114
|
+
heatmapByAccount: "Account",
|
|
115
|
+
heatmapAllAccounts: "All accounts",
|
|
110
116
|
analyticsHint: "Aggregated from local session metadata by last activity. A session that switched models is counted under its last model; failures are not exposed by the projection.",
|
|
111
117
|
analyticsEmpty: "No OpenCode sessions in this window.",
|
|
112
118
|
showAll: "Show all",
|
|
@@ -144,6 +150,21 @@ window.__ModuleLoader__.load({
|
|
|
144
150
|
maxOutputTokens: "Output limit (empty = model default, 0 = omit)",
|
|
145
151
|
contextWindow: "Context override",
|
|
146
152
|
showSidebarQuota: "Show the quota card in the sidebar",
|
|
153
|
+
routing: "Routing",
|
|
154
|
+
routingHint: "Recent account/model choices. No prompts or keys are recorded.",
|
|
155
|
+
routingEmpty: "No routing decisions yet.",
|
|
156
|
+
routingRule: "Model rule",
|
|
157
|
+
routingSticky: "Sticky session",
|
|
158
|
+
routingPinned: "Pinned account",
|
|
159
|
+
routingDefault: "Automatic",
|
|
160
|
+
routingRotation: "Rotation",
|
|
161
|
+
routingFallback: "Quota fallback",
|
|
162
|
+
routingFailed: "Failed",
|
|
163
|
+
quotaFallbackModel: "Quota fallback model",
|
|
164
|
+
quotaFallbackModelHint: "If Go rejects with a quota error, retry once on this free model through Console/Zen.",
|
|
165
|
+
stickySession: "Sticky account per conversation",
|
|
166
|
+
stickySessionHint: "Prefer the last successful account for the same conversation; explicit model rules and pinned accounts win.",
|
|
167
|
+
off: "Off",
|
|
147
168
|
pluginUpdate: "Plugin update",
|
|
148
169
|
currentVersion: "Current version",
|
|
149
170
|
latestVersion: "Latest version",
|
|
@@ -195,6 +216,10 @@ window.__ModuleLoader__.load({
|
|
|
195
216
|
accountRef: "凭据引用",
|
|
196
217
|
accountSource: "来源",
|
|
197
218
|
addAccount: "添加账户",
|
|
219
|
+
importAllAuth: "从 auth.json 导入全部",
|
|
220
|
+
checkAllAccounts: "检查全部账户",
|
|
221
|
+
importedAll: "已导入 auth.json 全部密钥;请检查账户并保存。",
|
|
222
|
+
accountsImported: "已导入密钥",
|
|
198
223
|
removeAccount: "移除",
|
|
199
224
|
pinAccount: "固定",
|
|
200
225
|
activeAccount: "固定账户",
|
|
@@ -264,6 +289,8 @@ window.__ModuleLoader__.load({
|
|
|
264
289
|
heatmapByHour: "按小时(全部日期)",
|
|
265
290
|
heatmapAllModels: "全部模型",
|
|
266
291
|
heatmapModel: "模型",
|
|
292
|
+
heatmapByAccount: "账户",
|
|
293
|
+
heatmapAllAccounts: "全部账户",
|
|
267
294
|
analyticsHint: "按最后活动时间聚合本地会话元数据。切换过模型的会话计入最后使用的模型;本地投影不暴露失败。",
|
|
268
295
|
analyticsEmpty: "此窗口内没有 OpenCode 会话。",
|
|
269
296
|
showAll: "显示全部",
|
|
@@ -301,6 +328,21 @@ window.__ModuleLoader__.load({
|
|
|
301
328
|
maxOutputTokens: "输出上限(空 = 模型默认,0 = 省略)",
|
|
302
329
|
contextWindow: "上下文覆盖",
|
|
303
330
|
showSidebarQuota: "在侧边栏显示配额卡片",
|
|
331
|
+
routing: "路由",
|
|
332
|
+
routingHint: "最近使用的账户/模型选择。不会记录提示词或密钥。",
|
|
333
|
+
routingEmpty: "尚无路由决策。",
|
|
334
|
+
routingRule: "模型规则",
|
|
335
|
+
routingSticky: "会话粘性",
|
|
336
|
+
routingPinned: "固定账户",
|
|
337
|
+
routingDefault: "自动",
|
|
338
|
+
routingRotation: "轮换",
|
|
339
|
+
routingFallback: "配额回退",
|
|
340
|
+
routingFailed: "失败",
|
|
341
|
+
quotaFallbackModel: "配额回退模型",
|
|
342
|
+
quotaFallbackModelHint: "Go 返回配额错误时,通过 Console/Zen 用此免费模型重试一次。",
|
|
343
|
+
stickySession: "每个会话固定账户",
|
|
344
|
+
stickySessionHint: "同一会话优先使用上次成功的账户;显式模型规则和固定账户优先。",
|
|
345
|
+
off: "关闭",
|
|
304
346
|
pluginUpdate: "插件更新",
|
|
305
347
|
currentVersion: "当前版本",
|
|
306
348
|
latestVersion: "最新版本",
|
|
@@ -386,10 +428,13 @@ window.__ModuleLoader__.load({
|
|
|
386
428
|
body: JSON.stringify({ ref })
|
|
387
429
|
});
|
|
388
430
|
}
|
|
389
|
-
function importAuth(entry) {
|
|
431
|
+
function importAuth(entry, ref) {
|
|
390
432
|
return call("/dsh-opencode/auth-import", {
|
|
391
433
|
method: "POST",
|
|
392
|
-
body: JSON.stringify({ entry }
|
|
434
|
+
body: JSON.stringify(ref === void 0 ? { entry } : {
|
|
435
|
+
entry,
|
|
436
|
+
ref
|
|
437
|
+
})
|
|
393
438
|
});
|
|
394
439
|
}
|
|
395
440
|
function fetchAnalytics(days = 30) {
|
|
@@ -429,10 +474,12 @@ window.__ModuleLoader__.load({
|
|
|
429
474
|
function RequestHeatmapView(props) {
|
|
430
475
|
const { heatmap, t } = props;
|
|
431
476
|
const [selected, setSelected] = (0, react.useState)("all");
|
|
477
|
+
const [selectedAccount, setSelectedAccount] = (0, react.useState)("all");
|
|
432
478
|
if (heatmap === void 0) return null;
|
|
433
479
|
const models = heatmap.models ?? [];
|
|
434
|
-
const
|
|
435
|
-
const
|
|
480
|
+
const accounts = heatmap.accounts ?? [];
|
|
481
|
+
const selectedModel = selectedAccount === "all" ? models.find((entry) => `${entry.provider}/${entry.model}` === selected) : void 0;
|
|
482
|
+
const view = accounts.find((entry) => entry.account === selectedAccount) ?? selectedModel ?? heatmap;
|
|
436
483
|
const selectedKey = selectedModel === void 0 ? "all" : selected;
|
|
437
484
|
const max = Math.max(0, ...view.grid.flat());
|
|
438
485
|
const maxHours = Math.max(0, ...view.hours);
|
|
@@ -452,10 +499,37 @@ window.__ModuleLoader__.load({
|
|
|
452
499
|
t("requests")
|
|
453
500
|
]
|
|
454
501
|
}),
|
|
502
|
+
accounts.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
503
|
+
className: "oc-select",
|
|
504
|
+
value: selectedAccount,
|
|
505
|
+
title: t("heatmapByAccount"),
|
|
506
|
+
onChange: (event) => {
|
|
507
|
+
setSelectedAccount(event.target.value);
|
|
508
|
+
setSelected("all");
|
|
509
|
+
},
|
|
510
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
511
|
+
value: "all",
|
|
512
|
+
children: [
|
|
513
|
+
t("heatmapAllAccounts"),
|
|
514
|
+
" (",
|
|
515
|
+
heatmap.total,
|
|
516
|
+
")"
|
|
517
|
+
]
|
|
518
|
+
}), accounts.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
519
|
+
value: entry.account,
|
|
520
|
+
children: [
|
|
521
|
+
entry.account,
|
|
522
|
+
" (",
|
|
523
|
+
entry.total,
|
|
524
|
+
")"
|
|
525
|
+
]
|
|
526
|
+
}, entry.account))]
|
|
527
|
+
}) : null,
|
|
455
528
|
models.length > 1 ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
456
529
|
className: "oc-select",
|
|
457
530
|
value: selectedKey,
|
|
458
531
|
title: t("heatmapModel"),
|
|
532
|
+
disabled: selectedAccount !== "all",
|
|
459
533
|
onChange: (event) => setSelected(event.target.value),
|
|
460
534
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
461
535
|
value: "all",
|
|
@@ -907,6 +981,14 @@ window.__ModuleLoader__.load({
|
|
|
907
981
|
windows?.monthly
|
|
908
982
|
].map((window) => window?.status).find((value) => value !== void 0 && value !== "ok");
|
|
909
983
|
}
|
|
984
|
+
function routingLabel(reason, t) {
|
|
985
|
+
if (reason === "rule") return t("routingRule");
|
|
986
|
+
if (reason === "sticky") return t("routingSticky");
|
|
987
|
+
if (reason === "pinned") return t("routingPinned");
|
|
988
|
+
if (reason === "rotation") return t("routingRotation");
|
|
989
|
+
if (reason === "quota-fallback") return t("routingFallback");
|
|
990
|
+
return t("routingDefault");
|
|
991
|
+
}
|
|
910
992
|
function fillClass(value) {
|
|
911
993
|
if (value >= 90) return "oc-footFill oc-footFillBad";
|
|
912
994
|
if (value >= 75) return "oc-footFill oc-footFillWarn";
|
|
@@ -1222,6 +1304,43 @@ window.__ModuleLoader__.load({
|
|
|
1222
1304
|
})
|
|
1223
1305
|
]
|
|
1224
1306
|
}),
|
|
1307
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1308
|
+
className: "oc-card",
|
|
1309
|
+
children: [
|
|
1310
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
1311
|
+
className: "oc-card-title",
|
|
1312
|
+
children: t("routing")
|
|
1313
|
+
}),
|
|
1314
|
+
(state?.routing ?? []).length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1315
|
+
className: "oc-hint",
|
|
1316
|
+
children: t("routingEmpty")
|
|
1317
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1318
|
+
className: "oc-table-scroll",
|
|
1319
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("table", {
|
|
1320
|
+
className: "oc-table",
|
|
1321
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("thead", { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1322
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("date") }),
|
|
1323
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("accountLabel") }),
|
|
1324
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("model") }),
|
|
1325
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("th", { children: t("routing") })
|
|
1326
|
+
] }) }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("tbody", { children: (state?.routing ?? []).slice(0, 8).map((row) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("tr", { children: [
|
|
1327
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: new Date(row.at).toLocaleTimeString() }),
|
|
1328
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: row.account }),
|
|
1329
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("td", { children: row.model }),
|
|
1330
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("td", { children: [
|
|
1331
|
+
routingLabel(row.reason, t),
|
|
1332
|
+
row.fallback ? ` · ${t("routingFallback")}` : "",
|
|
1333
|
+
row.ok ? "" : ` · ${t("routingFailed")}${row.error === void 0 ? "" : ` (${row.error})`}`
|
|
1334
|
+
] })
|
|
1335
|
+
] }, `${row.at}-${row.account}-${row.model}-${row.reason}`)) })]
|
|
1336
|
+
})
|
|
1337
|
+
}),
|
|
1338
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1339
|
+
className: "oc-hint",
|
|
1340
|
+
children: t("routingHint")
|
|
1341
|
+
})
|
|
1342
|
+
]
|
|
1343
|
+
}),
|
|
1225
1344
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1226
1345
|
className: "oc-card",
|
|
1227
1346
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -1329,6 +1448,8 @@ window.__ModuleLoader__.load({
|
|
|
1329
1448
|
activeAccount: state.config.activeAccount,
|
|
1330
1449
|
accounts: state.config.accounts.map((account) => ({ ...account })),
|
|
1331
1450
|
modelAccountRules: state.config.modelAccountRules.map((rule) => ({ ...rule })),
|
|
1451
|
+
quotaFallbackModel: state.config.quotaFallbackModel,
|
|
1452
|
+
stickySession: state.config.stickySession,
|
|
1332
1453
|
visibleModels: state.config.visibleModels.length === 0 ? allIds : [...state.config.visibleModels],
|
|
1333
1454
|
filterModelsByPlan: state.config.filterModelsByPlan,
|
|
1334
1455
|
showAll: state.config.visibleModels.length === 0,
|
|
@@ -1364,6 +1485,8 @@ window.__ModuleLoader__.load({
|
|
|
1364
1485
|
pattern: rule.pattern.trim(),
|
|
1365
1486
|
account: rule.account.trim()
|
|
1366
1487
|
})).filter((rule) => rule.pattern !== "" && rule.account !== ""),
|
|
1488
|
+
quotaFallbackModel: draft.quotaFallbackModel.trim(),
|
|
1489
|
+
stickySession: draft.stickySession,
|
|
1367
1490
|
visibleModels: draft.showAll ? [] : draft.visibleModels,
|
|
1368
1491
|
filterModelsByPlan: draft.filterModelsByPlan,
|
|
1369
1492
|
modelTransports: Object.fromEntries(Object.entries(draft.modelTransports).filter(([, value]) => value !== ""))
|
|
@@ -1560,6 +1683,7 @@ window.__ModuleLoader__.load({
|
|
|
1560
1683
|
const [testModel, setTestModel] = (0, react.useState)("deepseek-v4.1-flash");
|
|
1561
1684
|
const [testing, setTesting] = (0, react.useState)(false);
|
|
1562
1685
|
const [testResult, setTestResult] = (0, react.useState)(null);
|
|
1686
|
+
const [importing, setImporting] = (0, react.useState)(false);
|
|
1563
1687
|
const load = (0, react.useCallback)(async () => {
|
|
1564
1688
|
setLoading(true);
|
|
1565
1689
|
try {
|
|
@@ -1610,6 +1734,29 @@ window.__ModuleLoader__.load({
|
|
|
1610
1734
|
setError(caught instanceof Error ? caught.message : t("failed"));
|
|
1611
1735
|
}
|
|
1612
1736
|
};
|
|
1737
|
+
const importAllAuth = async () => {
|
|
1738
|
+
if (state === null || draft === null) return;
|
|
1739
|
+
const refFor = (entry) => `OPENCODE_AUTH_${entry.replace(/[^A-Za-z0-9]+/g, "_").toUpperCase()}`;
|
|
1740
|
+
const missing = state.auth.entries.filter((entry) => !draft.accounts.some((account) => account.label === entry || account.apiKeyEnv === refFor(entry)));
|
|
1741
|
+
if (missing.length === 0) {
|
|
1742
|
+
setNotice(t("importedAll"));
|
|
1743
|
+
return;
|
|
1744
|
+
}
|
|
1745
|
+
setImporting(true);
|
|
1746
|
+
setError(null);
|
|
1747
|
+
try {
|
|
1748
|
+
for (const entry of missing) await importAuth(entry, refFor(entry));
|
|
1749
|
+
update({ accounts: [...draft.accounts, ...missing.map((entry) => ({
|
|
1750
|
+
label: entry,
|
|
1751
|
+
apiKeyEnv: refFor(entry)
|
|
1752
|
+
}))] });
|
|
1753
|
+
setNotice(t("importedAll"));
|
|
1754
|
+
} catch (caught) {
|
|
1755
|
+
setError(caught instanceof Error ? caught.message : t("failed"));
|
|
1756
|
+
} finally {
|
|
1757
|
+
setImporting(false);
|
|
1758
|
+
}
|
|
1759
|
+
};
|
|
1613
1760
|
if (loading && state === null) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1614
1761
|
className: "oc-hint",
|
|
1615
1762
|
children: t("loading")
|
|
@@ -2126,6 +2273,23 @@ window.__ModuleLoader__.load({
|
|
|
2126
2273
|
}] }),
|
|
2127
2274
|
children: t("addAccount")
|
|
2128
2275
|
}),
|
|
2276
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2277
|
+
type: "button",
|
|
2278
|
+
className: "oc-button",
|
|
2279
|
+
disabled: importing,
|
|
2280
|
+
onClick: () => {
|
|
2281
|
+
importAllAuth();
|
|
2282
|
+
},
|
|
2283
|
+
children: importing ? t("loading") : t("importAllAuth")
|
|
2284
|
+
}),
|
|
2285
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2286
|
+
type: "button",
|
|
2287
|
+
className: "oc-button",
|
|
2288
|
+
onClick: () => {
|
|
2289
|
+
refresh();
|
|
2290
|
+
},
|
|
2291
|
+
children: t("checkAllAccounts")
|
|
2292
|
+
}),
|
|
2129
2293
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
2130
2294
|
className: "oc-badge",
|
|
2131
2295
|
children: [
|
|
@@ -2206,6 +2370,61 @@ window.__ModuleLoader__.load({
|
|
|
2206
2370
|
})
|
|
2207
2371
|
]
|
|
2208
2372
|
}),
|
|
2373
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2374
|
+
className: "oc-card",
|
|
2375
|
+
children: [
|
|
2376
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
|
|
2377
|
+
className: "oc-card-title",
|
|
2378
|
+
children: t("routing")
|
|
2379
|
+
}),
|
|
2380
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2381
|
+
className: "oc-hint",
|
|
2382
|
+
children: t("routingHint")
|
|
2383
|
+
}),
|
|
2384
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2385
|
+
className: "oc-row",
|
|
2386
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
2387
|
+
type: "checkbox",
|
|
2388
|
+
checked: draft.stickySession,
|
|
2389
|
+
onChange: (event) => update({ stickySession: event.target.checked })
|
|
2390
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("stickySession") })]
|
|
2391
|
+
}),
|
|
2392
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2393
|
+
className: "oc-hint",
|
|
2394
|
+
children: t("stickySessionHint")
|
|
2395
|
+
}),
|
|
2396
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
2397
|
+
className: "oc-field",
|
|
2398
|
+
children: [
|
|
2399
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2400
|
+
className: "oc-label",
|
|
2401
|
+
children: t("quotaFallbackModel")
|
|
2402
|
+
}),
|
|
2403
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
2404
|
+
className: "oc-select",
|
|
2405
|
+
value: draft.quotaFallbackModel,
|
|
2406
|
+
onChange: (event) => update({ quotaFallbackModel: event.target.value }),
|
|
2407
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
2408
|
+
value: "",
|
|
2409
|
+
children: t("off")
|
|
2410
|
+
}), state.models.filter((model) => model.free).map((model) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("option", {
|
|
2411
|
+
value: model.id,
|
|
2412
|
+
children: [
|
|
2413
|
+
model.name,
|
|
2414
|
+
" (",
|
|
2415
|
+
model.id,
|
|
2416
|
+
")"
|
|
2417
|
+
]
|
|
2418
|
+
}, model.id))]
|
|
2419
|
+
}),
|
|
2420
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2421
|
+
className: "oc-hint",
|
|
2422
|
+
children: t("quotaFallbackModelHint")
|
|
2423
|
+
})
|
|
2424
|
+
]
|
|
2425
|
+
})
|
|
2426
|
+
]
|
|
2427
|
+
}),
|
|
2209
2428
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
2210
2429
|
className: "oc-card",
|
|
2211
2430
|
children: [
|
package/lib/index.d.ts
CHANGED
|
@@ -55,6 +55,10 @@ interface Config {
|
|
|
55
55
|
accounts?: AccountConfig[];
|
|
56
56
|
/** Preferred account per model glob; other accounts remain fallbacks. */
|
|
57
57
|
modelAccountRules?: ModelAccountRule[];
|
|
58
|
+
/** Free model used once when the Go quota rejects a request; empty disables it. */
|
|
59
|
+
quotaFallbackModel?: string;
|
|
60
|
+
/** Prefer the last successful account for a conversation. */
|
|
61
|
+
stickySession?: boolean;
|
|
58
62
|
/** Pinned account id; empty means automatic. */
|
|
59
63
|
activeAccount?: string;
|
|
60
64
|
/** Show the quota card in the sidebar. */
|
|
@@ -82,6 +86,8 @@ interface ResolvedConfig {
|
|
|
82
86
|
maxOutputTokens?: number;
|
|
83
87
|
accounts: readonly AccountConfig[];
|
|
84
88
|
modelAccountRules: readonly ModelAccountRule[];
|
|
89
|
+
quotaFallbackModel: string;
|
|
90
|
+
stickySession: boolean;
|
|
85
91
|
activeAccount: string;
|
|
86
92
|
showSidebarQuota: boolean;
|
|
87
93
|
userAgent: string;
|
|
@@ -111,6 +117,17 @@ declare function createKeyPool(ctx: Context, config: () => ResolvedConfig): KeyP
|
|
|
111
117
|
declare function createKeyResolver(ctx: Context, config: () => ResolvedConfig): KeyResolver;
|
|
112
118
|
//#endregion
|
|
113
119
|
//#region src/adapter.d.ts
|
|
120
|
+
type RoutingReason = 'rule' | 'sticky' | 'pinned' | 'default' | 'rotation' | 'quota-fallback';
|
|
121
|
+
interface RoutingTraceEntry {
|
|
122
|
+
readonly at: number;
|
|
123
|
+
readonly provider: string;
|
|
124
|
+
readonly model: string;
|
|
125
|
+
readonly account: string;
|
|
126
|
+
readonly reason: RoutingReason;
|
|
127
|
+
readonly fallback: boolean;
|
|
128
|
+
readonly ok: boolean;
|
|
129
|
+
readonly error?: string;
|
|
130
|
+
}
|
|
114
131
|
interface OpenCodeAdapterDeps {
|
|
115
132
|
readonly config: () => ResolvedConfig;
|
|
116
133
|
readonly resolveKey: KeyResolver;
|
|
@@ -118,7 +135,7 @@ interface OpenCodeAdapterDeps {
|
|
|
118
135
|
readonly resolveAttachments?: () => AttachmentStore | undefined;
|
|
119
136
|
readonly fetchImpl?: typeof fetch;
|
|
120
137
|
/** Test seam; defaults to the local append-only request log. */
|
|
121
|
-
readonly recordRequest?: (provider: string, model: string, purpose?: string) => void;
|
|
138
|
+
readonly recordRequest?: (provider: string, model: string, purpose?: string, account?: string, reason?: string) => void;
|
|
122
139
|
/** Test seam; defaults to ~/.dsh/storages/dsh-opencode/cooldowns.json. */
|
|
123
140
|
readonly cooldownPath?: string;
|
|
124
141
|
/** Receives a one-time warning when the provider tool limit drops tools. */
|
|
@@ -131,6 +148,8 @@ declare class OpenCodeAdapter extends LlmAdapter {
|
|
|
131
148
|
private cooldownWrites;
|
|
132
149
|
/** key fingerprint → cooldown deadline and the rejection that caused it. */
|
|
133
150
|
private readonly cooldowns;
|
|
151
|
+
private readonly sticky;
|
|
152
|
+
private readonly routing;
|
|
134
153
|
private warnedTools;
|
|
135
154
|
constructor(deps: OpenCodeAdapterDeps);
|
|
136
155
|
providerInfo(provider: string): LlmProviderInfo;
|
|
@@ -138,9 +157,13 @@ declare class OpenCodeAdapter extends LlmAdapter {
|
|
|
138
157
|
listModels(provider: string, opts?: {
|
|
139
158
|
unfiltered?: boolean;
|
|
140
159
|
}): Promise<readonly LlmModelInfo[]>;
|
|
160
|
+
/** Recent routing decisions, newest first; no keys or prompt content. */
|
|
161
|
+
routingTrace(): readonly RoutingTraceEntry[];
|
|
141
162
|
resolveModel(provider: string, model: string): Promise<LlmResolvedModelInfo>;
|
|
142
163
|
stream(options: GenerateOptions): AsyncIterable<StreamChunk>;
|
|
143
164
|
private run;
|
|
165
|
+
private rememberSticky;
|
|
166
|
+
private pushTrace;
|
|
144
167
|
private parse;
|
|
145
168
|
private send;
|
|
146
169
|
private keyId;
|
|
@@ -292,6 +315,12 @@ interface ModelRequestHeatmap {
|
|
|
292
315
|
hours: number[];
|
|
293
316
|
grid: number[][];
|
|
294
317
|
}
|
|
318
|
+
interface AccountRequestHeatmap {
|
|
319
|
+
account: string;
|
|
320
|
+
total: number;
|
|
321
|
+
hours: number[];
|
|
322
|
+
grid: number[][];
|
|
323
|
+
}
|
|
295
324
|
interface RequestHeatmap {
|
|
296
325
|
since: number;
|
|
297
326
|
total: number;
|
|
@@ -302,6 +331,7 @@ interface RequestHeatmap {
|
|
|
302
331
|
firstAt: number | null;
|
|
303
332
|
lastAt: number | null;
|
|
304
333
|
models: ModelRequestHeatmap[];
|
|
334
|
+
accounts: AccountRequestHeatmap[];
|
|
305
335
|
}
|
|
306
336
|
//#endregion
|
|
307
337
|
//#region src/routes.d.ts
|