@ljwei-stak/dsh-model-router 0.16.0 → 0.16.1
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/.dsh-plugin/client.js +182 -50
- package/.dsh-plugin/index.mjs +23 -17
- package/.dsh-plugin/shared/adaptive-feedback.mjs +120 -40
- package/.dsh-plugin/shared/dynamic-data-view.mjs +59 -4
- package/.dsh-plugin/shared/dynamic-data.mjs +161 -83
- package/.dsh-plugin/shared/router-state.mjs +139 -38
- package/.dsh-plugin/shared/run-ledger.mjs +32 -11
- package/CHANGELOG.md +11 -0
- package/INSTALLATION_GUIDE.zh.md +12 -12
- package/README.md +7 -5
- package/README.zh.md +7 -5
- package/docs/ROUTING_ADAPTIVE_LEARNING.zh.md +2 -0
- package/docs/ROUTING_MATURITY.zh.md +76 -0
- package/package.json +2 -1
package/.dsh-plugin/client.js
CHANGED
|
@@ -1591,18 +1591,27 @@ function applyQualityBiases(routes, biases) {
|
|
|
1591
1591
|
var finite2 = (value) => typeof value === "number" && Number.isFinite(value);
|
|
1592
1592
|
var clean3 = (value) => typeof value === "string" ? value.trim() : "";
|
|
1593
1593
|
var routeKey3 = (provider, model) => `${provider}\0${model}`;
|
|
1594
|
+
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1594
1595
|
function applyFeedbackProfile(routes, profile) {
|
|
1595
|
-
if (!Array.isArray(routes) ||
|
|
1596
|
+
if (!Array.isArray(routes) || !record(profile) || typeof profile.enabled !== "boolean") return routes;
|
|
1596
1597
|
return routes.map((route) => {
|
|
1597
|
-
if (!route
|
|
1598
|
+
if (!record(route)) return route;
|
|
1599
|
+
let base = route;
|
|
1600
|
+
if (Object.hasOwn(route, "preferenceAdjustments") || Object.hasOwn(route, "preferenceAdjustmentMeta")) {
|
|
1601
|
+
base = { ...route };
|
|
1602
|
+
delete base.preferenceAdjustments;
|
|
1603
|
+
delete base.preferenceAdjustmentMeta;
|
|
1604
|
+
}
|
|
1605
|
+
if (profile.enabled !== true || !record(profile.adjustments)) return base;
|
|
1598
1606
|
const key = routeKey3(clean3(route.provider), clean3(route.model));
|
|
1599
|
-
if (!Object.hasOwn(profile.adjustments, key)) return
|
|
1607
|
+
if (!Object.hasOwn(profile.adjustments, key)) return base;
|
|
1600
1608
|
const value = profile.adjustments[key];
|
|
1601
|
-
if (!
|
|
1602
|
-
const
|
|
1603
|
-
|
|
1609
|
+
if (!record(value)) return base;
|
|
1610
|
+
const limit = finite2(profile.maxAdjustment) && profile.maxAdjustment >= 0 && profile.maxAdjustment <= 0.1 ? profile.maxAdjustment : 0.1;
|
|
1611
|
+
const adjustments = Object.fromEntries(Object.entries(value).filter(([, bias]) => finite2(bias) && Math.abs(bias) <= limit));
|
|
1612
|
+
if (!Object.keys(adjustments).length) return base;
|
|
1604
1613
|
return {
|
|
1605
|
-
...
|
|
1614
|
+
...base,
|
|
1606
1615
|
preferenceAdjustments: adjustments,
|
|
1607
1616
|
preferenceAdjustmentMeta: {
|
|
1608
1617
|
policyVersion: profile.policyVersion,
|
|
@@ -1613,7 +1622,11 @@ function applyFeedbackProfile(routes, profile) {
|
|
|
1613
1622
|
updatedAt: profile.updatedAt,
|
|
1614
1623
|
halfLifeDays: profile.halfLifeDays,
|
|
1615
1624
|
maxAdjustment: profile.maxAdjustment,
|
|
1616
|
-
window: profile.window
|
|
1625
|
+
window: profile.window,
|
|
1626
|
+
cellStats: Object.hasOwn(profile.cellStats ?? {}, key) ? profile.cellStats[key] : {},
|
|
1627
|
+
evaluatedAt: profile.evaluatedAt,
|
|
1628
|
+
decayRefreshMs: profile.decayRefreshMs,
|
|
1629
|
+
revisionSemantics: profile.revisionSemantics
|
|
1617
1630
|
}
|
|
1618
1631
|
};
|
|
1619
1632
|
});
|
|
@@ -2176,8 +2189,10 @@ function feedbackRatingArguments(run, item, direction) {
|
|
|
2176
2189
|
var nonnegative2 = (value) => Number.isFinite(value) && value >= 0 ? value : 0;
|
|
2177
2190
|
function learningView(learning, settings = {}) {
|
|
2178
2191
|
return {
|
|
2179
|
-
available: Boolean(learning && typeof learning === "object"),
|
|
2180
|
-
|
|
2192
|
+
available: Boolean(learning && typeof learning === "object" && !Array.isArray(learning)),
|
|
2193
|
+
// Current settings govern the next plan; a failed refresh must not make an
|
|
2194
|
+
// older profile claim that a newly disabled policy is still enabled.
|
|
2195
|
+
enabled: adaptiveSettingValue(settings, "feedbackLearningEnabled") !== false,
|
|
2181
2196
|
feedbackCount: nonnegative2(learning?.feedbackCount),
|
|
2182
2197
|
ignoredCount: nonnegative2(learning?.ignoredCount),
|
|
2183
2198
|
effectiveWeight: nonnegative2(learning?.effectiveWeight),
|
|
@@ -2185,18 +2200,71 @@ function learningView(learning, settings = {}) {
|
|
|
2185
2200
|
resetAt: nonnegative2(adaptiveSettingValue(settings, "feedbackResetAt"))
|
|
2186
2201
|
};
|
|
2187
2202
|
}
|
|
2203
|
+
function learningEvidenceRows(learning, limit = 12) {
|
|
2204
|
+
const cells = learning?.cellStats;
|
|
2205
|
+
if (!cells || typeof cells !== "object" || Array.isArray(cells)) return { rows: [], total: 0 };
|
|
2206
|
+
const rows = [];
|
|
2207
|
+
for (const [key, domains] of Object.entries(cells)) {
|
|
2208
|
+
const identity = key.split("\0");
|
|
2209
|
+
if (identity.length !== 2 || identity.some((part) => !part.trim()) || !domains || typeof domains !== "object" || Array.isArray(domains)) continue;
|
|
2210
|
+
for (const [domain, cell] of Object.entries(domains)) {
|
|
2211
|
+
if (!domain.trim() || !cell || typeof cell !== "object" || Array.isArray(cell) || !Number.isFinite(cell.adjustment) || Math.abs(cell.adjustment) > 0.1 || ["feedbackCount", "positiveCount", "negativeCount", "effectiveWeight", "effectiveSampleSize"].some((name) => !Number.isFinite(cell[name]) || cell[name] < 0) || !Number.isFinite(cell.shrinkage) || cell.shrinkage < 0 || cell.shrinkage > 1) continue;
|
|
2212
|
+
rows.push({
|
|
2213
|
+
id: JSON.stringify([key, domain]),
|
|
2214
|
+
route: identity.map((part) => part.slice(0, 100)).join("/"),
|
|
2215
|
+
domain: domain.slice(0, 80),
|
|
2216
|
+
feedbackCount: nonnegative2(cell.feedbackCount),
|
|
2217
|
+
positiveCount: nonnegative2(cell.positiveCount),
|
|
2218
|
+
negativeCount: nonnegative2(cell.negativeCount),
|
|
2219
|
+
effectiveWeight: nonnegative2(cell.effectiveWeight),
|
|
2220
|
+
effectiveSampleSize: nonnegative2(cell.effectiveSampleSize),
|
|
2221
|
+
shrinkage: Number.isFinite(cell.shrinkage) && cell.shrinkage >= 0 && cell.shrinkage <= 1 ? cell.shrinkage : 0,
|
|
2222
|
+
adjustment: cell.adjustment
|
|
2223
|
+
});
|
|
2224
|
+
}
|
|
2225
|
+
}
|
|
2226
|
+
rows.sort((left, right) => left.route === right.route ? left.domain.localeCompare(right.domain) : left.route.localeCompare(right.route));
|
|
2227
|
+
const count = Number.isInteger(limit) && limit > 0 ? Math.min(limit, 50) : 12;
|
|
2228
|
+
return { rows: rows.slice(0, count), total: rows.length };
|
|
2229
|
+
}
|
|
2230
|
+
var EXCLUSION_LABELS = Object.freeze({
|
|
2231
|
+
"unrated": "\u5C1A\u672A\u8BC4\u4EF7\uFF08\u4E0D\u662F\u4E0D\u6EE1\u610F\uFF09",
|
|
2232
|
+
"not-successful": "\u6CA1\u6709\u6210\u529F\u6267\u884C\u7ED3\u679C",
|
|
2233
|
+
"cli-model-unverified": "CLI \u5B9E\u9645\u6A21\u578B\u672A\u6838\u9A8C",
|
|
2234
|
+
"before-reset": "\u7ED3\u679C\u65E9\u4E8E\u5B66\u4E60\u8D77\u70B9",
|
|
2235
|
+
"superseded": "\u5DF2\u88AB\u540C\u4E00\u7ED3\u679C\u7684\u65B0\u8BB0\u5F55\u66FF\u4EE3",
|
|
2236
|
+
"missing-identity": "\u7F3A\u5C11\u8FD0\u884C\u6216\u6B65\u9AA4\u6807\u8BC6",
|
|
2237
|
+
"invalid-route": "\u8DEF\u7EBF\u6807\u8BC6\u65E0\u6548",
|
|
2238
|
+
"invalid-time": "\u7ED3\u679C\u6216\u8BC4\u4EF7\u65F6\u95F4\u65E0\u6548",
|
|
2239
|
+
"future-time": "\u65F6\u95F4\u665A\u4E8E\u5F53\u524D\u65F6\u523B"
|
|
2240
|
+
});
|
|
2241
|
+
function learningExclusionRows(learning) {
|
|
2242
|
+
return Object.entries(EXCLUSION_LABELS).flatMap(([reason, label]) => {
|
|
2243
|
+
const count = nonnegative2(learning?.exclusionReasons?.[reason]);
|
|
2244
|
+
return count > 0 ? [{ reason, label, count }] : [];
|
|
2245
|
+
});
|
|
2246
|
+
}
|
|
2188
2247
|
var STATUS_LABEL = Object.freeze({ disabled: "\u672A\u542F\u7528", missing: "\u5C1A\u65E0\u5FEB\u7167", fresh: "\u5FEB\u7167\u6709\u6548", stale: "\u5FEB\u7167\u8FC7\u671F", error: "\u66F4\u65B0\u5931\u8D25" });
|
|
2189
|
-
|
|
2190
|
-
|
|
2248
|
+
var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set(["refresh-failed", "snapshot-rollback-rejected", "storage-failed", "refresh-interrupted"]);
|
|
2249
|
+
var positiveTime = (value) => Number.isFinite(value) && value > 0 ? value : null;
|
|
2250
|
+
function dynamicSourceView(source, enabled = true) {
|
|
2251
|
+
const status = enabled === false ? "disabled" : Object.hasOwn(STATUS_LABEL, source?.status ?? "") ? source.status : "missing";
|
|
2191
2252
|
let publicSource = "";
|
|
2192
|
-
if (typeof source?.source === "string") publicSource = publicEndpointDraft("pricingSnapshotEndpoint", source.source);
|
|
2253
|
+
if (enabled !== false && typeof source?.source === "string") publicSource = publicEndpointDraft("pricingSnapshotEndpoint", source.source);
|
|
2193
2254
|
return {
|
|
2194
2255
|
status,
|
|
2195
2256
|
label: STATUS_LABEL[status],
|
|
2196
2257
|
source: publicSource,
|
|
2197
|
-
version: typeof source?.version === "string" ? source.version.slice(0, 160) : null,
|
|
2198
|
-
verifiedAt: Number.isFinite(source?.verifiedAt) && source.verifiedAt > 0 ? source.verifiedAt : null,
|
|
2199
|
-
count: nonnegative2(source?.modelCount ?? source?.rowCount)
|
|
2258
|
+
version: enabled !== false && typeof source?.version === "string" ? source.version.slice(0, 160) : null,
|
|
2259
|
+
verifiedAt: enabled !== false && Number.isFinite(source?.verifiedAt) && source.verifiedAt > 0 ? source.verifiedAt : null,
|
|
2260
|
+
count: enabled === false ? 0 : nonnegative2(source?.modelCount ?? source?.rowCount),
|
|
2261
|
+
refreshing: enabled !== false && source?.refreshing === true,
|
|
2262
|
+
pending: enabled !== false && source?.pending === true,
|
|
2263
|
+
lastAttemptAt: enabled === false ? null : positiveTime(source?.lastAttemptAt),
|
|
2264
|
+
lastSuccessAt: enabled === false ? null : positiveTime(source?.lastSuccessAt),
|
|
2265
|
+
errorCode: enabled !== false && PUBLIC_ERROR_CODES.has(source?.error) ? source.error : "",
|
|
2266
|
+
endpoint: enabled === false ? "" : publicEndpointDraft("pricingSnapshotEndpoint", source?.configuredEndpoint ?? source?.endpoint ?? ""),
|
|
2267
|
+
lastGoodEndpoint: enabled === false ? "" : publicEndpointDraft("pricingSnapshotEndpoint", source?.lastGoodEndpoint ?? "")
|
|
2200
2268
|
};
|
|
2201
2269
|
}
|
|
2202
2270
|
|
|
@@ -2234,21 +2302,36 @@ function Meter({ label, meter }) {
|
|
|
2234
2302
|
function useSettingsWriter(settingsScope) {
|
|
2235
2303
|
const [snapshot, setSnapshot] = import_react2.default.useState(() => settingsScope.getSnapshot());
|
|
2236
2304
|
const [notice, setNotice] = import_react2.default.useState("");
|
|
2305
|
+
const [busy, setBusy] = import_react2.default.useState(false);
|
|
2306
|
+
const pending = import_react2.default.useRef(false);
|
|
2307
|
+
const mounted = import_react2.default.useRef(true);
|
|
2308
|
+
import_react2.default.useEffect(() => {
|
|
2309
|
+
mounted.current = true;
|
|
2310
|
+
return () => {
|
|
2311
|
+
mounted.current = false;
|
|
2312
|
+
};
|
|
2313
|
+
}, []);
|
|
2237
2314
|
import_react2.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
|
|
2238
|
-
const writable = snapshot.status === "ready" && snapshot.writable === true;
|
|
2315
|
+
const writable = snapshot.status === "ready" && snapshot.writable === true && !busy;
|
|
2239
2316
|
const write = async (key, value) => {
|
|
2240
|
-
|
|
2317
|
+
const current = settingsScope.getSnapshot();
|
|
2318
|
+
if (pending.current || current.status !== "ready" || current.writable !== true) return false;
|
|
2319
|
+
pending.current = true;
|
|
2320
|
+
setBusy(true);
|
|
2241
2321
|
setNotice("");
|
|
2242
2322
|
try {
|
|
2243
|
-
const accepted = await settingsScope.mutate([{ op: "set", path: [key], value }],
|
|
2244
|
-
if (!accepted) setNotice("\u8BBE\u7F6E\u672A\u4FDD\u5B58\uFF0C\u53EF\u80FD\u5DF2\u5728\u5176\u4ED6\u9875\u9762\u4FEE\u6539\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
2323
|
+
const accepted = await settingsScope.mutate([{ op: "set", path: [key], value }], current.revision);
|
|
2324
|
+
if (!accepted && mounted.current) setNotice("\u8BBE\u7F6E\u672A\u4FDD\u5B58\uFF0C\u53EF\u80FD\u5DF2\u5728\u5176\u4ED6\u9875\u9762\u4FEE\u6539\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
2245
2325
|
return accepted;
|
|
2246
2326
|
} catch (error) {
|
|
2247
|
-
setNotice(text(error?.message) || "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
2327
|
+
if (mounted.current) setNotice(text(error?.message) || "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
2248
2328
|
return false;
|
|
2329
|
+
} finally {
|
|
2330
|
+
pending.current = false;
|
|
2331
|
+
if (mounted.current) setBusy(false);
|
|
2249
2332
|
}
|
|
2250
2333
|
};
|
|
2251
|
-
return { value: snapshot.value ?? {}, writable, write, notice };
|
|
2334
|
+
return { value: snapshot.value ?? {}, writable, busy, write, notice };
|
|
2252
2335
|
}
|
|
2253
2336
|
var ADAPTIVE_FIELD_LABEL = {
|
|
2254
2337
|
feedbackHalfLifeDays: "\u53CD\u9988\u534A\u8870\u671F\uFF08\u5929\uFF09",
|
|
@@ -2260,12 +2343,18 @@ function AdaptiveNumberControl({ field: field2, value, writable, onSave }) {
|
|
|
2260
2343
|
const [draft, setDraft] = import_react2.default.useState("");
|
|
2261
2344
|
const [notice, setNotice] = import_react2.default.useState("");
|
|
2262
2345
|
const save = async () => {
|
|
2346
|
+
let next;
|
|
2263
2347
|
try {
|
|
2264
|
-
|
|
2265
|
-
setNotice("");
|
|
2266
|
-
if (await onSave(field2.key, next)) setDraft("");
|
|
2348
|
+
next = parseAdaptiveNumber(field2.key, draft);
|
|
2267
2349
|
} catch {
|
|
2268
2350
|
setNotice(`\u8BF7\u8F93\u5165 ${field2.min} \u5230 ${field2.max} \u7684\u6570\u5B57\u3002`);
|
|
2351
|
+
return;
|
|
2352
|
+
}
|
|
2353
|
+
setNotice("");
|
|
2354
|
+
try {
|
|
2355
|
+
if (await onSave(field2.key, next)) setDraft((current) => current === draft ? "" : current);
|
|
2356
|
+
} catch {
|
|
2357
|
+
setNotice("\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
2269
2358
|
}
|
|
2270
2359
|
};
|
|
2271
2360
|
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: `mr-${field2.key}` }, ADAPTIVE_FIELD_LABEL[field2.key]), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement(
|
|
@@ -2291,12 +2380,18 @@ function PublicEndpointControl({ name, label, value, writable, onSave }) {
|
|
|
2291
2380
|
const [notice, setNotice] = import_react2.default.useState("");
|
|
2292
2381
|
const current = publicEndpointDraft(name, value);
|
|
2293
2382
|
const save = async () => {
|
|
2383
|
+
let next;
|
|
2294
2384
|
try {
|
|
2295
|
-
|
|
2296
|
-
setNotice("");
|
|
2297
|
-
if (await onSave(name, next)) setDraft(null);
|
|
2385
|
+
next = parsePublicEndpoint(name, draft ?? current);
|
|
2298
2386
|
} catch {
|
|
2299
2387
|
setNotice("\u8BF7\u8F93\u5165\u4E0D\u542B\u8BA4\u8BC1\u3001\u67E5\u8BE2\u53C2\u6570\u6216\u7247\u6BB5\u7684\u516C\u5F00 HTTPS \u5730\u5740\uFF1B\u4EF7\u683C\u5FEB\u7167\u53EF\u7559\u7A7A\u3002");
|
|
2388
|
+
return;
|
|
2389
|
+
}
|
|
2390
|
+
setNotice("");
|
|
2391
|
+
try {
|
|
2392
|
+
if (await onSave(name, next)) setDraft((currentDraft) => currentDraft === draft ? null : currentDraft);
|
|
2393
|
+
} catch {
|
|
2394
|
+
setNotice("\u5730\u5740\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
2300
2395
|
}
|
|
2301
2396
|
};
|
|
2302
2397
|
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: `mr-${name}` }, label), /* @__PURE__ */ import_react2.default.createElement(
|
|
@@ -2315,25 +2410,42 @@ function PublicEndpointControl({ name, label, value, writable, onSave }) {
|
|
|
2315
2410
|
void save();
|
|
2316
2411
|
} }, "\u4FDD\u5B58\u5730\u5740"), notice && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-error", role: "alert" }, notice));
|
|
2317
2412
|
}
|
|
2318
|
-
function PublicSourceStatus({ label, source }) {
|
|
2319
|
-
const view = dynamicSourceView(source);
|
|
2320
|
-
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, label, "\uFF1A", view.label), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, view.version ? `\u7248\u672C ${view.version} \xB7 ` : "", view.count, " \u6761\u8BB0\u5F55", view.verifiedAt ? ` \xB7 \u6700\u8FD1\u6821\u9A8C ${new Date(view.verifiedAt).toLocaleString()}` : ""), view.source && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u516C\u5F00\u6765\u6E90\uFF1A", view.source), view.status === "error" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u66F4\u65B0\u5931\u8D25\uFF1B\u662F\u5426\u53EF\u7528\u7531 Host \u6821\u9A8C\uFF0C\u4E0D\u5C06\u7F3A\u5931\u6570\u636E\u5F53\u4F5C\u96F6\u6210\u672C\u6216\u6EE1\u5206\u3002"));
|
|
2413
|
+
function PublicSourceStatus({ label, source, enabled }) {
|
|
2414
|
+
const view = dynamicSourceView(source, enabled);
|
|
2415
|
+
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, label, "\uFF1A", view.label), view.status !== "disabled" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, view.version ? `\u7248\u672C ${view.version} \xB7 ` : "", view.count, " \u6761\u8BB0\u5F55", view.verifiedAt ? ` \xB7 \u6700\u8FD1\u6821\u9A8C ${new Date(view.verifiedAt).toLocaleString()}` : ""), view.source && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u516C\u5F00\u6765\u6E90\uFF1A", view.source), (view.refreshing || view.pending) && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption", role: "status" }, "\u516C\u5F00\u6E90\u6B63\u5728\u5237\u65B0\uFF1B\u4E0A\u9762\u7684\u7248\u672C\u4ECD\u662F\u6700\u8FD1\u4E00\u6B21\u5DF2\u4FDD\u5B58\u5FEB\u7167\uFF0C\u4E0D\u662F\u672C\u6B21\u8BF7\u6C42\u5DF2\u6210\u529F\u3002"), view.lastAttemptAt && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u6700\u8FD1\u5C1D\u8BD5 ", new Date(view.lastAttemptAt).toLocaleString(), view.lastSuccessAt ? ` \xB7 \u6700\u8FD1\u6210\u529F ${new Date(view.lastSuccessAt).toLocaleString()}` : ""), view.status === "error" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u66F4\u65B0\u5931\u8D25\uFF1B\u662F\u5426\u53EF\u7528\u7531 Host \u6821\u9A8C\uFF0C\u4E0D\u5C06\u7F3A\u5931\u6570\u636E\u5F53\u4F5C\u96F6\u6210\u672C\u6216\u6EE1\u5206\u3002"), view.status === "stale" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u4E0A\u6B21\u6709\u6548\u5FEB\u7167\u5DF2\u8FC7\u671F\u6216\u672C\u6B21\u66F4\u65B0\u5931\u8D25\uFF1BHost \u53EF\u80FD\u6CBF\u7528\u65E7\u6570\u636E\uFF0C\u4E0D\u80FD\u5F53\u4F5C\u6700\u65B0\u4EF7\u683C\u6216\u80FD\u529B\u3002"), view.errorCode === "storage-failed" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u672C\u6B21\u72B6\u6001\u4FDD\u5B58\u5931\u8D25\uFF1B\u53EA\u80FD\u4F7F\u7528\u6B64\u524D\u5DF2\u4FDD\u5B58\u7684\u5F53\u524D\u6E90\u5FEB\u7167\uFF0C\u8BF7\u91CD\u8BD5\u5237\u65B0\u3002"), view.errorCode === "refresh-interrupted" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u4E0A\u6B21\u516C\u5F00\u6570\u636E\u5237\u65B0\u4E2D\u65AD\uFF1B\u8BF7\u5237\u65B0\u6458\u8981\u67E5\u770B Host \u7684\u540E\u7EED\u91CD\u8BD5\u72B6\u6001\u3002"), view.lastGoodEndpoint && view.endpoint && view.lastGoodEndpoint !== view.endpoint && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u6B64\u524D\u6765\u6E90\u7684\u6709\u6548\u5FEB\u7167\u4ECD\u4FDD\u7559\u5728\u672C\u5730\uFF0C\u4F46\u4E0D\u5E94\u7528\u5230\u5F53\u524D\u516C\u5F00\u6E90\u3002"), view.status === "disabled" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u5F53\u524D\u8BBE\u7F6E\u4E0D\u4F7F\u7528\u6B64\u516C\u5F00\u6E90\uFF1B\u4FDD\u7559\u7684\u5386\u53F2\u5FEB\u7167\u4E0D\u4EE3\u8868\u6B63\u5728\u4F7F\u7528\u3002"));
|
|
2416
|
+
}
|
|
2417
|
+
function LearningEvidence({ learning }) {
|
|
2418
|
+
const evidence = learningEvidenceRows(learning);
|
|
2419
|
+
const exclusions = learningExclusionRows(learning);
|
|
2420
|
+
return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, evidence.total > 0 && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u5206\u6A21\u578B / \u4EFB\u52A1\u7C7B\u578B\u7684\u5DF2\u89C2\u5BDF\u53CD\u9988\uFF08", evidence.total, " \u7EC4\uFF09"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u53EA\u63CF\u8FF0\u5DF2\u9009\u62E9\u5E76\u83B7\u5F97\u8BC4\u4EF7\u7684\u7ED3\u679C\uFF0C\u4E0D\u4EE3\u8868\u6240\u6709\u6A21\u578B\u7684\u771F\u5B9E\u8D28\u91CF\u6216\u6536\u76CA\uFF1B\u6709\u6548\u6837\u672C\u91CF\u53CD\u6620\u6743\u91CD\u96C6\u4E2D\u7A0B\u5EA6\uFF0C\u4E0D\u662F\u8D28\u91CF\u7F6E\u4FE1\u5EA6\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-table-wrap" }, /* @__PURE__ */ import_react2.default.createElement("table", { className: "mr-table" }, /* @__PURE__ */ import_react2.default.createElement("thead", null, /* @__PURE__ */ import_react2.default.createElement("tr", null, /* @__PURE__ */ import_react2.default.createElement("th", null, "\u8DEF\u7EBF / \u4EFB\u52A1\u7C7B\u578B"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u6709\u7528 / \u4E0D\u597D"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u504F\u597D\u6548\u7528\u8C03\u6574"))), /* @__PURE__ */ import_react2.default.createElement("tbody", null, evidence.rows.map((row) => /* @__PURE__ */ import_react2.default.createElement("tr", { key: row.id }, /* @__PURE__ */ import_react2.default.createElement("td", null, row.route, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-caption" }, row.domain)), /* @__PURE__ */ import_react2.default.createElement("td", null, row.positiveCount, " / ", row.negativeCount, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-caption" }, "\u5DF2\u89C2\u5BDF ", row.feedbackCount, " \xB7 \u6709\u6548\u6743\u91CD ", row.effectiveWeight.toFixed(2), " \xB7 \u6709\u6548\u6837\u672C\u91CF ", row.effectiveSampleSize.toFixed(2))), /* @__PURE__ */ import_react2.default.createElement("td", null, row.adjustment > 0 ? "+" : "", row.adjustment.toFixed(4), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-caption" }, "\u6536\u7F29\u7CFB\u6570 ", row.shrinkage.toFixed(2)))))))), evidence.total > evidence.rows.length && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u4EC5\u5C55\u793A\u524D ", evidence.rows.length, " \u7EC4\uFF1B\u4E0D\u662F\u6240\u6709\u6A21\u578B\u7684\u8986\u76D6\u7387\u3002")), exclusions.length > 0 && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u672A\u53C2\u4E0E\u5B66\u4E60\u7684\u539F\u56E0"), /* @__PURE__ */ import_react2.default.createElement("ul", { className: "mr-checklist" }, exclusions.map((row) => /* @__PURE__ */ import_react2.default.createElement("li", { key: row.reason }, row.label, "\uFF1A", row.count)))));
|
|
2321
2421
|
}
|
|
2322
|
-
function AdaptiveControls({ ledger, settings, onChanged }) {
|
|
2422
|
+
function AdaptiveControls({ ledger, settings, onChanged, refreshing }) {
|
|
2323
2423
|
const value = settings.value;
|
|
2324
2424
|
const learning = learningView(ledger?.learning, value);
|
|
2325
2425
|
const [resetBusy, setResetBusy] = import_react2.default.useState(false);
|
|
2426
|
+
const [refreshNotice, setRefreshNotice] = import_react2.default.useState("");
|
|
2427
|
+
const resetPending = import_react2.default.useRef(false);
|
|
2326
2428
|
const save = async (key, next) => {
|
|
2327
2429
|
const accepted = await settings.write(key, next);
|
|
2328
|
-
if (accepted)
|
|
2430
|
+
if (accepted) {
|
|
2431
|
+
setRefreshNotice("");
|
|
2432
|
+
try {
|
|
2433
|
+
await onChanged?.();
|
|
2434
|
+
} catch {
|
|
2435
|
+
setRefreshNotice("\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u6458\u8981\u5237\u65B0\u5931\u8D25\uFF1B\u8BF7\u5237\u65B0\u6267\u884C\u8BB0\u5F55\u540E\u91CD\u65B0\u89C4\u5212\u3002");
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2329
2438
|
return accepted;
|
|
2330
2439
|
};
|
|
2331
2440
|
const reset = async () => {
|
|
2332
|
-
if (!
|
|
2333
|
-
|
|
2441
|
+
if (resetPending.current || !settings.writable) return;
|
|
2442
|
+
resetPending.current = true;
|
|
2334
2443
|
try {
|
|
2444
|
+
if (!window.confirm("\u4ECE\u73B0\u5728\u91CD\u65B0\u5B66\u4E60\u4F60\u7684\u504F\u597D\uFF1F\u6B64\u524D\u53CD\u9988\u5C06\u4E0D\u518D\u53C2\u4E0E\u8C03\u6574\uFF0C\u4F46\u6267\u884C\u8BB0\u5F55\u3001\u8D39\u7528\u5386\u53F2\u548C\u539F\u8BC4\u4EF7\u4E0D\u4F1A\u5220\u9664\u3002")) return;
|
|
2445
|
+
setResetBusy(true);
|
|
2335
2446
|
await save("feedbackResetAt", Date.now());
|
|
2336
2447
|
} finally {
|
|
2448
|
+
resetPending.current = false;
|
|
2337
2449
|
setResetBusy(false);
|
|
2338
2450
|
}
|
|
2339
2451
|
};
|
|
@@ -2356,7 +2468,7 @@ function AdaptiveControls({ ledger, settings, onChanged }) {
|
|
|
2356
2468
|
writable: settings.writable,
|
|
2357
2469
|
onSave: save
|
|
2358
2470
|
}
|
|
2359
|
-
))), learning.available ? /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, learning.enabled ? "\u5B66\u4E60\u5DF2\u542F\u7528" : "\u5B66\u4E60\u5DF2\u505C\u7528", " \xB7 \u6709\u6548\u53CD\u9988 ", learning.feedbackCount, " \xB7 \u6709\u6548\u6743\u91CD ", learning.effectiveWeight.toFixed(2), " \xB7 \u5FFD\u7565 ", learning.ignoredCount, learning.policyVersion ? ` \xB7 \u7B56\u7565 ${learning.policyVersion}` : "") : /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, "\u5B66\u4E60\u6458\u8981\u5C1A\u672A\u8BFB\u53D6\uFF1B\u4FDD\u5B58\u540E\u5237\u65B0\u6267\u884C\u8BB0\u5F55\u67E5\u770B\u3002"), learning.resetAt > 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u5B66\u4E60\u8D77\u70B9\uFF1A", new Date(learning.resetAt).toLocaleString(), "\uFF0C\u6B64\u524D\u53CD\u9988\u4E0D\u518D\u53C2\u4E0E\u8C03\u6574\u3002"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !settings.writable || resetBusy, onClick: () => {
|
|
2471
|
+
))), settings.busy && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, "\u6B63\u5728\u4FDD\u5B58\u8BBE\u7F6E\uFF0C\u8BF7\u7A0D\u5019\u2026"), refreshing && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, "\u6B63\u5728\u8BFB\u53D6\u53CD\u9988\u4E0E\u516C\u5F00\u6570\u636E\u6458\u8981\u2026"), refreshNotice && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, refreshNotice), learning.available ? /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, learning.enabled ? "\u5B66\u4E60\u5DF2\u542F\u7528" : "\u5B66\u4E60\u5DF2\u505C\u7528", " \xB7 \u6700\u8FD1\u4E00\u6B21\u8D26\u672C\u6458\u8981\uFF1A\u6709\u6548\u53CD\u9988 ", learning.feedbackCount, " \xB7 \u6709\u6548\u6743\u91CD ", learning.effectiveWeight.toFixed(2), " \xB7 \u5FFD\u7565 ", learning.ignoredCount, learning.policyVersion ? ` \xB7 \u7B56\u7565 ${learning.policyVersion}` : "") : /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, "\u5B66\u4E60\u6458\u8981\u5C1A\u672A\u8BFB\u53D6\uFF1B\u4FDD\u5B58\u540E\u5237\u65B0\u6267\u884C\u8BB0\u5F55\u67E5\u770B\u3002"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u6458\u8981\u662F\u6700\u8FD1\u4E00\u6B21\u8BFB\u53D6\u7684\u5DF2\u89C2\u5BDF\u8BC1\u636E\u3002\u4FEE\u6539\u8BC4\u4EF7 / \u5B66\u4E60\u8BBE\u7F6E\u540E\u9700\u5237\u65B0\u6458\u8981\u5E76\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\uFF1B\u5237\u65B0\u6309\u68C0\u67E5\u95F4\u9694\u590D\u7528\u516C\u5F00\u5FEB\u7167\uFF0C\u4E0D\u4FDD\u8BC1\u6BCF\u6B21\u4E0B\u8F7D\u65B0\u6570\u636E\u3002"), /* @__PURE__ */ import_react2.default.createElement(LearningEvidence, { learning: ledger?.learning }), learning.resetAt > 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u5B66\u4E60\u8D77\u70B9\uFF1A", new Date(learning.resetAt).toLocaleString(), "\uFF0C\u6B64\u524D\u53CD\u9988\u4E0D\u518D\u53C2\u4E0E\u8C03\u6574\u3002"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !settings.writable || resetBusy, onClick: () => {
|
|
2360
2472
|
void reset();
|
|
2361
2473
|
} }, resetBusy ? "\u6B63\u5728\u8BBE\u7F6E\u2026" : "\u4ECE\u73B0\u5728\u91CD\u65B0\u5B66\u4E60"), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "mr-section-title" }, "\u52A8\u6001\u516C\u5171\u6570\u636E"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u9ED8\u8BA4\u5173\u95ED\u3002\u660E\u786E\u542F\u7528\u540E\uFF0C\u5728\u6253\u5F00 / \u5237\u65B0\u8D26\u672C\u53CA\u6267\u884C\u524D\u6309\u68C0\u67E5\u95F4\u9694\u8BFB\u53D6\u516C\u5F00 LiveBench \u548C\u53EF\u9009\u4EF7\u683C\u5FEB\u7167\uFF1B\u4EF7\u683C\u4E0E\u80FD\u529B\u5148\u9A8C\u4FDD\u7559\u7248\u672C\uFF0C\u4E0D\u4EE3\u8868\u771F\u5B9E\u4EFB\u52A1\u6536\u76CA\u3002\u53EA\u6709\u6253\u5F00\u516C\u5171\u6E90\u5F00\u5173\u624D\u4F1A\u8BFB\u53D6\u8FDC\u7A0B\u6570\u636E\uFF0C\u4E0D\u53D1\u9001\u4EFB\u52A1\u6216\u53CD\u9988\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-check" }, /* @__PURE__ */ import_react2.default.createElement(
|
|
2362
2474
|
"input",
|
|
@@ -2376,9 +2488,9 @@ function AdaptiveControls({ ledger, settings, onChanged }) {
|
|
|
2376
2488
|
writable: settings.writable,
|
|
2377
2489
|
onSave: save
|
|
2378
2490
|
}
|
|
2379
|
-
), /* @__PURE__ */ import_react2.default.createElement(PublicEndpointControl, { name: "liveBenchEndpoint", label: "LiveBench \u516C\u5F00\u6E90", value: adaptiveSettingValue(value, "liveBenchEndpoint"), writable: settings.writable, onSave: save }), /* @__PURE__ */ import_react2.default.createElement(PublicEndpointControl, { name: "pricingSnapshotEndpoint", label: "\u4EF7\u683C\u5FEB\u7167\u516C\u5F00\u6E90\uFF08\u53EF\u9009\uFF09", value: adaptiveSettingValue(value, "pricingSnapshotEndpoint"), writable: settings.writable, onSave: save }), /* @__PURE__ */ import_react2.default.createElement(PublicSourceStatus, { label: "LiveBench", source: ledger?.dynamicData?.liveBench }), /* @__PURE__ */ import_react2.default.createElement(PublicSourceStatus, { label: "\u4EF7\u683C", source: ledger?.dynamicData?.pricing })));
|
|
2491
|
+
), /* @__PURE__ */ import_react2.default.createElement(PublicEndpointControl, { name: "liveBenchEndpoint", label: "LiveBench \u516C\u5F00\u6E90", value: adaptiveSettingValue(value, "liveBenchEndpoint"), writable: settings.writable, onSave: save }), /* @__PURE__ */ import_react2.default.createElement(PublicEndpointControl, { name: "pricingSnapshotEndpoint", label: "\u4EF7\u683C\u5FEB\u7167\u516C\u5F00\u6E90\uFF08\u53EF\u9009\uFF09", value: adaptiveSettingValue(value, "pricingSnapshotEndpoint"), writable: settings.writable, onSave: save }), /* @__PURE__ */ import_react2.default.createElement(PublicSourceStatus, { label: "LiveBench", source: ledger?.dynamicData?.liveBench, enabled: adaptiveSettingValue(value, "dynamicDataEnabled") === true }), /* @__PURE__ */ import_react2.default.createElement(PublicSourceStatus, { label: "\u4EF7\u683C", source: ledger?.dynamicData?.pricing, enabled: adaptiveSettingValue(value, "dynamicDataEnabled") === true && Boolean(adaptiveSettingValue(value, "pricingSnapshotEndpoint")) })));
|
|
2380
2492
|
}
|
|
2381
|
-
function CostControlCard({ ledger, settingsScope, onChanged, error }) {
|
|
2493
|
+
function CostControlCard({ ledger, settingsScope, onChanged, error, refreshing = false }) {
|
|
2382
2494
|
const settings = useSettingsWriter(settingsScope);
|
|
2383
2495
|
const [dailyDraft, setDailyDraft] = import_react2.default.useState("");
|
|
2384
2496
|
const [monthlyDraft, setMonthlyDraft] = import_react2.default.useState("");
|
|
@@ -2416,7 +2528,7 @@ function CostControlCard({ ledger, settingsScope, onChanged, error }) {
|
|
|
2416
2528
|
void set("allowManualReassign", event.target.checked);
|
|
2417
2529
|
} }), "\u5141\u8BB8\u624B\u52A8\u6539\u6D3E\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-check" }, /* @__PURE__ */ import_react2.default.createElement("input", { type: "checkbox", checked: value.confirmUnsandboxedCli !== false, disabled: !settings.writable, onChange: (event) => {
|
|
2418
2530
|
void set("confirmUnsandboxedCli", event.target.checked);
|
|
2419
|
-
} }), "\u76F4\u63A5\u542F\u52A8\u65E0\u6C99\u7BB1 CLI \u524D\u5148\u786E\u8BA4")), /* @__PURE__ */ import_react2.default.createElement(AdaptiveControls, { ledger, settings, onChanged }), settings.notice && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, settings.notice)));
|
|
2531
|
+
} }), "\u76F4\u63A5\u542F\u52A8\u65E0\u6C99\u7BB1 CLI \u524D\u5148\u786E\u8BA4")), /* @__PURE__ */ import_react2.default.createElement(AdaptiveControls, { ledger, settings, onChanged, refreshing }), settings.notice && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, settings.notice)));
|
|
2420
2532
|
}
|
|
2421
2533
|
function DagView({ packages, renderNode, label = "\u5B50\u4EFB\u52A1\u4F9D\u8D56\u56FE" }) {
|
|
2422
2534
|
const layers = dagLayers(packages);
|
|
@@ -2432,13 +2544,13 @@ function ChannelText({ item }) {
|
|
|
2432
2544
|
return /* @__PURE__ */ import_react2.default.createElement("span", { className: item.channel === "official-cli" ? "mr-pill mr-pill-channel-ok" : "mr-pill" }, item.channel === "official-cli" ? `\u5B98\u65B9 CLI \xB7 ${item.toolId ?? ""}` : "\u6A21\u578B\u76EE\u5F55 API");
|
|
2433
2545
|
}
|
|
2434
2546
|
function RunNode({ run, item, routes, allowReassign, busy, onRate, onRerun }) {
|
|
2435
|
-
if (item.status === "paused") return /* @__PURE__ */ import_react2.default.createElement(PausedNode, { run, item, busy, onRerun });
|
|
2436
2547
|
const [target, setTarget] = import_react2.default.useState("");
|
|
2548
|
+
if (item.status === "paused") return /* @__PURE__ */ import_react2.default.createElement(PausedNode, { run, item, busy, onRerun });
|
|
2437
2549
|
const cost = packageCost(item);
|
|
2438
2550
|
const rerun = rerunSupport(run);
|
|
2439
2551
|
const canRerun = rerun.supported && !item.ok && !busy;
|
|
2440
2552
|
const others = routes.filter((route) => `${route.provider}/${route.model}` !== `${item.provider}/${item.model}`);
|
|
2441
|
-
return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-route" }, item.provider, "/", item.model, item.reassigned ? "\uFF08\u5DF2\u6539\u6D3E\uFF09" : ""), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelText, { item }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u9884\u4F30 ", money(item.estimatedCost), " \xB7 \u5B9E\u9645 ", cost.budget, item.difficulty ? ` \xB7 \u96BE\u5EA6 ${BAND[item.difficulty] ?? item.difficulty}` : "")), cost.reference && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, cost.reference), item.actualModel && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "CLI \u56DE\u62A5\u6A21\u578B\uFF1A", item.actualModel), (item.billing || item.billingMode) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u8BA1\u8D39\uFF1A", BILLING_CHANNEL_LABEL[item.billing] ?? "\u2014", item.subscriptionRoute ? ` \xB7 \u5957\u9910\u8DEF\u7EBF ${item.subscriptionRoute.provider}/${item.subscriptionRoute.model}` : "", item.billingMode && item.billingMode !== "subscription-first" ? ` \xB7 ${BILLING_MODE_LABEL[item.billingMode] ?? item.billingMode}` : ""), billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-warn-text" }, "\u8BA1\u8D39\u5207\u6362\uFF1A", billingSwitchText(item)), item.fallback?.reason && item.fallback.reason !== billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u56DE\u9000\u539F\u56E0\uFF1A", item.fallback.reason), item.fallback?.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, "CLI \u539F\u59CB\u9519\u8BEF\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, item.fallback.error)), !item.ok && item.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, item.error), item.review && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5F3A\u6A21\u578B\u62BD\u67E5\uFF08", item.review.provider, "/", item.review.model, "\uFF09\uFF1A", item.review.score ? `${item.review.score}/5` : "\u672A\u8BC4\u5206", " ", item.review.summary), item.answer && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u7ED3\u679C"), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.answer, item.answerTruncated ? "\n\u2026\uFF08\u5DF2\u622A\u65AD\uFF09" : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-node-actions" }, item.ok && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", "aria-pressed": item.rating === 1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "up")) }, "\u{1F44D} \u6709\u7528"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", "aria-pressed": item.rating === -1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "down")) }, "\u{1F44E} \u4E0D\u597D")), canRerun && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-mini", type: "button", title: rerun.confirm || void 0, onClick: () => onRerun(run.id, item.id, null) }, run.kind === "tool" ? "\u91CD\u65B0\u6267\u884C\u6B64\u8C03\u7528" : rerun.writes ? "\u5728\u65B0\u5DE5\u4F5C\u533A\u7EED\u8DD1" : "\u91CD\u8DD1\u6B64\u6B65"), !rerun.supported && !item.ok && item.ran && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, rerun.reason), rerun.supported && rerun.reassign && !(rerun.writes && item.ok) && allowReassign && others.length > 0 && !busy && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement("select", { className: "mr-input mr-mini-select", "aria-label": "\u6539\u6D3E\u5230", value: target, onChange: (event) => setTarget(event.target.value) }, /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "\u6539\u6D3E\u5230\u2026"), others.map((route) => /* @__PURE__ */ import_react2.default.createElement("option", { key: `${route.provider}/${route.model}`, value: `${route.provider}\0${route.model}` }, route.provider, "/", route.model))), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", disabled: !target, onClick: () => {
|
|
2553
|
+
return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-route" }, item.provider, "/", item.model, item.reassigned ? "\uFF08\u5DF2\u6539\u6D3E\uFF09" : ""), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelText, { item }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u9884\u4F30 ", money(item.estimatedCost), " \xB7 \u5B9E\u9645 ", cost.budget, item.difficulty ? ` \xB7 \u96BE\u5EA6 ${BAND[item.difficulty] ?? item.difficulty}` : "")), cost.reference && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, cost.reference), item.actualModel && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "CLI \u56DE\u62A5\u6A21\u578B\uFF1A", item.actualModel), (item.billing || item.billingMode) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u8BA1\u8D39\uFF1A", BILLING_CHANNEL_LABEL[item.billing] ?? "\u2014", item.subscriptionRoute ? ` \xB7 \u5957\u9910\u8DEF\u7EBF ${item.subscriptionRoute.provider}/${item.subscriptionRoute.model}` : "", item.billingMode && item.billingMode !== "subscription-first" ? ` \xB7 ${BILLING_MODE_LABEL[item.billingMode] ?? item.billingMode}` : ""), billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-warn-text" }, "\u8BA1\u8D39\u5207\u6362\uFF1A", billingSwitchText(item)), item.fallback?.reason && item.fallback.reason !== billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u56DE\u9000\u539F\u56E0\uFF1A", item.fallback.reason), item.fallback?.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, "CLI \u539F\u59CB\u9519\u8BEF\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, item.fallback.error)), !item.ok && item.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, item.error), item.review && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5F3A\u6A21\u578B\u62BD\u67E5\uFF08", item.review.provider, "/", item.review.model, "\uFF09\uFF1A", item.review.score ? `${item.review.score}/5` : "\u672A\u8BC4\u5206", " ", item.review.summary), item.answer && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u7ED3\u679C"), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.answer, item.answerTruncated ? "\n\u2026\uFF08\u5DF2\u622A\u65AD\uFF09" : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-node-actions" }, item.ok && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", title: item.rating === 1 ? "\u518D\u6B21\u70B9\u51FB\u64A4\u56DE\u6709\u7528\u8BC4\u4EF7" : "\u8BC4\u4EF7\u6B64\u7ED3\u679C\u6709\u7528", "aria-pressed": item.rating === 1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "up")) }, "\u{1F44D} \u6709\u7528"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", title: item.rating === -1 ? "\u518D\u6B21\u70B9\u51FB\u64A4\u56DE\u4E0D\u597D\u8BC4\u4EF7" : "\u8BC4\u4EF7\u6B64\u7ED3\u679C\u4E0D\u597D", "aria-pressed": item.rating === -1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "down")) }, "\u{1F44E} \u4E0D\u597D")), canRerun && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-mini", type: "button", title: rerun.confirm || void 0, onClick: () => onRerun(run.id, item.id, null) }, run.kind === "tool" ? "\u91CD\u65B0\u6267\u884C\u6B64\u8C03\u7528" : rerun.writes ? "\u5728\u65B0\u5DE5\u4F5C\u533A\u7EED\u8DD1" : "\u91CD\u8DD1\u6B64\u6B65"), !rerun.supported && !item.ok && item.ran && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, rerun.reason), rerun.supported && rerun.reassign && !(rerun.writes && item.ok) && allowReassign && others.length > 0 && !busy && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement("select", { className: "mr-input mr-mini-select", "aria-label": "\u6539\u6D3E\u5230", value: target, onChange: (event) => setTarget(event.target.value) }, /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "\u6539\u6D3E\u5230\u2026"), others.map((route) => /* @__PURE__ */ import_react2.default.createElement("option", { key: `${route.provider}/${route.model}`, value: `${route.provider}\0${route.model}` }, route.provider, "/", route.model))), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", disabled: !target, onClick: () => {
|
|
2442
2554
|
const [provider, model] = target.split("\0");
|
|
2443
2555
|
onRerun(run.id, item.id, { provider, model });
|
|
2444
2556
|
} }, "\u6539\u6D3E\u5E76\u91CD\u8DD1"))));
|
|
@@ -12003,7 +12115,7 @@ var xterm_default = `/**
|
|
|
12003
12115
|
`;
|
|
12004
12116
|
|
|
12005
12117
|
// .dsh-plugin/client/host-version.mjs
|
|
12006
|
-
var ROUTER_CLIENT_VERSION = true ? "0.16.
|
|
12118
|
+
var ROUTER_CLIENT_VERSION = true ? "0.16.1" : "";
|
|
12007
12119
|
var STALE_HOST_MESSAGE = "\u63D2\u4EF6\u540E\u53F0\u7248\u672C\u8F83\u65E7\uFF0C\u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u542F Harness\uFF08\u5305\u62EC\u6258\u76D8\u56FE\u6807\uFF09\u540E\u518D\u4F7F\u7528\u3002";
|
|
12008
12120
|
function isMissingRemoteMethod(message) {
|
|
12009
12121
|
const value = String(message ?? "");
|
|
@@ -12962,13 +13074,18 @@ var WORKSPACE_VIEWS = [
|
|
|
12962
13074
|
];
|
|
12963
13075
|
function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries }) {
|
|
12964
13076
|
const [health, setHealth] = import_react5.default.useState({ report: null, error: "", refreshing: false });
|
|
12965
|
-
const [ledger, setLedger] = import_react5.default.useState({ value: null, error: "" });
|
|
13077
|
+
const [ledger, setLedger] = import_react5.default.useState({ value: null, error: "", refreshing: false });
|
|
12966
13078
|
const [boundaries, setBoundaries] = import_react5.default.useState({ value: null, error: "" });
|
|
12967
13079
|
const [busy, setBusy] = import_react5.default.useState(false);
|
|
12968
13080
|
const mounted = import_react5.default.useRef(true);
|
|
12969
13081
|
const ledgerRequest = import_react5.default.useRef(0);
|
|
12970
|
-
import_react5.default.
|
|
12971
|
-
|
|
13082
|
+
const mutationBusy = import_react5.default.useRef(false);
|
|
13083
|
+
import_react5.default.useEffect(() => {
|
|
13084
|
+
mounted.current = true;
|
|
13085
|
+
return () => {
|
|
13086
|
+
mounted.current = false;
|
|
13087
|
+
ledgerRequest.current += 1;
|
|
13088
|
+
};
|
|
12972
13089
|
}, []);
|
|
12973
13090
|
const call = async (operation, fallback) => {
|
|
12974
13091
|
if (typeof operation !== "function") throw new Error("\u5DE5\u4F5C\u53F0\u670D\u52A1\u5C1A\u672A\u52A0\u8F7D\uFF0C\u8BF7\u66F4\u65B0\u63D2\u4EF6\u540E\u91CD\u8BD5\u3002");
|
|
@@ -12985,13 +13102,18 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
12985
13102
|
};
|
|
12986
13103
|
const refreshLedger = async () => {
|
|
12987
13104
|
const request = ++ledgerRequest.current;
|
|
13105
|
+
if (mounted.current) setLedger((previous) => ({ ...previous, error: "", refreshing: true }));
|
|
12988
13106
|
try {
|
|
12989
13107
|
const value = await call(loadLedger, "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002");
|
|
12990
|
-
if (mounted.current && request === ledgerRequest.current) setLedger({ value, error: "" });
|
|
12991
|
-
if (request !== ledgerRequest.current) return null;
|
|
13108
|
+
if (mounted.current && request === ledgerRequest.current) setLedger({ value, error: "", refreshing: false });
|
|
13109
|
+
if (!mounted.current || request !== ledgerRequest.current) return null;
|
|
12992
13110
|
return value;
|
|
12993
13111
|
} catch (error) {
|
|
12994
|
-
if (mounted.current && request === ledgerRequest.current) setLedger((previous) => ({
|
|
13112
|
+
if (mounted.current && request === ledgerRequest.current) setLedger((previous) => ({
|
|
13113
|
+
...previous,
|
|
13114
|
+
refreshing: false,
|
|
13115
|
+
error: `${text5(error?.message) || "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002"}${previous.value ? " \u4E0B\u65B9\u4FDD\u7559\u6700\u8FD1\u4E00\u6B21\u6458\u8981\uFF0C\u5C1A\u672A\u91CD\u65B0\u6838\u9A8C\u3002" : ""}`
|
|
13116
|
+
}));
|
|
12995
13117
|
return null;
|
|
12996
13118
|
}
|
|
12997
13119
|
};
|
|
@@ -13012,6 +13134,8 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
13012
13134
|
}
|
|
13013
13135
|
};
|
|
13014
13136
|
const rate = async (runId, packageId, rating, expectedFinishedAt) => {
|
|
13137
|
+
if (mutationBusy.current) return;
|
|
13138
|
+
mutationBusy.current = true;
|
|
13015
13139
|
setBusy(true);
|
|
13016
13140
|
try {
|
|
13017
13141
|
await call(() => rateResult({ runId, packageId, rating, ...expectedFinishedAt === void 0 ? {} : { expectedFinishedAt } }), "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
@@ -13019,10 +13143,12 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
13019
13143
|
} catch (error) {
|
|
13020
13144
|
if (mounted.current) setLedger((previous) => ({ ...previous, error: text5(error?.message) || "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002" }));
|
|
13021
13145
|
} finally {
|
|
13146
|
+
mutationBusy.current = false;
|
|
13022
13147
|
if (mounted.current) setBusy(false);
|
|
13023
13148
|
}
|
|
13024
13149
|
};
|
|
13025
13150
|
const rerun = async (runId, packageId, override, choice = null) => {
|
|
13151
|
+
if (mutationBusy.current) return;
|
|
13026
13152
|
const run = ledger.value?.runs?.find((item2) => item2.id === runId);
|
|
13027
13153
|
const item = run?.packages?.find((entry) => entry.id === packageId);
|
|
13028
13154
|
const reasons = rerunConfirmations({ run, item, override, choice, ledger: ledger.value, health: health.report, toolFor: toolForProvider, headlessIds: HEADLESS_TOOLS });
|
|
@@ -13031,6 +13157,8 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
13031
13157
|
${reasons.map((entry, index) => `${index + 1}. ${entry.text}`).join("\n")}
|
|
13032
13158
|
\u5168\u90E8\u786E\u8BA4\u5E76\u7EE7\u7EED\u5417\uFF1F`)) return;
|
|
13033
13159
|
const codes = new Set(reasons.map((entry) => entry.code));
|
|
13160
|
+
if (mutationBusy.current) return;
|
|
13161
|
+
mutationBusy.current = true;
|
|
13034
13162
|
setBusy(true);
|
|
13035
13163
|
try {
|
|
13036
13164
|
const request = {
|
|
@@ -13051,6 +13179,7 @@ ${reasons.map((entry, index) => `${index + 1}. ${entry.text}`).join("\n")}
|
|
|
13051
13179
|
} catch (error) {
|
|
13052
13180
|
if (mounted.current) setLedger((previous) => ({ ...previous, error: `${text5(error?.message) || "\u91CD\u8DD1\u5931\u8D25\u3002"} \u53EF\u4EE5\u4FEE\u6B63\u540E\u518D\u70B9\u201C\u91CD\u8DD1\u6B64\u6B65\u201D\uFF0C\u6216\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u8C03\u7528 model_router_rerun_step\u3002` }));
|
|
13053
13181
|
} finally {
|
|
13182
|
+
mutationBusy.current = false;
|
|
13054
13183
|
if (mounted.current) setBusy(false);
|
|
13055
13184
|
}
|
|
13056
13185
|
};
|
|
@@ -13187,6 +13316,8 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
13187
13316
|
const ledger = await workbench.refreshLedger();
|
|
13188
13317
|
if (request !== generationRequest.current || inputs !== generationInputs.current || !mounted.current) return;
|
|
13189
13318
|
if (!ledger) throw new Error("\u8BF7\u5148\u6062\u590D\u6267\u884C\u8BB0\u5F55\u8FDE\u63A5\uFF1B\u65E0\u6CD5\u6838\u5BF9\u6700\u65B0\u6570\u636E\u4E0E\u5B66\u4E60\u72B6\u6001\u3002");
|
|
13319
|
+
if (ledger.storageAvailable === false) throw new Error("\u6267\u884C\u8BB0\u5F55\u5B58\u50A8\u6682\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u6838\u5BF9\u8D39\u7528\u4E0E\u5B66\u4E60\u72B6\u6001\uFF1B\u8BF7\u6062\u590D\u8FDE\u63A5\u540E\u518D\u89C4\u5212\u3002");
|
|
13320
|
+
if (ledger.budget?.historyVerified === false) throw new Error("\u8D39\u7528\u5386\u53F2\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u4ECE\u5907\u4EFD\u6062\u590D\u5E76\u6838\u9A8C\u540E\u518D\u89C4\u5212\u3002");
|
|
13190
13321
|
if (signature2 !== routingSettingsSignature(settingsScope.getSnapshot().value ?? {})) throw new Error("\u89C4\u5212\u8BBE\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\u3002");
|
|
13191
13322
|
const next = createWorkspacePlan(task, catalogState.catalog, {
|
|
13192
13323
|
...ledger.routingData,
|
|
@@ -13294,11 +13425,12 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
13294
13425
|
CostControlCard,
|
|
13295
13426
|
{
|
|
13296
13427
|
ledger: workbench.ledger.value,
|
|
13297
|
-
error: workbench.ledger.error
|
|
13428
|
+
error: workbench.ledger.error || workbench.ledger.value?.storageNotice || "",
|
|
13429
|
+
refreshing: workbench.ledger.refreshing,
|
|
13298
13430
|
settingsScope,
|
|
13299
13431
|
onChanged: () => {
|
|
13300
13432
|
invalidatePlan();
|
|
13301
|
-
|
|
13433
|
+
return workbench.refreshLedger();
|
|
13302
13434
|
}
|
|
13303
13435
|
}
|
|
13304
13436
|
), /* @__PURE__ */ import_react5.default.createElement(
|
package/.dsh-plugin/index.mjs
CHANGED
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { createRouterState } from './shared/router-state.mjs'
|
|
15
15
|
import { applyFeedbackProfile, buildFeedbackProfile } from './shared/adaptive-feedback.mjs'
|
|
16
16
|
import { applyPricingSnapshot, createDynamicDataRefresher, dynamicDataSummary, routingData } from './shared/dynamic-data.mjs'
|
|
17
|
+
import { projectDynamicData } from './shared/dynamic-data-view.mjs'
|
|
17
18
|
import {
|
|
18
19
|
budgetCheck, buildRunRecord, formatUsd, buildTeamRunRecord, buildToolRunRecord, billingOf, mergeRerun,
|
|
19
20
|
spending, storedResults, actualCost, teamExecutionResults,
|
|
@@ -180,8 +181,14 @@ export function routerStateStore() {
|
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
async function savedState() {
|
|
183
|
-
try {
|
|
184
|
-
|
|
184
|
+
try {
|
|
185
|
+
const saved = await routerStateStore().read()
|
|
186
|
+
const storageAvailable = !(saved.notices ?? []).some(notice => notice.kind === 'state-recovery-pending')
|
|
187
|
+
return { ...saved, storageAvailable, historyVerified: storageAvailable && saved.historyIncomplete !== true }
|
|
188
|
+
} catch {
|
|
189
|
+
return { onboarding: { completedAt: null }, health: null, runs: [], storageAvailable: false, historyVerified: false,
|
|
190
|
+
notices: [{ kind: 'state-read-failed', at: Date.now(), message: '执行记录暂时无法读取,费用与学习摘要尚未核验;请恢复存储连接后重试。' }] }
|
|
191
|
+
}
|
|
185
192
|
}
|
|
186
193
|
|
|
187
194
|
const NOTICE_DAYS = 7
|
|
@@ -344,6 +351,9 @@ function budgetSettings(config) {
|
|
|
344
351
|
|
|
345
352
|
function budgetFor(config, saved, estimateUsd) {
|
|
346
353
|
const settings = budgetSettings(config)
|
|
354
|
+
if ((saved.storageAvailable === false || saved.historyVerified === false) && (settings.dailyLimitUsd > 0 || settings.monthlyLimitUsd > 0)) {
|
|
355
|
+
throw new Error('无法读取费用历史,不能核验日/月预算;请恢复存储连接后再执行。')
|
|
356
|
+
}
|
|
347
357
|
const spent = spending(saved.runs, undefined, saved.archivedSpending)
|
|
348
358
|
return { ...budgetCheck({ estimateUsd, spent, ...settings }), spent, ...settings }
|
|
349
359
|
}
|
|
@@ -395,18 +405,10 @@ async function routingSavedState(config) {
|
|
|
395
405
|
try { refreshedData = await dynamicRefresher.refresh(options) } catch { /* state/read errors use the normal fallback */ }
|
|
396
406
|
}
|
|
397
407
|
const saved = await savedState()
|
|
398
|
-
//
|
|
399
|
-
//
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
saved.dynamicData = { ...saved.dynamicData, status: { ...saved.dynamicData.status } }
|
|
403
|
-
for (const [kind, endpoint] of [['pricing', options.pricingSnapshotEndpoint], ['liveBench', options.liveBenchEndpoint]]) {
|
|
404
|
-
if (!text(endpoint)) {
|
|
405
|
-
saved.dynamicData[kind] = null
|
|
406
|
-
saved.dynamicData.status[kind] = { disabled: true, endpoint: '', error: '' }
|
|
407
|
-
}
|
|
408
|
-
}
|
|
409
|
-
}
|
|
408
|
+
// Config can change while a public request is in flight. Reproject against
|
|
409
|
+
// the current settings, not the options captured before awaiting refresh.
|
|
410
|
+
// Cached source A must not masquerade as a newly configured source B.
|
|
411
|
+
saved.dynamicData = projectDynamicData(refreshedData ?? saved.dynamicData, dynamicOptions(config))
|
|
410
412
|
saved.routingLearning = buildFeedbackProfile(saved.runs, feedbackOptions(config))
|
|
411
413
|
return saved
|
|
412
414
|
}
|
|
@@ -1100,11 +1102,15 @@ export async function ledgerSummary(config = {}, { limit = 30 } = {}) {
|
|
|
1100
1102
|
return {
|
|
1101
1103
|
runs: saved.runs.slice(-limit).reverse().map(run => ({
|
|
1102
1104
|
...run,
|
|
1103
|
-
task: run.task.slice(0, 2_000),
|
|
1104
|
-
packages: run.packages.
|
|
1105
|
+
task: text(run.task).slice(0, 2_000),
|
|
1106
|
+
packages: (Array.isArray(run.packages) ? run.packages : []).filter(item => item && typeof item === 'object')
|
|
1107
|
+
.map(item => ({ ...item, answer: text(item.answer).slice(0, 1_500) })),
|
|
1105
1108
|
})),
|
|
1106
1109
|
spent,
|
|
1107
|
-
budget: { ...settings, ...budgetCheck({ estimateUsd: null, spent, ...settings }) },
|
|
1110
|
+
budget: { ...settings, ...budgetCheck({ estimateUsd: null, spent, ...settings }), historyVerified: saved.historyVerified !== false },
|
|
1111
|
+
storageAvailable: saved.storageAvailable !== false,
|
|
1112
|
+
storageNotice: saved.storageAvailable === false ? '执行记录存储暂不可用;下方费用与学习摘要尚未核验,不能当作零花费或没有反馈。'
|
|
1113
|
+
: saved.historyVerified === false ? '此前执行记录已损坏或不完整,当前摘要只包含可读记录;不能视为完整费用历史,请从备份恢复后再核验预算。' : '',
|
|
1108
1114
|
// Legacy scalar quality biases are intentionally no longer learned.
|
|
1109
1115
|
biases: {},
|
|
1110
1116
|
learning: saved.routingLearning,
|