@ljwei-stak/dsh-model-router 0.15.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 +701 -180
- package/.dsh-plugin/index.mjs +126 -25
- package/.dsh-plugin/shared/adaptive-feedback.mjs +272 -0
- package/.dsh-plugin/shared/assignment-solver.mjs +86 -0
- package/.dsh-plugin/shared/dynamic-data-view.mjs +120 -0
- package/.dsh-plugin/shared/dynamic-data.mjs +372 -0
- package/.dsh-plugin/shared/harness-plan.mjs +5 -2
- package/.dsh-plugin/shared/livebench.mjs +57 -20
- package/.dsh-plugin/shared/model-profiles.mjs +7 -1
- package/.dsh-plugin/shared/official-tools-remote.mjs +3 -1
- package/.dsh-plugin/shared/router-state.mjs +140 -38
- package/.dsh-plugin/shared/router.mjs +115 -152
- package/.dsh-plugin/shared/run-ledger.mjs +37 -11
- package/CHANGELOG.md +29 -0
- package/INSTALLATION_GUIDE.zh.md +21 -15
- package/README.md +14 -6
- package/README.zh.md +14 -6
- package/docs/ROUTING_ADAPTIVE_LEARNING.zh.md +153 -0
- package/docs/ROUTING_DERIVATION_EXPERIMENTS.zh.md +684 -0
- package/docs/ROUTING_LIVEBENCH_DATA_SEARCH.zh.md +74 -0
- package/docs/ROUTING_LIVEBENCH_VALIDATION.zh.md +154 -0
- package/docs/ROUTING_MATURITY.zh.md +76 -0
- package/docs/ROUTING_RESEARCH.zh.md +59 -0
- package/docs/ROUTING_SELFTEST_STEPS.zh.md +192 -0
- package/docs/WORKBENCH_USER_GUIDE.zh.md +25 -3
- package/package.json +19 -2
package/.dsh-plugin/client.js
CHANGED
|
@@ -174,7 +174,7 @@ function nonnegative(value, label) {
|
|
|
174
174
|
function normalizeProfile(entry, index) {
|
|
175
175
|
const label = `\u7B2C ${index + 1} \u4E2A\u6A21\u578B`;
|
|
176
176
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
177
|
-
const allowed = /* @__PURE__ */ new Set(["provider", "model", "quality", "pricing", "specialties", "cliModel", "execution", "billing", "subscription", "apiRoute"]);
|
|
177
|
+
const allowed = /* @__PURE__ */ new Set(["provider", "model", "benchmarkModel", "quality", "pricing", "specialties", "cliModel", "execution", "billing", "subscription", "apiRoute"]);
|
|
178
178
|
const unknown = Object.keys(entry).find((key) => !allowed.has(key));
|
|
179
179
|
if (unknown) throw new Error(`${label} \u542B\u4E0D\u652F\u6301\u7684\u5B57\u6BB5 ${unknown}\uFF1B\u4E0D\u8981\u5728\u8FD9\u91CC\u586B\u5199\u5BC6\u94A5\u6216\u547D\u4EE4`);
|
|
180
180
|
const provider = id(entry.provider);
|
|
@@ -183,6 +183,11 @@ function normalizeProfile(entry, index) {
|
|
|
183
183
|
throw new Error(`${label} \u9700\u8981\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E provider \u548C model`);
|
|
184
184
|
}
|
|
185
185
|
const profile = { provider, model };
|
|
186
|
+
if (entry.benchmarkModel !== void 0) {
|
|
187
|
+
const benchmarkModel = id(entry.benchmarkModel);
|
|
188
|
+
if (!benchmarkModel || benchmarkModel.length > 240 || /[\u0000-\u001f]/u.test(benchmarkModel)) throw new Error(`${label} \u7684 benchmarkModel \u9700\u8981\u51C6\u786E\u7684\u57FA\u51C6\u6A21\u578B\u540D\u79F0`);
|
|
189
|
+
profile.benchmarkModel = benchmarkModel;
|
|
190
|
+
}
|
|
186
191
|
if (entry.quality !== void 0) {
|
|
187
192
|
const value = nonnegative(entry.quality, `${label} \u7684 quality`);
|
|
188
193
|
if (value > 100) throw new Error(`${label} \u7684 quality \u5E94\u5728 0 \u5230 100 \u4E4B\u95F4`);
|
|
@@ -278,6 +283,7 @@ function applyModelProfiles(routes, profiles) {
|
|
|
278
283
|
if (!profile) return route;
|
|
279
284
|
return {
|
|
280
285
|
...route,
|
|
286
|
+
...profile.benchmarkModel === void 0 ? {} : { benchmarkModel: profile.benchmarkModel },
|
|
281
287
|
...profile.quality === void 0 ? {} : { quality: profile.quality, qualitySource: "user" },
|
|
282
288
|
...profile.pricing === void 0 ? {} : { pricing: { ...profile.pricing }, pricingSource: "user" },
|
|
283
289
|
...profile.specialties === void 0 ? {} : { specialties: [...profile.specialties] },
|
|
@@ -466,7 +472,7 @@ var TASK_ALIASES = Object.freeze({
|
|
|
466
472
|
var CATEGORY_TO_TASK = Object.freeze({
|
|
467
473
|
reasoning: "reasoning",
|
|
468
474
|
coding: "code",
|
|
469
|
-
|
|
475
|
+
// Agentic Coding is a different task family, not silently a Coding score.
|
|
470
476
|
mathematics: "math",
|
|
471
477
|
"data analysis": "research",
|
|
472
478
|
language: "writing",
|
|
@@ -478,7 +484,85 @@ function normalized(value) {
|
|
|
478
484
|
return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
479
485
|
}
|
|
480
486
|
function liveBenchRow(snapshot, model) {
|
|
481
|
-
|
|
487
|
+
const row = snapshot?.models?.[normalized(model)] ?? null;
|
|
488
|
+
return snapshot?.exactModelMatch === true && row?.model !== model ? null : row;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
// .dsh-plugin/shared/assignment-solver.mjs
|
|
492
|
+
var EPSILON = 1e-12;
|
|
493
|
+
var compareText = (a, b2) => a < b2 ? -1 : a > b2 ? 1 : 0;
|
|
494
|
+
var signature = (state) => JSON.stringify(state.choices.map((option) => option.route));
|
|
495
|
+
function compareStates(left, right, minimizeCost = false) {
|
|
496
|
+
return left.relaxedCount - right.relaxedCount || left.qualityShortfall - right.qualityShortfall || (minimizeCost ? left.cost - right.cost : right.score - left.score) || (minimizeCost ? right.score - left.score : left.cost - right.cost) || left.switches - right.switches || compareText(signature(left), signature(right));
|
|
497
|
+
}
|
|
498
|
+
function solveCandidateAssignments({ tasks = [], pools = [], budget = Infinity, beamWidth = 256, exactLimit = 4096, handoffPenalty = 0.015, minimizeCost = false } = {}) {
|
|
499
|
+
if (!tasks.length || tasks.length !== pools.length) return null;
|
|
500
|
+
const preceding = /* @__PURE__ */ new Set();
|
|
501
|
+
for (const task of tasks) {
|
|
502
|
+
if (!task.id || preceding.has(task.id) || (task.dependsOn ?? []).some((id2) => !preceding.has(id2))) {
|
|
503
|
+
throw new RangeError("Assignment tasks must have unique ids and known dependencies in topological order");
|
|
504
|
+
}
|
|
505
|
+
preceding.add(task.id);
|
|
506
|
+
}
|
|
507
|
+
const constrained = Number.isFinite(budget);
|
|
508
|
+
const options = pools.map((pool) => (Array.isArray(pool) ? pool : []).filter((option) => Number.isFinite(option.cost) && option.cost >= 0 && Number.isFinite(option.score) && (!constrained || option.pricingKnown === true)));
|
|
509
|
+
if (options.some((pool) => pool.length === 0)) return null;
|
|
510
|
+
const suffixMinimum = Array(tasks.length + 1).fill(0);
|
|
511
|
+
let searchSpace = 1;
|
|
512
|
+
let searchSpaceCapped = false;
|
|
513
|
+
for (let index = tasks.length - 1; index >= 0; index--) {
|
|
514
|
+
suffixMinimum[index] = suffixMinimum[index + 1] + Math.min(...options[index].map((option) => option.cost));
|
|
515
|
+
const product = searchSpace * options[index].length;
|
|
516
|
+
if (product > Number.MAX_SAFE_INTEGER) searchSpaceCapped = true;
|
|
517
|
+
searchSpace = Math.min(Number.MAX_SAFE_INTEGER, product);
|
|
518
|
+
}
|
|
519
|
+
if (constrained && suffixMinimum[0] > budget + EPSILON) return null;
|
|
520
|
+
const exact = !searchSpaceCapped && searchSpace <= Math.max(1, exactLimit);
|
|
521
|
+
const width = Math.max(1, Math.floor(beamWidth) || 256);
|
|
522
|
+
const search = { method: exact ? "exact" : "beam", exact, searchSpace, searchSpaceCapped, expandedStates: 0, beamPruned: 0 };
|
|
523
|
+
let states = [{ choices: [], routesByTask: /* @__PURE__ */ new Map(), score: 0, cost: 0, switches: 0, relaxedCount: 0, qualityShortfall: 0 }];
|
|
524
|
+
for (let index = 0; index < tasks.length; index++) {
|
|
525
|
+
const task = tasks[index];
|
|
526
|
+
const expanded = [];
|
|
527
|
+
for (const state of states) {
|
|
528
|
+
for (const option of options[index]) {
|
|
529
|
+
const cost = state.cost + option.cost;
|
|
530
|
+
if (constrained && cost + suffixMinimum[index + 1] > budget + EPSILON) continue;
|
|
531
|
+
const switches = (task.dependsOn ?? []).reduce((count, dependency) => {
|
|
532
|
+
const route = state.routesByTask.get(dependency);
|
|
533
|
+
return count + (route !== void 0 && route !== option.route ? 1 : 0);
|
|
534
|
+
}, 0);
|
|
535
|
+
const penalty = switches * handoffPenalty;
|
|
536
|
+
const shortfall = Math.max(0, Number(option.qualityShortfall) || 0);
|
|
537
|
+
const routesByTask2 = new Map(state.routesByTask);
|
|
538
|
+
routesByTask2.set(task.id, option.route);
|
|
539
|
+
expanded.push({
|
|
540
|
+
choices: [...state.choices, { ...option, handoffPenalty: penalty }],
|
|
541
|
+
routesByTask: routesByTask2,
|
|
542
|
+
score: state.score + option.score - penalty,
|
|
543
|
+
cost,
|
|
544
|
+
switches: state.switches + switches,
|
|
545
|
+
relaxedCount: state.relaxedCount + (shortfall > 0 ? 1 : 0),
|
|
546
|
+
qualityShortfall: state.qualityShortfall + shortfall
|
|
547
|
+
});
|
|
548
|
+
search.expandedStates++;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
if (!expanded.length) return null;
|
|
552
|
+
expanded.sort((left, right) => compareStates(left, right, minimizeCost));
|
|
553
|
+
if (!exact && expanded.length > width) {
|
|
554
|
+
states = expanded.slice(0, width);
|
|
555
|
+
if (constrained && index < tasks.length - 1) {
|
|
556
|
+
const cheapest = expanded.reduce((best2, state) => compareStates(state, best2, true) < 0 ? state : best2);
|
|
557
|
+
if (!states.includes(cheapest)) states[width - 1] = cheapest;
|
|
558
|
+
}
|
|
559
|
+
search.beamPruned += expanded.length - states.length;
|
|
560
|
+
} else states = expanded;
|
|
561
|
+
}
|
|
562
|
+
states.sort((left, right) => compareStates(left, right, minimizeCost));
|
|
563
|
+
const best = states[0];
|
|
564
|
+
const { routesByTask, ...result } = best;
|
|
565
|
+
return { ...result, minimumCostLowerBound: suffixMinimum[0], search };
|
|
482
566
|
}
|
|
483
567
|
|
|
484
568
|
// .dsh-plugin/shared/router.mjs
|
|
@@ -590,9 +674,12 @@ function specialtyMatch(model, taskType, liveScores = {}) {
|
|
|
590
674
|
return 0.38;
|
|
591
675
|
}
|
|
592
676
|
function qualityForTask(row, taskType) {
|
|
593
|
-
const base = asScore(row?.liveScores?.[taskType]) ?? asScore(row?.liveOverall) ?? row?.metadata?.quality ?? row?.quality ?? 0;
|
|
677
|
+
const base = asScore(row?.liveScores?.[taskType]) ?? asScore(row?.liveOverall) ?? row?.metadata?.quality ?? row?.baseQuality ?? row?.quality ?? 0;
|
|
594
678
|
return row?.qualityBias ? clamp(base + row.qualityBias) : base;
|
|
595
679
|
}
|
|
680
|
+
function qualitySourceForTask(row, taskType) {
|
|
681
|
+
return asScore(row?.liveScores?.[taskType]) !== void 0 || asScore(row?.liveOverall) !== void 0 ? "livebench" : row?.fallbackQualitySource ?? row?.qualitySource ?? "unknown";
|
|
682
|
+
}
|
|
596
683
|
function specialtyForTask(row, taskType) {
|
|
597
684
|
return specialtyMatch(row.metadata, taskType, row.liveScores);
|
|
598
685
|
}
|
|
@@ -644,20 +731,7 @@ function normalizedCacheRatios(cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
|
644
731
|
const write = Number.isFinite(Number(cacheWriteRatio)) ? Math.min(clamp(Number(cacheWriteRatio)), 1 - read) : 0;
|
|
645
732
|
return { read, write };
|
|
646
733
|
}
|
|
647
|
-
|
|
648
|
-
const { read, write } = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
|
|
649
|
-
return {
|
|
650
|
-
input: (1 - read - write) * Number(pricing.input) + read * Number(pricing.cacheRead) + write * Number(pricing.cacheWrite),
|
|
651
|
-
output: Number(pricing.output)
|
|
652
|
-
};
|
|
653
|
-
}
|
|
654
|
-
function costScore(pricing, maxCost, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
655
|
-
if (pricing === null) return 0;
|
|
656
|
-
const effective = effectivePricing(pricing, cacheReadRatio, cacheWriteRatio);
|
|
657
|
-
const mean = (effective.input + effective.output) / 2;
|
|
658
|
-
if (maxCost <= 0) return mean === 0 ? 1 : 0;
|
|
659
|
-
return clamp(1 - mean / maxCost);
|
|
660
|
-
}
|
|
734
|
+
var COST_REFERENCE_PRICING = Object.freeze({ input: 1, output: 5, cacheRead: 1, cacheWrite: 1, currency: "USD" });
|
|
661
735
|
function estimateCost(model, text6, outputTokens = 900, pricingOverrides = {}, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
662
736
|
const pricing = pricingFor(model, pricingOverrides);
|
|
663
737
|
if (pricing === null) return null;
|
|
@@ -898,13 +972,13 @@ function weightsForTask(weights, task) {
|
|
|
898
972
|
if (task.weights) return task.weights;
|
|
899
973
|
return task.purpose === "synthesis" ? SYNTHESIS_WEIGHTS : OBJECTIVE_WEIGHTS[task.difficulty] ?? weights;
|
|
900
974
|
}
|
|
901
|
-
function
|
|
975
|
+
function compareText2(left, right) {
|
|
902
976
|
const a = String(left);
|
|
903
977
|
const b2 = String(right);
|
|
904
978
|
return a < b2 ? -1 : a > b2 ? 1 : 0;
|
|
905
979
|
}
|
|
906
980
|
function compareRowsStable(left, right) {
|
|
907
|
-
return
|
|
981
|
+
return compareText2(routeKey(left.provider, left.model), routeKey(right.provider, right.model));
|
|
908
982
|
}
|
|
909
983
|
function reasoningEffortRank(effort) {
|
|
910
984
|
const normalized2 = String(effort ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
@@ -925,7 +999,7 @@ function selectReasoningEffort(efforts, preferred = "medium") {
|
|
|
925
999
|
return exact.slice().sort((left, right) => {
|
|
926
1000
|
const distance = Math.abs(reasoningEffortRank(left) - preferredRank) - Math.abs(reasoningEffortRank(right) - preferredRank);
|
|
927
1001
|
if (distance !== 0) return distance;
|
|
928
|
-
return reasoningEffortRank(left) - reasoningEffortRank(right) ||
|
|
1002
|
+
return reasoningEffortRank(left) - reasoningEffortRank(right) || compareText2(left, right);
|
|
929
1003
|
})[0];
|
|
930
1004
|
}
|
|
931
1005
|
function reasoningDecision(row, task) {
|
|
@@ -951,16 +1025,17 @@ function reasoningDecision(row, task) {
|
|
|
951
1025
|
multiplier: reasoningEffortMultiplier(chosen)
|
|
952
1026
|
};
|
|
953
1027
|
}
|
|
954
|
-
function candidateUtility(row, task, weights,
|
|
1028
|
+
function candidateUtility(row, task, weights, cacheReadRatio = 0, cacheWriteRatio = 0, text6 = "", complexity = "balanced") {
|
|
955
1029
|
const quality = qualityForTask(row, task.type);
|
|
1030
|
+
const rawPreference = row.preferenceAdjustments?.[task.type];
|
|
1031
|
+
const preferenceAdjustment = Number.isFinite(rawPreference) ? clamp(rawPreference, -0.1, 0.1) : 0;
|
|
956
1032
|
const floor = Number(task.qualityFloor ?? taskQualityFloor("complex", task));
|
|
957
1033
|
const qualityGap = Math.max(0, floor - quality);
|
|
958
|
-
const duplicatePenalty = 0;
|
|
959
|
-
const synthesisPreference = task.purpose === "synthesis" && /deepseek[- ]?v4[- ]?pro/i.test(row.model) ? 0.025 : 0;
|
|
960
1034
|
const reasoning = reasoningDecision(row, task);
|
|
961
|
-
const
|
|
962
|
-
const
|
|
963
|
-
|
|
1035
|
+
const reference = taskCost({ pricing: COST_REFERENCE_PRICING, reasoningKnown: true, reasoningEfforts: [] }, task, text6, complexity, cacheReadRatio, cacheWriteRatio);
|
|
1036
|
+
const cost = row.pricing === null ? 0 : 1 / (1 + taskCost(row, task, text6, complexity, cacheReadRatio, cacheWriteRatio) / Math.max(reference, 1e-12));
|
|
1037
|
+
const score = weights.quality * quality + weights.cost * cost + weights.latency * (1 - clamp(row.latency * reasoning.multiplier.latency)) + weights.specialty * specialtyForTask(row, task.type) + (weights.reasoning ?? 0) * reasoning.reasoningFit - weights.risk * row.risk - qualityGap * (task.criticality ?? 0.75) + preferenceAdjustment;
|
|
1038
|
+
return { score, floor, qualityGap, preferenceAdjustment, ...reasoning };
|
|
964
1039
|
}
|
|
965
1040
|
function taskCost(row, task, text6, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
966
1041
|
if (row?.pricing === null) return 0;
|
|
@@ -992,96 +1067,65 @@ function dominates(left, right, task, text6, complexity, cacheReadRatio, cacheWr
|
|
|
992
1067
|
const strictlyBetter = leftValues.quality > rightValues.quality || leftValues.cost < rightValues.cost || leftValues.latency < rightValues.latency || leftValues.specialty > rightValues.specialty || leftValues.reasoning > rightValues.reasoning || leftValues.risk < rightValues.risk;
|
|
993
1068
|
return noWorse && strictlyBetter;
|
|
994
1069
|
}
|
|
995
|
-
function
|
|
996
|
-
|
|
1070
|
+
function eligibleRowsForTask(rows, task) {
|
|
1071
|
+
return task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
|
|
1072
|
+
}
|
|
1073
|
+
function candidatePool(rows, task, weights, text6, complexity, cacheReadRatio, cacheWriteRatio, dependencySensitive = false) {
|
|
1074
|
+
const eligibleRows = eligibleRowsForTask(rows, task);
|
|
997
1075
|
const floor = Number(task.qualityFloor ?? 0);
|
|
998
1076
|
const feasible = eligibleRows.filter((row) => qualityForTask(row, task.type) >= floor);
|
|
999
1077
|
const source = feasible.length > 0 ? feasible : eligibleRows.slice().sort((left, right) => qualityForTask(right, task.type) - qualityForTask(left, task.type) || compareRowsStable(left, right)).slice(0, 3);
|
|
1000
1078
|
const taskWeights = weightsForTask(weights, task);
|
|
1001
1079
|
const scored = source.map((row) => ({
|
|
1002
1080
|
row,
|
|
1003
|
-
decision: candidateUtility(row, task, taskWeights,
|
|
1081
|
+
decision: candidateUtility(row, task, taskWeights, cacheReadRatio, cacheWriteRatio, text6, complexity),
|
|
1004
1082
|
cost: taskCost(row, task, text6, complexity, cacheReadRatio, cacheWriteRatio)
|
|
1005
1083
|
}));
|
|
1006
|
-
const frontier = scored.filter((item) => !
|
|
1084
|
+
const frontier = dependencySensitive ? scored : scored.filter((item) => !scored.some((other) => other !== item && other.decision.score >= item.decision.score && dominates(other.row, item.row, task, text6, complexity, cacheReadRatio, cacheWriteRatio)));
|
|
1007
1085
|
const essential = [
|
|
1008
|
-
scored.
|
|
1086
|
+
scored.filter((item) => item.row.pricing !== null).sort((left, right) => left.cost - right.cost || compareRowsStable(left.row, right.row))[0],
|
|
1009
1087
|
scored.slice().sort((left, right) => right.decision.score - left.decision.score || compareRowsStable(left.row, right.row))[0],
|
|
1010
1088
|
scored.slice().sort((left, right) => qualityForTask(right.row, task.type) - qualityForTask(left.row, task.type) || compareRowsStable(left.row, right.row))[0]
|
|
1011
1089
|
].filter(Boolean);
|
|
1012
|
-
const
|
|
1090
|
+
const all = [...frontier, ...essential].filter((item, index, all2) => all2.findIndex((candidate) => candidate.row === item.row) === index).sort((left, right) => right.decision.score - left.decision.score || left.cost - right.cost || compareRowsStable(left.row, right.row));
|
|
1091
|
+
const anchors = [...new Set(essential)];
|
|
1092
|
+
const ordered = [...anchors, ...all.filter((item) => !anchors.includes(item))].slice(0, ROUTING_CANDIDATE_LIMIT).sort((left, right) => right.decision.score - left.decision.score || left.cost - right.cost || compareRowsStable(left.row, right.row));
|
|
1013
1093
|
return {
|
|
1014
1094
|
options: ordered,
|
|
1015
1095
|
relaxed: feasible.length === 0,
|
|
1016
|
-
pruned: Math.max(0,
|
|
1096
|
+
pruned: Math.max(0, scored.length - all.length),
|
|
1097
|
+
filtered: Math.max(0, eligibleRows.length - source.length),
|
|
1098
|
+
truncated: Math.max(0, all.length - ordered.length),
|
|
1099
|
+
dependencySensitive
|
|
1017
1100
|
};
|
|
1018
1101
|
}
|
|
1019
|
-
function
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
if (
|
|
1031
|
-
const suffixMinimum = Array(tasks.length + 1).fill(0);
|
|
1032
|
-
for (let index = tasks.length - 1; index >= 0; index -= 1) {
|
|
1033
|
-
const costOptions = Number.isFinite(budget) ? pools[index].options.filter((option) => option.row.pricing !== null) : pools[index].options;
|
|
1034
|
-
if (costOptions.length === 0) return null;
|
|
1035
|
-
suffixMinimum[index] = suffixMinimum[index + 1] + Math.min(...costOptions.map((option) => option.cost));
|
|
1036
|
-
}
|
|
1037
|
-
if (Number.isFinite(budget) && suffixMinimum[0] > budget + 1e-12) return null;
|
|
1038
|
-
let states = [{ assignments: [], routesByTask: /* @__PURE__ */ new Map(), usedRoutes: /* @__PURE__ */ new Set(), score: 0, cost: 0, switches: 0, relaxedCount: 0, qualityShortfall: 0 }];
|
|
1039
|
-
for (let index = 0; index < tasks.length; index += 1) {
|
|
1040
|
-
const task = tasks[index];
|
|
1041
|
-
const pool = pools[index];
|
|
1042
|
-
const expanded = [];
|
|
1043
|
-
for (const state of states) {
|
|
1044
|
-
for (const option of pool.options) {
|
|
1045
|
-
if (Number.isFinite(budget) && option.row.pricing === null) continue;
|
|
1046
|
-
const nextCost = state.cost + option.cost;
|
|
1047
|
-
if (Number.isFinite(budget) && nextCost + suffixMinimum[index + 1] > budget + 1e-12) continue;
|
|
1048
|
-
const taskWeights = weightsForTask(weights, task);
|
|
1049
|
-
const decision = candidateUtility(option.row, task, taskWeights, maxCost, state.usedRoutes, cacheReadRatio, cacheWriteRatio);
|
|
1050
|
-
const route = routeKey(option.row.provider, option.row.model);
|
|
1051
|
-
const dependencySwitches = (task.dependsOn ?? []).reduce((count, dependency) => {
|
|
1052
|
-
const dependencyRoute = state.routesByTask.get(dependency);
|
|
1053
|
-
return count + (dependencyRoute !== void 0 && dependencyRoute !== route ? 1 : 0);
|
|
1054
|
-
}, 0);
|
|
1055
|
-
const handoffPenalty = dependencySwitches * 0.015;
|
|
1056
|
-
const qualityShortfall = Math.max(0, decision.floor - qualityForTask(option.row, task.type));
|
|
1057
|
-
const usedRoutes = new Set(state.usedRoutes);
|
|
1058
|
-
usedRoutes.add(route);
|
|
1059
|
-
const routesByTask = new Map(state.routesByTask);
|
|
1060
|
-
routesByTask.set(task.id, route);
|
|
1061
|
-
expanded.push({
|
|
1062
|
-
assignments: [...state.assignments, { task, row: option.row, decision: { ...decision, relaxed: qualityShortfall > 0 }, estimatedCost: option.cost, handoffPenalty }],
|
|
1063
|
-
routesByTask,
|
|
1064
|
-
usedRoutes,
|
|
1065
|
-
score: state.score + decision.score - handoffPenalty,
|
|
1066
|
-
cost: nextCost,
|
|
1067
|
-
switches: state.switches + dependencySwitches,
|
|
1068
|
-
relaxedCount: state.relaxedCount + (qualityShortfall > 0 ? 1 : 0),
|
|
1069
|
-
qualityShortfall: state.qualityShortfall + qualityShortfall
|
|
1070
|
-
});
|
|
1071
|
-
}
|
|
1072
|
-
}
|
|
1073
|
-
if (expanded.length === 0) return null;
|
|
1074
|
-
expanded.sort(minimizeCost ? compareCostStates : compareUtilityStates);
|
|
1075
|
-
states = expanded.slice(0, ROUTING_BEAM_WIDTH);
|
|
1076
|
-
}
|
|
1077
|
-
states.sort(minimizeCost ? compareCostStates : compareUtilityStates);
|
|
1102
|
+
function solveAssignments({ rows, tasks, weights, text: text6, complexity, budget, cacheReadRatio, cacheWriteRatio, minimizeCost = false }) {
|
|
1103
|
+
const dependencySensitive = tasks.some((task) => (task.dependsOn ?? []).length > 0);
|
|
1104
|
+
const pools = tasks.map((task) => candidatePool(rows, task, weights, text6, complexity, cacheReadRatio, cacheWriteRatio, dependencySensitive));
|
|
1105
|
+
const result = solveCandidateAssignments({ tasks, pools: pools.map((pool, index) => pool.options.map((option) => ({
|
|
1106
|
+
route: routeKey(option.row.provider, option.row.model),
|
|
1107
|
+
score: option.decision.score,
|
|
1108
|
+
cost: option.cost,
|
|
1109
|
+
qualityShortfall: Math.max(0, option.decision.floor - qualityForTask(option.row, tasks[index].type)),
|
|
1110
|
+
pricingKnown: option.row.pricing !== null,
|
|
1111
|
+
payload: option
|
|
1112
|
+
}))), budget, beamWidth: ROUTING_BEAM_WIDTH, minimizeCost });
|
|
1113
|
+
if (!result) return null;
|
|
1078
1114
|
return {
|
|
1079
|
-
...
|
|
1115
|
+
...result,
|
|
1116
|
+
assignments: result.choices.map((choice, index) => ({
|
|
1117
|
+
task: tasks[index],
|
|
1118
|
+
row: choice.payload.row,
|
|
1119
|
+
decision: { ...choice.payload.decision, relaxed: choice.qualityShortfall > 0 },
|
|
1120
|
+
estimatedCost: choice.cost,
|
|
1121
|
+
handoffPenalty: choice.handoffPenalty
|
|
1122
|
+
})),
|
|
1123
|
+
usedRoutes: new Set(result.choices.map((choice) => choice.route)),
|
|
1080
1124
|
candidatePools: pools,
|
|
1081
|
-
minimumFeasibleCost:
|
|
1125
|
+
minimumFeasibleCost: result.minimumCostLowerBound
|
|
1082
1126
|
};
|
|
1083
1127
|
}
|
|
1084
|
-
function buildPlan({ text: text6 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0, preset = DEFAULT_ROUTING_PRESET } = {}) {
|
|
1128
|
+
function buildPlan({ text: text6 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", dataVersions = null, learning = null, budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0, preset = DEFAULT_ROUTING_PRESET } = {}) {
|
|
1085
1129
|
const presetId = normalizeRoutingPreset(preset);
|
|
1086
1130
|
const firstLine = String(text6 ?? "").split(/\r?\n/u)[0].trim();
|
|
1087
1131
|
const transformOnly = /^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(firstLine) && !/(?:执行|完成|实施|分配)/u.test(firstLine);
|
|
@@ -1097,17 +1141,21 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1097
1141
|
return {
|
|
1098
1142
|
provider: String(entry.provider ?? ""),
|
|
1099
1143
|
model: String(entry.model ?? ""),
|
|
1144
|
+
benchmarkModel: typeof entry.benchmarkModel === "string" ? entry.benchmarkModel : void 0,
|
|
1100
1145
|
reasoningEfforts: [...new Set(reasoningEfforts)],
|
|
1101
1146
|
defaultReasoningEffort: entry.defaultReasoningEffort === void 0 ? void 0 : String(entry.defaultReasoningEffort),
|
|
1102
1147
|
reasoningKnown: entry.reasoningKnown === true || entry.reasoningKnown === void 0 && Array.isArray(entry.reasoningEfforts),
|
|
1103
1148
|
quality: asScore(entry.quality),
|
|
1104
1149
|
qualityBias: Number.isFinite(entry.qualityBias) ? clamp(entry.qualityBias, -0.05, 0.05) : 0,
|
|
1150
|
+
preferenceAdjustments: entry.preferenceAdjustments ?? null,
|
|
1105
1151
|
qualitySource: entry.qualitySource === "user" ? "user" : "route",
|
|
1106
1152
|
latency: asScore(entry.latency),
|
|
1107
1153
|
risk: asScore(entry.risk),
|
|
1108
1154
|
specialties: Array.isArray(entry.specialties) ? entry.specialties.filter((item) => typeof item === "string") : null,
|
|
1109
1155
|
pricing: normalizePricing({ route: entry.pricing ?? entry.price }).route ?? null,
|
|
1110
|
-
pricingSource: entry.pricingSource === "user" ? "user" : "route",
|
|
1156
|
+
pricingSource: entry.pricingSource === "user" ? "user" : entry.pricingSource === "dynamic" ? "dynamic" : "route",
|
|
1157
|
+
pricingVersion: entry.pricingVersion ?? null,
|
|
1158
|
+
snapshotAsOf: entry.snapshotAsOf ?? null,
|
|
1111
1159
|
inputModalities: Array.isArray(entry.inputModalities) ? entry.inputModalities.map((item) => String(item).toLowerCase()) : []
|
|
1112
1160
|
};
|
|
1113
1161
|
}) : [];
|
|
@@ -1123,10 +1171,12 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1123
1171
|
...route.risk === void 0 ? {} : { risk: route.risk },
|
|
1124
1172
|
...route.specialties === null ? {} : { specialties: route.specialties }
|
|
1125
1173
|
};
|
|
1126
|
-
const live = liveBenchRow(liveBench, route.model);
|
|
1174
|
+
const live = liveBenchRow(liveBench, route.benchmarkModel ?? route.model);
|
|
1127
1175
|
const liveScores = live?.scores ?? {};
|
|
1128
|
-
const liveOverall = asScore(live?.overall);
|
|
1129
|
-
const
|
|
1176
|
+
const liveOverall = live?.overallSource === "explicit" || live?.overallSource === void 0 && Object.keys(liveScores).length === 0 ? asScore(live?.overall) : void 0;
|
|
1177
|
+
const baseQuality = asScore(metadata.quality) ?? 0;
|
|
1178
|
+
const quality = clamp((asScore(liveScores?.[taskType]) ?? liveOverall ?? baseQuality) + route.qualityBias);
|
|
1179
|
+
const fallbackQualitySource = route.quality !== void 0 ? route.qualitySource : catalog ? "catalog-heuristic" : "unknown";
|
|
1130
1180
|
const qualitySource = liveOverall !== void 0 || asScore(liveScores?.[taskType]) !== void 0 ? "livebench" : route.quality !== void 0 ? route.qualitySource : catalog ? "catalog-heuristic" : "unknown";
|
|
1131
1181
|
const userPrice = normalizedPrices[normalize(`${route.provider}/${route.model}`)] ?? normalizedPrices[normalize(route.model)];
|
|
1132
1182
|
const pricingRow = userPrice ?? route.pricing ?? null;
|
|
@@ -1137,9 +1187,14 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1137
1187
|
model: route.model,
|
|
1138
1188
|
metadata,
|
|
1139
1189
|
quality,
|
|
1190
|
+
baseQuality,
|
|
1191
|
+
fallbackQualitySource,
|
|
1140
1192
|
qualityBias: route.qualityBias,
|
|
1193
|
+
preferenceAdjustments: route.preferenceAdjustments,
|
|
1141
1194
|
qualitySource,
|
|
1142
1195
|
pricingSource,
|
|
1196
|
+
pricingVersion: route.pricingVersion,
|
|
1197
|
+
snapshotAsOf: route.snapshotAsOf,
|
|
1143
1198
|
liveScores,
|
|
1144
1199
|
liveOverall,
|
|
1145
1200
|
latency: metadata.latency ?? 0.5,
|
|
@@ -1160,10 +1215,6 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1160
1215
|
}, text6, 900, {}, cacheReadRatio, cacheWriteRatio)
|
|
1161
1216
|
});
|
|
1162
1217
|
}
|
|
1163
|
-
const maxCost = Math.max(1, ...rows.filter((row) => row.pricing !== null).map((row) => {
|
|
1164
|
-
const effective = effectivePricing(row.pricing, cacheReadRatio, cacheWriteRatio);
|
|
1165
|
-
return effective.input + effective.output;
|
|
1166
|
-
}));
|
|
1167
1218
|
const taskNodes = taskPackages(taskType, text6, complexity.band).map((task) => presetId === DEFAULT_ROUTING_PRESET ? task : {
|
|
1168
1219
|
...task,
|
|
1169
1220
|
qualityFloor: presetFloor(task.qualityFloor, presetId),
|
|
@@ -1175,7 +1226,6 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1175
1226
|
rows,
|
|
1176
1227
|
tasks: taskNodes,
|
|
1177
1228
|
weights,
|
|
1178
|
-
maxCost,
|
|
1179
1229
|
text: text6,
|
|
1180
1230
|
complexity: complexity.band,
|
|
1181
1231
|
budget: Number.POSITIVE_INFINITY,
|
|
@@ -1186,7 +1236,6 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1186
1236
|
rows,
|
|
1187
1237
|
tasks: taskNodes,
|
|
1188
1238
|
weights,
|
|
1189
|
-
maxCost,
|
|
1190
1239
|
text: text6,
|
|
1191
1240
|
complexity: complexity.band,
|
|
1192
1241
|
budget,
|
|
@@ -1197,7 +1246,6 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1197
1246
|
rows,
|
|
1198
1247
|
tasks: taskNodes,
|
|
1199
1248
|
weights,
|
|
1200
|
-
maxCost,
|
|
1201
1249
|
text: text6,
|
|
1202
1250
|
complexity: complexity.band,
|
|
1203
1251
|
budget: Number.POSITIVE_INFINITY,
|
|
@@ -1210,7 +1258,8 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1210
1258
|
const usedRoutes = optimized?.usedRoutes ?? /* @__PURE__ */ new Set();
|
|
1211
1259
|
const constraintRelaxed = (optimized?.relaxedCount ?? 0) > 0;
|
|
1212
1260
|
for (const row of rows) {
|
|
1213
|
-
row.score = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band] }, weights,
|
|
1261
|
+
row.score = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band] }, weights, cacheReadRatio, cacheWriteRatio, text6, complexity.band).score;
|
|
1262
|
+
if (row.pricing !== null && taskNodes[0]) row.estimatedCost = taskCost(row, taskNodes[0], text6, complexity.band, cacheReadRatio, cacheWriteRatio);
|
|
1214
1263
|
}
|
|
1215
1264
|
rows.sort((left, right) => right.score - left.score || compareRowsStable(left, right));
|
|
1216
1265
|
const selectedAssignment = assignments[0];
|
|
@@ -1225,7 +1274,7 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1225
1274
|
difficulty: task.difficulty,
|
|
1226
1275
|
recommended: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
|
|
1227
1276
|
recommendedProvider: row?.provider ?? "",
|
|
1228
|
-
qualitySource: row
|
|
1277
|
+
qualitySource: qualitySourceForTask(row, task.type),
|
|
1229
1278
|
pricingSource: row?.pricingSource ?? "unknown",
|
|
1230
1279
|
recommendedReasoningEffort: decision?.reasoningEffort,
|
|
1231
1280
|
preferredReasoningEffort: decision?.preferredReasoningEffort ?? task.preferredReasoningEffort,
|
|
@@ -1253,25 +1302,25 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1253
1302
|
cacheWriteTokens: tokens.cacheWriteTokens,
|
|
1254
1303
|
outputTokens: tokens.outputTokens,
|
|
1255
1304
|
estimatedCost: row?.pricing === null || row === null ? null : Number(taskEstimate.toFixed(6)),
|
|
1256
|
-
quality: row
|
|
1257
|
-
qualitySource: row
|
|
1305
|
+
quality: qualitySourceForTask(row, task.type) === "unknown" || row === null ? null : Number(qualityForTask(row, task.type).toFixed(3)),
|
|
1306
|
+
qualitySource: qualitySourceForTask(row, task.type),
|
|
1258
1307
|
pricingSource: row?.pricingSource ?? "unknown",
|
|
1259
1308
|
handoffPenalty: Number(Number(handoffPenalty ?? 0).toFixed(3))
|
|
1260
1309
|
};
|
|
1261
1310
|
});
|
|
1262
1311
|
const pricingComplete = assignments.length > 0 && assignments.every(({ row }) => row?.pricing !== null);
|
|
1263
|
-
const totalEstimate = pricingComplete ?
|
|
1312
|
+
const totalEstimate = pricingComplete ? assignments.reduce((sum, { task, row, estimatedCost }) => sum + (estimatedCost ?? taskCost(row, task, text6, complexity.band, cacheReadRatio, cacheWriteRatio)), 0) : null;
|
|
1264
1313
|
const baselineRows = assignments.map(({ task }) => {
|
|
1265
|
-
const strongest = rows.reduce((best, row) => qualityForTask(row, task.type) > (best === null ? -1 : qualityForTask(best, task.type)) ? row : best, null);
|
|
1314
|
+
const strongest = eligibleRowsForTask(rows, task).reduce((best, row) => qualityForTask(row, task.type) > (best === null ? -1 : qualityForTask(best, task.type)) ? row : best, null);
|
|
1266
1315
|
return { task, strongest };
|
|
1267
1316
|
});
|
|
1268
1317
|
const baselineCost = baselineRows.every((item) => item.strongest?.pricing !== null && item.strongest !== null) ? baselineRows.reduce((sum, { task, strongest }) => sum + taskCost(strongest, task, text6, complexity.band, cacheReadRatio, cacheWriteRatio), 0) : null;
|
|
1269
|
-
const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row }) => ["livebench", "route", "user"].includes(row
|
|
1318
|
+
const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row, task }) => ["livebench", "route", "user"].includes(qualitySourceForTask(row, task.type))) && baselineRows.every(({ strongest, task }) => ["livebench", "route", "user"].includes(qualitySourceForTask(strongest, task.type)));
|
|
1270
1319
|
const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate !== null ? totalEstimate > Number(budgetUsd) : null;
|
|
1271
1320
|
const savings = baselineCost === null || totalEstimate === null || !qualityEvidenceComplete ? null : baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
|
|
1272
1321
|
const paretoPruned = (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.pruned, 0);
|
|
1273
1322
|
const minimumFeasibleCost = rows.every((row) => row.pricing !== null) ? optimized?.minimumFeasibleCost ?? minimumCostPlan?.cost ?? 0 : null;
|
|
1274
|
-
const reason = selected === null ? unassignableTasks.length > 0 ? `\u56FE\u50CF\u5DE5\u4F5C\u5305 ${unassignableTasks.join("\u3001")} \u6CA1\u6709\u53EF\u7528\u7684\u56FE\u50CF\u6A21\u578B\uFF0C\u65E0\u6CD5\u5F62\u6210\u5B8C\u6574\u5206\u914D\u8BA1\u5212\u3002` : "\u5C1A\u672A\u53D1\u73B0\u53EF\u7528\u6A21\u578B\uFF0C\u4FDD\u7559 Harness \u539F\u59CB\u6A21\u578B\u9009\u62E9\u3002" : `${complexity.band === "simple" ? "\u4F4E\u590D\u6742\u5EA6\u4F18\u5148\u6210\u672C\u3001\u54CD\u5E94\u901F\u5EA6\u4E0E\u8F83\u4F4E\u63A8\u7406\u5F00\u9500" : complexity.band === "balanced" ? "\u5728\u8D28\u91CF\u3001\u6210\u672C\u3001\u63A8\u7406\u7B49\u7EA7\u3001\u5EF6\u8FDF\u4E0E\u98CE\u9669\u4E4B\u95F4\u5E73\u8861" : "\u9AD8\u590D\u6742\u5EA6\u6267\u884C\u5305\u542B\u63A8\u7406\u7B49\u7EA7\u7684\u4F9D\u8D56\u611F\u77E5\
|
|
1323
|
+
const reason = selected === null ? unassignableTasks.length > 0 ? `\u56FE\u50CF\u5DE5\u4F5C\u5305 ${unassignableTasks.join("\u3001")} \u6CA1\u6709\u53EF\u7528\u7684\u56FE\u50CF\u6A21\u578B\uFF0C\u65E0\u6CD5\u5F62\u6210\u5B8C\u6574\u5206\u914D\u8BA1\u5212\u3002` : "\u5C1A\u672A\u53D1\u73B0\u53EF\u7528\u6A21\u578B\uFF0C\u4FDD\u7559 Harness \u539F\u59CB\u6A21\u578B\u9009\u62E9\u3002" : `${complexity.band === "simple" ? "\u4F4E\u590D\u6742\u5EA6\u4F18\u5148\u6210\u672C\u3001\u54CD\u5E94\u901F\u5EA6\u4E0E\u8F83\u4F4E\u63A8\u7406\u5F00\u9500" : complexity.band === "balanced" ? "\u5728\u8D28\u91CF\u3001\u6210\u672C\u3001\u63A8\u7406\u7B49\u7EA7\u3001\u5EF6\u8FDF\u4E0E\u98CE\u9669\u4E4B\u95F4\u5E73\u8861" : "\u9AD8\u590D\u6742\u5EA6\u6267\u884C\u5305\u542B\u63A8\u7406\u7B49\u7EA7\u7684\u4F9D\u8D56\u611F\u77E5\u7EA6\u675F\u5206\u914D"}\uFF1B\u4EFB\u52A1\u7C7B\u578B\u4E3A ${taskType}\uFF0C${String(subtasks.length)} \u4E2A\u5DE5\u4F5C\u5305\u91C7\u7528${optimized?.search?.exact ? "\u4FDD\u7559\u5019\u9009\u7A7A\u95F4\u5185\u7684\u7CBE\u786E\u641C\u7D22" : "\u4FDD\u7559\u5019\u9009\u7A7A\u95F4\u5185\u7684\u6709\u754C\u8FD1\u4F3C\u641C\u7D22"}\u3002\u8D28\u91CF\u5206\u4E0E\u95E8\u69DB\u662F\u672A\u6821\u51C6\u7684\u4F30\u8BA1\uFF0C\u4E0D\u662F\u7B54\u5BF9\u6982\u7387\u3002`;
|
|
1275
1324
|
return {
|
|
1276
1325
|
mode,
|
|
1277
1326
|
preset: presetId,
|
|
@@ -1282,16 +1331,39 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1282
1331
|
taskTypes: [...new Set(taskNodes.map((task) => task.type).filter((type) => type !== "reasoning"))],
|
|
1283
1332
|
objectiveWeights: weights,
|
|
1284
1333
|
candidates: rows.slice(0, 8).map((row) => {
|
|
1285
|
-
const decision = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band], preferredReasoningEffort: complexity.band === "simple" ? "low" : "medium" }, weights,
|
|
1286
|
-
return { provider: row.provider, model: row.model, score: Number(row.score.toFixed(3)), quality: row.qualitySource === "unknown" ? null : Number(row.quality.toFixed(3)), qualitySource: row.qualitySource, specialty: Number(row.specialty.toFixed(3)), reasoningEffort: decision.reasoningEffort, preferredReasoningEffort: decision.preferredReasoningEffort, reasoningFit: Number(decision.reasoningFit.toFixed(3)), reasoningKnown: row.reasoningKnown, reasoningEfforts: row.reasoningEfforts, estimatedCost: row.estimatedCost === null ? null : Number(row.estimatedCost.toFixed(6)), inputPrice: row.pricing?.input ?? null, outputPrice: row.pricing?.output ?? null, pricingSource: row.pricingSource };
|
|
1334
|
+
const decision = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band], preferredReasoningEffort: complexity.band === "simple" ? "low" : "medium" }, weights, cacheReadRatio, cacheWriteRatio, text6, complexity.band);
|
|
1335
|
+
return { provider: row.provider, model: row.model, score: Number(row.score.toFixed(3)), preferenceAdjustment: decision.preferenceAdjustment, quality: row.qualitySource === "unknown" ? null : Number(row.quality.toFixed(3)), qualitySource: row.qualitySource, specialty: Number(row.specialty.toFixed(3)), reasoningEffort: decision.reasoningEffort, preferredReasoningEffort: decision.preferredReasoningEffort, reasoningFit: Number(decision.reasoningFit.toFixed(3)), reasoningKnown: row.reasoningKnown, reasoningEfforts: row.reasoningEfforts, estimatedCost: row.estimatedCost === null ? null : Number(row.estimatedCost.toFixed(6)), inputPrice: row.pricing?.input ?? null, outputPrice: row.pricing?.output ?? null, pricingSource: row.pricingSource, pricingVersion: row.pricingVersion, pricingAsOf: row.snapshotAsOf };
|
|
1287
1336
|
}),
|
|
1288
|
-
selected: selected === null ? null : { provider: selected.provider, model: selected.model, reasoningEffort: selectedAssignment?.decision?.reasoningEffort, estimatedCost: selected.estimatedCost === null ? null : Number(selected.estimatedCost.toFixed(6)), qualitySource: selected.
|
|
1337
|
+
selected: selected === null ? null : { provider: selected.provider, model: selected.model, reasoningEffort: selectedAssignment?.decision?.reasoningEffort, estimatedCost: selected.estimatedCost === null ? null : Number(selected.estimatedCost.toFixed(6)), qualitySource: qualitySourceForTask(selected, selectedAssignment?.task.type ?? taskType), pricingSource: selected.pricingSource },
|
|
1289
1338
|
subtasks,
|
|
1290
1339
|
synthesizer: synthesizer == null ? null : { provider: synthesizer.provider, model: synthesizer.model, reasoningEffort: synthesizerAssignment?.decision?.reasoningEffort },
|
|
1291
|
-
|
|
1340
|
+
// Preserve precision for downstream budget gates; formatting belongs to UI.
|
|
1341
|
+
estimatedCost: totalEstimate,
|
|
1292
1342
|
costBreakdown,
|
|
1293
1343
|
optimization: {
|
|
1294
|
-
|
|
1344
|
+
dataVersions,
|
|
1345
|
+
personalization: learning ? {
|
|
1346
|
+
policyVersion: learning.policyVersion,
|
|
1347
|
+
revision: learning.revision,
|
|
1348
|
+
enabled: learning.enabled,
|
|
1349
|
+
scope: learning.scope,
|
|
1350
|
+
window: learning.window,
|
|
1351
|
+
feedbackCount: learning.feedbackCount,
|
|
1352
|
+
effectiveWeight: learning.effectiveWeight,
|
|
1353
|
+
halfLifeDays: learning.halfLifeDays,
|
|
1354
|
+
priorWeight: learning.priorWeight,
|
|
1355
|
+
maxAdjustment: learning.maxAdjustment,
|
|
1356
|
+
resetAt: learning.resetAt,
|
|
1357
|
+
semantics: "subjective-utility-not-objective-quality"
|
|
1358
|
+
} : null,
|
|
1359
|
+
solver: "dependency-safe quality-constrained exact/beam assignment",
|
|
1360
|
+
qualitySemantics: "uncalibrated-surrogate",
|
|
1361
|
+
costUtility: { method: "1/(1+estimated-task-cost/reference-task-cost)", referencePricing: COST_REFERENCE_PRICING },
|
|
1362
|
+
search: optimized?.search ?? null,
|
|
1363
|
+
optimalityScope: "retained-candidates-and-surrogate-objective",
|
|
1364
|
+
candidateTruncated: (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.truncated, 0),
|
|
1365
|
+
qualityFiltered: (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.filtered, 0),
|
|
1366
|
+
localParetoDisabled: (optimized?.candidatePools ?? []).some((pool) => pool.dependencySensitive),
|
|
1295
1367
|
qualityFloor: QUALITY_FLOORS[complexity.band],
|
|
1296
1368
|
budgetUsd: Number(Number(budgetUsd) > 0 ? Number(budgetUsd) : 0),
|
|
1297
1369
|
cacheReadRatio: normalizedCacheRatios(cacheReadRatio, cacheWriteRatio).read,
|
|
@@ -1308,7 +1380,7 @@ function buildPlan({ text: text6 = "", available = [], mode = "collective", pric
|
|
|
1308
1380
|
beamWidth: ROUTING_BEAM_WIDTH,
|
|
1309
1381
|
budgetFeasible: budget <= 0 ? pricingComplete ? true : null : budgetPlan !== null,
|
|
1310
1382
|
minimumFeasibleCost: minimumFeasibleCost === null ? null : Number(Number(minimumFeasibleCost).toFixed(6)),
|
|
1311
|
-
liveBench: liveBench?.fetchedAt ? { source: liveBench.source ?? "livebench", fetchedAt: liveBench.fetchedAt, models: Object.keys(liveBench.models ?? {}).length, stale: String(liveBenchError).length > 0, error: String(liveBenchError || "") } : { source: "experimental-baseline", fetchedAt: null, models: 0, stale: false, error: String(liveBenchError || "") }
|
|
1383
|
+
liveBench: liveBench?.fetchedAt ?? liveBench?.verifiedAt ? { source: liveBench.source ?? "livebench", fetchedAt: liveBench.fetchedAt ?? liveBench.verifiedAt, verifiedAt: liveBench.verifiedAt ?? liveBench.fetchedAt, publishedAt: liveBench.publishedAt ?? null, version: liveBench.version ?? null, models: Object.keys(liveBench.models ?? {}).length, stale: String(liveBenchError).length > 0, error: String(liveBenchError || "") } : { source: "experimental-baseline", fetchedAt: null, models: 0, stale: false, error: String(liveBenchError || "") }
|
|
1312
1384
|
},
|
|
1313
1385
|
reason,
|
|
1314
1386
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
@@ -1379,6 +1451,9 @@ function createPlanFromRoutes(task, availableRoutes, {
|
|
|
1379
1451
|
runnableToolIds = [],
|
|
1380
1452
|
pricing = {},
|
|
1381
1453
|
liveBench = null,
|
|
1454
|
+
liveBenchError = "",
|
|
1455
|
+
dataVersions = null,
|
|
1456
|
+
learning = null,
|
|
1382
1457
|
cacheReadRatio = 0,
|
|
1383
1458
|
cacheWriteRatio = 0,
|
|
1384
1459
|
directProvider = "",
|
|
@@ -1417,6 +1492,9 @@ function createPlanFromRoutes(task, availableRoutes, {
|
|
|
1417
1492
|
budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0),
|
|
1418
1493
|
pricing,
|
|
1419
1494
|
liveBench,
|
|
1495
|
+
liveBenchError,
|
|
1496
|
+
dataVersions,
|
|
1497
|
+
learning,
|
|
1420
1498
|
cacheReadRatio,
|
|
1421
1499
|
cacheWriteRatio,
|
|
1422
1500
|
preset
|
|
@@ -1433,7 +1511,7 @@ function createPlanFromRoutes(task, availableRoutes, {
|
|
|
1433
1511
|
...selectedChannel ? annotate(selectedChannel) : {},
|
|
1434
1512
|
availabilityNotice: "\u6A21\u578B\u76EE\u5F55\u5217\u51FA\u7684\u8DEF\u7EBF\u5C1A\u672A\u9A8C\u8BC1\u5F53\u524D\u51ED\u636E\u548C\u7F51\u7EDC\uFF1B\u5B9E\u9645\u53EF\u7528\u6027\u4EE5\u5B98\u65B9\u9002\u914D\u5668\u8C03\u7528\u7ED3\u679C\u4E3A\u51C6\u3002",
|
|
1435
1513
|
pricingNotice: plan.estimatedCost === null ? "\u90E8\u5206\u8DEF\u7EBF\u5C1A\u672A\u914D\u7F6E\u8BE5\u4F9B\u5E94\u5546\u7684\u7F8E\u5143\u8F93\u5165/\u8F93\u51FA\u5355\u4EF7\uFF0C\u65E0\u6CD5\u8BA1\u7B97\u53EF\u9760\u7684\u603B\u8D39\u7528\u4E0E\u8282\u7701\u6BD4\u4F8B\uFF1B\u8BF7\u5728\u6A21\u578B\u4EF7\u683C\u8BBE\u7F6E\u4E2D\u8865\u9F50\u3002" : "\u8D39\u7528\u6309\u5DF2\u63D0\u4F9B\u7684\u7F8E\u5143\u5355\u4EF7\u548C\u4F30\u8BA1 token \u6570\u8BA1\u7B97\uFF0C\u4E0D\u662F\u4F9B\u5E94\u5546\u8D26\u5355\uFF0C\u4E5F\u4E0D\u662F\u786C\u6027\u652F\u51FA\u4E0A\u9650\u3002",
|
|
1436
|
-
qualityNotice: plan.optimization.qualityEvidenceComplete ? "\u6A21\u578B\u8D28\u91CF\u4F7F\u7528\u5DF2\u63D0\u4F9B\u8BC4\u5206\u6216\u57FA\u51C6\u6570\u636E\u4F30\u8BA1\uFF0C\
|
|
1514
|
+
qualityNotice: plan.optimization.qualityEvidenceComplete ? "\u6A21\u578B\u8D28\u91CF\u4F7F\u7528\u5DF2\u63D0\u4F9B\u8BC4\u5206\u6216\u57FA\u51C6\u6570\u636E\u4F30\u8BA1\uFF1B\u5206\u6570\u4E0E\u95E8\u69DB\u4E0D\u662F\u7B54\u5BF9\u6982\u7387\uFF0C\u4E5F\u4E0D\u4FDD\u8BC1\u5B9E\u9645\u8D28\u91CF\uFF0C\u4ECD\u9700\u72EC\u7ACB\u4EFB\u52A1\u9A8C\u8BC1\u3002" : "\u90E8\u5206\u6A21\u578B\u8D28\u91CF\u7F3A\u5C11\u53EF\u6838\u9A8C\u8BC4\u5206\uFF1B\u76EE\u5F55\u542F\u53D1\u5F0F\u53EA\u4F9B\u9009\u62E9\u53C2\u8003\uFF0C\u8D28\u91CF\u95E8\u69DB\u548C\u8282\u7701\u6BD4\u4F8B\u65E0\u6CD5\u4FDD\u8BC1\u3002",
|
|
1437
1515
|
modalityNotice: needsImage ? plan.unassignableTasks.length > 0 ? "\u56FE\u50CF\u5DE5\u4F5C\u5305\u6CA1\u6709\u53EF\u786E\u8BA4\u652F\u6301\u56FE\u50CF\u8F93\u5165\u7684\u8DEF\u7EBF\uFF0C\u5F53\u524D\u8BA1\u5212\u65E0\u6CD5\u5B8C\u6574\u5206\u914D\uFF1B\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u914D\u7F6E\u652F\u6301\u56FE\u50CF\u7684\u6A21\u578B\u3002" : routes.some((route) => !Array.isArray(route.inputModalities) || route.inputModalities.length === 0) ? "\u56FE\u50CF\u5DE5\u4F5C\u5305\u53EA\u5206\u7ED9\u5DF2\u58F0\u660E\u56FE\u50CF\u80FD\u529B\u6216\u672A\u58F0\u660E\u8F93\u5165\u80FD\u529B\u7684\u6A21\u578B\uFF1B\u672A\u58F0\u660E\u80FD\u529B\u7684\u6A21\u578B\u4ECD\u9700\u5B9E\u9645\u9A8C\u8BC1\u3002\u5176\u4ED6\u6587\u672C\u5DE5\u4F5C\u5305\u53EF\u7EE7\u7EED\u4F7F\u7528\u7ECF\u6D4E\u578B\u6587\u672C\u6A21\u578B\u3002" : "\u56FE\u50CF\u5DE5\u4F5C\u5305\u53EA\u5206\u7ED9\u660E\u786E\u652F\u6301\u56FE\u50CF\u8F93\u5165\u7684\u6A21\u578B\uFF1B\u5176\u4ED6\u6587\u672C\u5DE5\u4F5C\u5305\u53EF\u7EE7\u7EED\u4F7F\u7528\u7ECF\u6D4E\u578B\u6587\u672C\u6A21\u578B\u3002" : null,
|
|
1438
1516
|
toolNotice: "\u6267\u884C\u6E20\u9053\u6309\u5B98\u65B9\u5DE5\u5177\u6CE8\u518C\u8868\u548C\u5DF2\u6838\u9A8C\u9002\u914D\u5668\u6807\u6CE8\uFF1Aofficial-cli \u8868\u793A\u8BE5\u5382\u5546\u5B98\u65B9 CLI \u5DF2\u5B89\u88C5\u4E14\u53EF\u6258\u7BA1\u6267\u884C\uFF1Bharness-llm \u8868\u793A\u901A\u8FC7\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u8C03\u7528\u3002\u53EF\u5728\u5DE5\u4F5C\u53F0\u67E5\u770B\u5B89\u88C5\u4E0E\u6267\u884C\u652F\u6301\u72B6\u6001\u3002",
|
|
1439
1517
|
team: {
|
|
@@ -1509,32 +1587,101 @@ function applyQualityBiases(routes, biases) {
|
|
|
1509
1587
|
});
|
|
1510
1588
|
}
|
|
1511
1589
|
|
|
1512
|
-
// .dsh-plugin/
|
|
1590
|
+
// .dsh-plugin/shared/adaptive-feedback.mjs
|
|
1591
|
+
var finite2 = (value) => typeof value === "number" && Number.isFinite(value);
|
|
1513
1592
|
var clean3 = (value) => typeof value === "string" ? value.trim() : "";
|
|
1593
|
+
var routeKey3 = (provider, model) => `${provider}\0${model}`;
|
|
1594
|
+
var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
1595
|
+
function applyFeedbackProfile(routes, profile) {
|
|
1596
|
+
if (!Array.isArray(routes) || !record(profile) || typeof profile.enabled !== "boolean") return routes;
|
|
1597
|
+
return routes.map((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;
|
|
1606
|
+
const key = routeKey3(clean3(route.provider), clean3(route.model));
|
|
1607
|
+
if (!Object.hasOwn(profile.adjustments, key)) return base;
|
|
1608
|
+
const value = profile.adjustments[key];
|
|
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;
|
|
1613
|
+
return {
|
|
1614
|
+
...base,
|
|
1615
|
+
preferenceAdjustments: adjustments,
|
|
1616
|
+
preferenceAdjustmentMeta: {
|
|
1617
|
+
policyVersion: profile.policyVersion,
|
|
1618
|
+
revision: profile.revision,
|
|
1619
|
+
scope: profile.scope,
|
|
1620
|
+
feedbackCount: profile.feedbackCount,
|
|
1621
|
+
effectiveWeight: profile.effectiveWeight,
|
|
1622
|
+
updatedAt: profile.updatedAt,
|
|
1623
|
+
halfLifeDays: profile.halfLifeDays,
|
|
1624
|
+
maxAdjustment: profile.maxAdjustment,
|
|
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
|
|
1630
|
+
}
|
|
1631
|
+
};
|
|
1632
|
+
});
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
// .dsh-plugin/shared/dynamic-data-view.mjs
|
|
1636
|
+
var exactRouteKey = (route) => `${String(route?.provider ?? "")}\0${String(route?.model ?? "")}`;
|
|
1637
|
+
function applyPricingSnapshot(routes, snapshot) {
|
|
1638
|
+
if (snapshot?.kind !== "pricing" || !snapshot.prices || typeof snapshot.prices !== "object") return routes;
|
|
1639
|
+
return (Array.isArray(routes) ? routes : []).map((route) => {
|
|
1640
|
+
if (route?.pricingSource === "user") return route;
|
|
1641
|
+
const entry = snapshot.prices[exactRouteKey(route)];
|
|
1642
|
+
if (!entry) return route;
|
|
1643
|
+
return {
|
|
1644
|
+
...route,
|
|
1645
|
+
pricing: {
|
|
1646
|
+
input: entry.input,
|
|
1647
|
+
output: entry.output,
|
|
1648
|
+
currency: "USD",
|
|
1649
|
+
...entry.cacheRead === void 0 ? {} : { cacheRead: entry.cacheRead },
|
|
1650
|
+
...entry.cacheWrite === void 0 ? {} : { cacheWrite: entry.cacheWrite }
|
|
1651
|
+
},
|
|
1652
|
+
pricingSource: "dynamic",
|
|
1653
|
+
pricingVersion: snapshot.version,
|
|
1654
|
+
snapshotAsOf: entry.asOf
|
|
1655
|
+
};
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
|
|
1659
|
+
// .dsh-plugin/client/catalog.mjs
|
|
1660
|
+
var clean4 = (value) => typeof value === "string" ? value.trim() : "";
|
|
1514
1661
|
function routesFromModelCatalog(catalog) {
|
|
1515
1662
|
const groups = Array.isArray(catalog?.groups) ? catalog.groups : [];
|
|
1516
1663
|
const routable = new Set(Array.isArray(catalog?.routableProviders) ? catalog.routableProviders : []);
|
|
1517
1664
|
const routes = [];
|
|
1518
1665
|
const seen = /* @__PURE__ */ new Set();
|
|
1519
1666
|
for (const group of groups) {
|
|
1520
|
-
const provider =
|
|
1667
|
+
const provider = clean4(group?.id);
|
|
1521
1668
|
if (!provider || !routable.has(provider)) continue;
|
|
1522
1669
|
for (const entry of Array.isArray(group.models) ? group.models : []) {
|
|
1523
|
-
const model =
|
|
1670
|
+
const model = clean4(entry?.id);
|
|
1524
1671
|
if (!model) continue;
|
|
1525
1672
|
const key = `${provider}\0${model}`;
|
|
1526
1673
|
if (seen.has(key)) continue;
|
|
1527
1674
|
seen.add(key);
|
|
1528
1675
|
const reasoning = entry?.reasoning;
|
|
1529
|
-
const efforts = Array.isArray(reasoning?.efforts) ? [...new Set(reasoning.efforts.map((item) =>
|
|
1676
|
+
const efforts = Array.isArray(reasoning?.efforts) ? [...new Set(reasoning.efforts.map((item) => clean4(item?.id ?? item)).filter(Boolean))] : [];
|
|
1530
1677
|
routes.push({
|
|
1531
1678
|
provider,
|
|
1532
|
-
providerName:
|
|
1679
|
+
providerName: clean4(group.name) || provider,
|
|
1533
1680
|
model,
|
|
1534
|
-
name:
|
|
1681
|
+
name: clean4(entry.name) || model,
|
|
1535
1682
|
reasoningKnown: reasoning !== void 0 && reasoning !== null,
|
|
1536
1683
|
reasoningEfforts: efforts,
|
|
1537
|
-
...
|
|
1684
|
+
...clean4(reasoning?.defaultEffort) ? { defaultReasoningEffort: clean4(reasoning.defaultEffort) } : {},
|
|
1538
1685
|
// modelCatalog deliberately omits modalities; unknown means the Host
|
|
1539
1686
|
// must still validate image capability before a real request.
|
|
1540
1687
|
inputModalities: []
|
|
@@ -1545,10 +1692,9 @@ function routesFromModelCatalog(catalog) {
|
|
|
1545
1692
|
return routes;
|
|
1546
1693
|
}
|
|
1547
1694
|
function createWorkspacePlan(task, catalog, options = {}) {
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
), options.qualityBiases ?? null);
|
|
1695
|
+
const profiled = applyModelProfiles(routesFromModelCatalog(catalog), parseModelProfilesJson(options.modelProfilesJson ?? "[]"));
|
|
1696
|
+
const priced = applyPricingSnapshot(profiled, options.pricingSnapshot ?? null);
|
|
1697
|
+
const routes = options.learning ? applyFeedbackProfile(priced, options.learning) : applyQualityBiases(priced, options.qualityBiases ?? null);
|
|
1552
1698
|
return createPlanFromRoutes(task, routes, options);
|
|
1553
1699
|
}
|
|
1554
1700
|
|
|
@@ -1575,6 +1721,7 @@ function profileDraft(profile) {
|
|
|
1575
1721
|
cacheWrite: profile?.pricing?.cacheWrite === void 0 ? "" : String(profile.pricing.cacheWrite),
|
|
1576
1722
|
specialties: Array.isArray(profile?.specialties) ? profile.specialties.join(", ") : "",
|
|
1577
1723
|
cliModel: profile?.cliModel ?? "",
|
|
1724
|
+
benchmarkModel: profile?.benchmarkModel ?? "",
|
|
1578
1725
|
execution: profile?.execution === "official" || profile?.execution === "api" ? profile.execution : "auto",
|
|
1579
1726
|
billing: BILLING_MODES.includes(profile?.billing) ? profile.billing : DEFAULT_BILLING_MODE,
|
|
1580
1727
|
subscription: ["plan-key", "cli-login", "none"].includes(profile?.subscription) ? profile.subscription : "auto",
|
|
@@ -1586,6 +1733,8 @@ function profileFromDraft(route, draft) {
|
|
|
1586
1733
|
const model = field(route?.model);
|
|
1587
1734
|
if (!provider || !model) throw new Error("\u8BF7\u5148\u4ECE\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u9009\u62E9\u4E00\u6761\u51C6\u786E\u7684\u6A21\u578B\u8DEF\u7EBF\u3002");
|
|
1588
1735
|
const profile = { provider, model };
|
|
1736
|
+
const benchmarkModel = field(draft?.benchmarkModel);
|
|
1737
|
+
if (benchmarkModel) profile.benchmarkModel = benchmarkModel;
|
|
1589
1738
|
const quality = nonnegativeField(draft?.quality, "\u8D28\u91CF\u8BC4\u5206", 100);
|
|
1590
1739
|
if (quality !== null) profile.quality = quality;
|
|
1591
1740
|
const input = nonnegativeField(draft?.input, "\u8F93\u5165\u5355\u4EF7");
|
|
@@ -1655,7 +1804,7 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
|
1655
1804
|
const [notice, setNotice] = import_react.default.useState(null);
|
|
1656
1805
|
import_react.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
|
|
1657
1806
|
const route = routes.find((item) => profileRouteKey(item) === selected) ?? routes[0];
|
|
1658
|
-
const
|
|
1807
|
+
const routeKey4 = route ? profileRouteKey(route) : "";
|
|
1659
1808
|
const rawJson = snapshot.value?.modelProfilesJson ?? "[]";
|
|
1660
1809
|
let profiles = [];
|
|
1661
1810
|
let loadError = "";
|
|
@@ -1664,16 +1813,16 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
|
1664
1813
|
} catch (error) {
|
|
1665
1814
|
loadError = messageOf(error);
|
|
1666
1815
|
}
|
|
1667
|
-
const saved = profiles.find((item) => profileRouteKey(item) ===
|
|
1668
|
-
const pending = drafts[
|
|
1816
|
+
const saved = profiles.find((item) => profileRouteKey(item) === routeKey4);
|
|
1817
|
+
const pending = drafts[routeKey4];
|
|
1669
1818
|
const draft = pending?.fields ?? profileDraft(saved);
|
|
1670
1819
|
const writable = snapshot.status === "ready" && snapshot.writable === true && !saving && !loadError;
|
|
1671
1820
|
const unmatched = profiles.filter((item) => !routes.some((route2) => profileRouteKey(route2) === profileRouteKey(item))).length;
|
|
1672
1821
|
const edit = (name, value) => {
|
|
1673
1822
|
if (!route || !writable) return;
|
|
1674
1823
|
setDrafts((previous) => {
|
|
1675
|
-
const previousEntry = previous[
|
|
1676
|
-
return { ...previous, [
|
|
1824
|
+
const previousEntry = previous[routeKey4] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
|
|
1825
|
+
return { ...previous, [routeKey4]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
|
|
1677
1826
|
});
|
|
1678
1827
|
setNotice(null);
|
|
1679
1828
|
};
|
|
@@ -1698,7 +1847,7 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
|
1698
1847
|
if (!accepted) throw new Error("\u8BBE\u7F6E\u672A\u88AB\u4FDD\u5B58\uFF0C\u53EF\u80FD\u88AB\u5176\u4ED6\u9875\u9762\u4FEE\u6539\u3002\u8F93\u5165\u5DF2\u4FDD\u7559\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u540E\u6838\u5BF9\u3002");
|
|
1699
1848
|
setDrafts((previous) => {
|
|
1700
1849
|
const next = { ...previous };
|
|
1701
|
-
delete next[
|
|
1850
|
+
delete next[routeKey4];
|
|
1702
1851
|
return next;
|
|
1703
1852
|
});
|
|
1704
1853
|
setNotice({ tone: "success", text: remove ? "\u5DF2\u5220\u9664\u8BE5\u6A21\u578B\u7684\u81EA\u62A5\u914D\u7F6E\u3002" : "\u8BE5\u6A21\u578B\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF1B\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\u5373\u53EF\u4F7F\u7528\u3002" });
|
|
@@ -1712,15 +1861,15 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
|
1712
1861
|
const reload = () => {
|
|
1713
1862
|
setDrafts((previous) => {
|
|
1714
1863
|
const next = { ...previous };
|
|
1715
|
-
delete next[
|
|
1864
|
+
delete next[routeKey4];
|
|
1716
1865
|
return next;
|
|
1717
1866
|
});
|
|
1718
1867
|
setNotice(null);
|
|
1719
1868
|
};
|
|
1720
|
-
return /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card mr-profile-card", "aria-label": "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u9009\u62E9\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E\u8DEF\u7EBF\uFF0C\u586B\u5199\u4F60\u638C\u63E1\u7684\u8D28\u91CF\u8BC4\u5206\u4E0E\u5355\u4EF7\u3002\u914D\u7F6E\u7531\u4F60\u63D0\u4F9B\uFF0C\u63D2\u4EF6\u4E0D\u4F1A\u8BFB\u53D6\u8D26\u53F7\u5BC6\u94A5\u3002"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, loadError && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "alert" }, "\u5DF2\u6709\u914D\u7F6E\u65E0\u6CD5\u89E3\u6790\uFF1A", loadError, "\u3002\u8BF7\u5148\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u4FEE\u6B63 JSON\u3002"), snapshot.status !== "ready" && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u8BBE\u7F6E\u72B6\u6001\uFF1A", snapshot.status === "loading" ? "\u6B63\u5728\u52A0\u8F7D" : "\u5F53\u524D\u4E0D\u53EF\u7528"), snapshot.status === "ready" && !snapshot.writable && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "status" }, "\u5F53\u524D\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\uFF0C\u8BF7\u5728\u53EF\u5199\u7684\u672C\u673A\u73AF\u5883\u914D\u7F6E\u3002"), routes.length === 0 ? /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-empty" }, "\u8BF7\u5148\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\uFF0C\u518D\u8FD4\u56DE\u8FD9\u91CC\u586B\u5199\u4EF7\u683C\u4E0E\u80FD\u529B\u3002") : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-label", htmlFor: "mr-profile-route" }, "\u6A21\u578B\u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", id: "mr-profile-route", value:
|
|
1869
|
+
return /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card mr-profile-card", "aria-label": "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u9009\u62E9\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E\u8DEF\u7EBF\uFF0C\u586B\u5199\u4F60\u638C\u63E1\u7684\u8D28\u91CF\u8BC4\u5206\u4E0E\u5355\u4EF7\u3002\u914D\u7F6E\u7531\u4F60\u63D0\u4F9B\uFF0C\u63D2\u4EF6\u4E0D\u4F1A\u8BFB\u53D6\u8D26\u53F7\u5BC6\u94A5\u3002"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, loadError && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "alert" }, "\u5DF2\u6709\u914D\u7F6E\u65E0\u6CD5\u89E3\u6790\uFF1A", loadError, "\u3002\u8BF7\u5148\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u4FEE\u6B63 JSON\u3002"), snapshot.status !== "ready" && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u8BBE\u7F6E\u72B6\u6001\uFF1A", snapshot.status === "loading" ? "\u6B63\u5728\u52A0\u8F7D" : "\u5F53\u524D\u4E0D\u53EF\u7528"), snapshot.status === "ready" && !snapshot.writable && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "status" }, "\u5F53\u524D\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\uFF0C\u8BF7\u5728\u53EF\u5199\u7684\u672C\u673A\u73AF\u5883\u914D\u7F6E\u3002"), routes.length === 0 ? /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-empty" }, "\u8BF7\u5148\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\uFF0C\u518D\u8FD4\u56DE\u8FD9\u91CC\u586B\u5199\u4EF7\u683C\u4E0E\u80FD\u529B\u3002") : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-label", htmlFor: "mr-profile-route" }, "\u6A21\u578B\u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", id: "mr-profile-route", value: routeKey4, onChange: (event) => {
|
|
1721
1870
|
setSelected(event.target.value);
|
|
1722
1871
|
setNotice(null);
|
|
1723
|
-
} }, routes.map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: profileRouteKey(item), value: profileRouteKey(item) }, item.provider, "/", item.model))), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption mr-profile-state" }, saved ? `\u5DF2\u914D\u7F6E${saved.pricing ? "\u5355\u4EF7" : "\u80FD\u529B\uFF0C\u4EF7\u683C\u672A\u77E5"}` : "\u672A\u914D\u7F6E\uFF0C\u4EF7\u683C\u4E0E\u8D28\u91CF\u6765\u6E90\u672A\u77E5", pending ? " \xB7 \u5F53\u524D\u6709\u672A\u4FDD\u5B58\u8F93\u5165" : "", unmatched > 0 ? ` \xB7 \u53E6\u6709 ${unmatched} \u6761\u914D\u7F6E\u4E0D\u5728\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u4E2D\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u4FDD\u7559` : ""), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8D28\u91CF\u8BC4\u5206\uFF080\u2013100\uFF0C\u81EA\u62A5\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", max: "100", step: "any", value: draft.quality, disabled: !writable, onChange: (event) => edit("quality", event.target.value), placeholder: "\u4F8B\u5982 85\uFF1B\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u5165\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.input, disabled: !writable, onChange: (event) => edit("input", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u51FA\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.output, disabled: !writable, onChange: (event) => edit("output", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u64C5\u957F\u65B9\u5411\uFF08\u82F1\u6587\u6807\u7B7E\uFF0C\u9017\u53F7\u5206\u9694\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.specialties, disabled: !writable, onChange: (event) => edit("specialties", event.target.value), placeholder: PROFILE_SPECIALTY_HINT })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u5B98\u65B9 CLI \u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.cliModel, disabled: !writable, onChange: (event) => edit("cliModel", event.target.value), placeholder: "\u4EC5\u5728\u5382\u5546 CLI \u652F\u6301\u8BE5\u51C6\u786E\u540D\u79F0\u65F6\u586B\u5199" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u6267\u884C\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.execution || "auto", disabled: !writable, onChange: (event) => edit("execution", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u5DF2\u5B89\u88C5\u5219\u7528\u5B98\u65B9\u5DE5\u5177\uFF0C\u5931\u8D25\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "official" }, "\u5B98\u65B9\u5DE5\u5177\uFF1A\u5931\u8D25\u6216\u672A\u5B89\u88C5\u65F6\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api" }, "\u4EC5\u6A21\u578B\u76EE\u5F55 API"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA1\u8D39\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.billing, disabled: !writable, onChange: (event) => edit("billing", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-first" }, "\u8BA2\u9605\u4F18\u5148\uFF1A\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u65F6\u5207\u6362 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api-only" }, "\u53EA\u7528 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-only" }, "\u53EA\u7528\u8BA2\u9605\uFF1A\u989D\u5EA6\u7528\u5C3D\u65F6\u4E0D\u5207\u6362"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA2\u9605\u6765\u6E90"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.subscription, disabled: !writable, onChange: (event) => edit("subscription", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u6709\u5B98\u65B9 CLI \u65F6\u7528 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "plan-key" }, "\u7F16\u7A0B\u5957\u9910 Key\uFF1A\u8BE5\u8DEF\u7EBF\u672C\u8EAB\u662F\u5957\u9910\u7AEF\u70B9"), /* @__PURE__ */ import_react.default.createElement("option", { value: "cli-login" }, "\u5B98\u65B9 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "none" }, "\u65E0\u8BA2\u9605\uFF1A\u59CB\u7EC8\u6309 API \u8BA1\u8D39"))), draft.subscription === "plan-key" && /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u989D\u5EA6\u7528\u5C3D\u65F6\u56DE\u9000\u7684 API \u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.apiRoute, disabled: !writable, onChange: (event) => edit("apiRoute", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "" }, "\u4E0D\u56DE\u9000\uFF08\u672A\u914D\u7F6E API \u8DEF\u7EBF\uFF09"), routes.filter((item) => profileRouteKey(item) !==
|
|
1872
|
+
} }, routes.map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: profileRouteKey(item), value: profileRouteKey(item) }, item.provider, "/", item.model))), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption mr-profile-state" }, saved ? `\u5DF2\u914D\u7F6E${saved.pricing ? "\u5355\u4EF7" : "\u80FD\u529B\uFF0C\u4EF7\u683C\u672A\u77E5"}` : "\u672A\u914D\u7F6E\uFF0C\u4EF7\u683C\u4E0E\u8D28\u91CF\u6765\u6E90\u672A\u77E5", pending ? " \xB7 \u5F53\u524D\u6709\u672A\u4FDD\u5B58\u8F93\u5165" : "", unmatched > 0 ? ` \xB7 \u53E6\u6709 ${unmatched} \u6761\u914D\u7F6E\u4E0D\u5728\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u4E2D\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u4FDD\u7559` : ""), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8D28\u91CF\u8BC4\u5206\uFF080\u2013100\uFF0C\u81EA\u62A5\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", max: "100", step: "any", value: draft.quality, disabled: !writable, onChange: (event) => edit("quality", event.target.value), placeholder: "\u4F8B\u5982 85\uFF1B\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u5165\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.input, disabled: !writable, onChange: (event) => edit("input", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u51FA\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.output, disabled: !writable, onChange: (event) => edit("output", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u64C5\u957F\u65B9\u5411\uFF08\u82F1\u6587\u6807\u7B7E\uFF0C\u9017\u53F7\u5206\u9694\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.specialties, disabled: !writable, onChange: (event) => edit("specialties", event.target.value), placeholder: PROFILE_SPECIALTY_HINT })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u5B98\u65B9 CLI \u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.cliModel, disabled: !writable, onChange: (event) => edit("cliModel", event.target.value), placeholder: "\u4EC5\u5728\u5382\u5546 CLI \u652F\u6301\u8BE5\u51C6\u786E\u540D\u79F0\u65F6\u586B\u5199" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "LiveBench \u51C6\u786E\u6A21\u578B\u540D\u79F0\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.benchmarkModel ?? "", disabled: !writable, onChange: (event) => edit("benchmarkModel", event.target.value), placeholder: "\u786E\u8BA4\u7248\u672C\u4E0E\u63A8\u7406\u6863\u4F4D\u4E00\u81F4\uFF1B\u4E0D\u81EA\u52A8\u731C\u6D4B\u522B\u540D" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u6267\u884C\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.execution || "auto", disabled: !writable, onChange: (event) => edit("execution", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u5DF2\u5B89\u88C5\u5219\u7528\u5B98\u65B9\u5DE5\u5177\uFF0C\u5931\u8D25\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "official" }, "\u5B98\u65B9\u5DE5\u5177\uFF1A\u5931\u8D25\u6216\u672A\u5B89\u88C5\u65F6\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api" }, "\u4EC5\u6A21\u578B\u76EE\u5F55 API"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA1\u8D39\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.billing, disabled: !writable, onChange: (event) => edit("billing", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-first" }, "\u8BA2\u9605\u4F18\u5148\uFF1A\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u65F6\u5207\u6362 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api-only" }, "\u53EA\u7528 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-only" }, "\u53EA\u7528\u8BA2\u9605\uFF1A\u989D\u5EA6\u7528\u5C3D\u65F6\u4E0D\u5207\u6362"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA2\u9605\u6765\u6E90"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.subscription, disabled: !writable, onChange: (event) => edit("subscription", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u6709\u5B98\u65B9 CLI \u65F6\u7528 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "plan-key" }, "\u7F16\u7A0B\u5957\u9910 Key\uFF1A\u8BE5\u8DEF\u7EBF\u672C\u8EAB\u662F\u5957\u9910\u7AEF\u70B9"), /* @__PURE__ */ import_react.default.createElement("option", { value: "cli-login" }, "\u5B98\u65B9 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "none" }, "\u65E0\u8BA2\u9605\uFF1A\u59CB\u7EC8\u6309 API \u8BA1\u8D39"))), draft.subscription === "plan-key" && /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u989D\u5EA6\u7528\u5C3D\u65F6\u56DE\u9000\u7684 API \u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.apiRoute, disabled: !writable, onChange: (event) => edit("apiRoute", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "" }, "\u4E0D\u56DE\u9000\uFF08\u672A\u914D\u7F6E API \u8DEF\u7EBF\uFF09"), routes.filter((item) => profileRouteKey(item) !== routeKey4).map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: profileRouteKey(item), value: profileRouteKey(item) }, item.provider, "/", item.model))))), /* @__PURE__ */ import_react.default.createElement("details", { className: "mr-profile-advanced" }, /* @__PURE__ */ import_react.default.createElement("summary", null, "\u7F13\u5B58\u5355\u4EF7\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u7F13\u5B58\u8BFB\u53D6\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.cacheRead, disabled: !writable, onChange: (event) => edit("cacheRead", event.target.value), placeholder: "\u7559\u7A7A\u6309\u666E\u901A\u8F93\u5165\u4EF7\u683C\u4F30\u7B97" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u7F13\u5B58\u5199\u5165\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.cacheWrite, disabled: !writable, onChange: (event) => edit("cacheWrite", event.target.value), placeholder: "\u7559\u7A7A\u6309\u666E\u901A\u8F93\u5165\u4EF7\u683C\u4F30\u7B97" })))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button", type: "button", disabled: !writable || !pending, onClick: () => {
|
|
1724
1873
|
void write(false);
|
|
1725
1874
|
} }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u6B64\u6A21\u578B"), /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !pending || saving, onClick: reload }, "\u91CD\u65B0\u52A0\u8F7D\u6B64\u8DEF\u7EBF"), saved && /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable, onClick: () => {
|
|
1726
1875
|
void write(true);
|
|
@@ -1978,6 +2127,147 @@ function rerunConfirmations({ run, item, override = null, choice = null, ledger
|
|
|
1978
2127
|
return reasons;
|
|
1979
2128
|
}
|
|
1980
2129
|
|
|
2130
|
+
// .dsh-plugin/client/adaptive-state.mjs
|
|
2131
|
+
var ADAPTIVE_DEFAULTS = Object.freeze({
|
|
2132
|
+
feedbackLearningEnabled: true,
|
|
2133
|
+
feedbackHalfLifeDays: 30,
|
|
2134
|
+
feedbackPriorWeight: 3,
|
|
2135
|
+
feedbackMaxAdjustment: 0.04,
|
|
2136
|
+
feedbackResetAt: 0,
|
|
2137
|
+
dynamicDataEnabled: false,
|
|
2138
|
+
liveBenchEndpoint: "https://livebench.ai",
|
|
2139
|
+
dynamicDataTtlMinutes: 1440,
|
|
2140
|
+
pricingSnapshotEndpoint: ""
|
|
2141
|
+
});
|
|
2142
|
+
var ADAPTIVE_NUMBER_FIELDS = Object.freeze([
|
|
2143
|
+
Object.freeze({ key: "feedbackHalfLifeDays", min: 1, max: 3650, step: 1 }),
|
|
2144
|
+
Object.freeze({ key: "feedbackPriorWeight", min: 1, max: 1e6, step: 1 }),
|
|
2145
|
+
Object.freeze({ key: "feedbackMaxAdjustment", min: 0, max: 0.1, step: 0.01 }),
|
|
2146
|
+
Object.freeze({ key: "dynamicDataTtlMinutes", min: 1, max: 10080, step: 1 })
|
|
2147
|
+
]);
|
|
2148
|
+
function adaptiveSettingValue(settings, key) {
|
|
2149
|
+
return settings?.[key] ?? ADAPTIVE_DEFAULTS[key];
|
|
2150
|
+
}
|
|
2151
|
+
function parseAdaptiveNumber(key, draft) {
|
|
2152
|
+
const field2 = ADAPTIVE_NUMBER_FIELDS.find((item) => item.key === key);
|
|
2153
|
+
if (!field2) throw new TypeError("Unknown numeric setting");
|
|
2154
|
+
if (typeof draft !== "string" && typeof draft !== "number" || String(draft).trim() === "") throw new TypeError("Enter a number");
|
|
2155
|
+
const value = Number(draft);
|
|
2156
|
+
if (!Number.isFinite(value) || value < field2.min || value > field2.max) throw new RangeError(`Expected ${field2.min}..${field2.max}`);
|
|
2157
|
+
return value;
|
|
2158
|
+
}
|
|
2159
|
+
function parsePublicEndpoint(key, draft) {
|
|
2160
|
+
if (!["liveBenchEndpoint", "pricingSnapshotEndpoint"].includes(key)) throw new TypeError("Unknown endpoint setting");
|
|
2161
|
+
if (typeof draft !== "string") throw new TypeError("Enter a public HTTPS URL");
|
|
2162
|
+
const value = draft.trim();
|
|
2163
|
+
if (key === "pricingSnapshotEndpoint" && value === "") return "";
|
|
2164
|
+
if (!value || value.length > 2048) throw new TypeError("Enter a public HTTPS URL");
|
|
2165
|
+
let parsed;
|
|
2166
|
+
try {
|
|
2167
|
+
parsed = new URL(value);
|
|
2168
|
+
} catch {
|
|
2169
|
+
throw new TypeError("Enter a public HTTPS URL");
|
|
2170
|
+
}
|
|
2171
|
+
if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.search || parsed.hash || parsed.port && parsed.port !== "443") {
|
|
2172
|
+
throw new TypeError("Use a public HTTPS URL without credentials, query or fragment");
|
|
2173
|
+
}
|
|
2174
|
+
if (!/^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$/iu.test(parsed.hostname) || /(?:^|\.)(?:localhost|local|internal|lan|home|test|invalid)$/iu.test(parsed.hostname)) throw new TypeError("Use a public hostname");
|
|
2175
|
+
return value;
|
|
2176
|
+
}
|
|
2177
|
+
function publicEndpointDraft(key, value) {
|
|
2178
|
+
try {
|
|
2179
|
+
return parsePublicEndpoint(key, value);
|
|
2180
|
+
} catch {
|
|
2181
|
+
return "";
|
|
2182
|
+
}
|
|
2183
|
+
}
|
|
2184
|
+
function feedbackRatingArguments(run, item, direction) {
|
|
2185
|
+
if (!["up", "down"].includes(direction)) throw new TypeError("Unknown rating direction");
|
|
2186
|
+
const active = direction === "up" ? item?.rating === 1 : item?.rating === -1;
|
|
2187
|
+
return [run?.id, item?.id, active ? "clear" : direction, item?.finishedAt];
|
|
2188
|
+
}
|
|
2189
|
+
var nonnegative2 = (value) => Number.isFinite(value) && value >= 0 ? value : 0;
|
|
2190
|
+
function learningView(learning, settings = {}) {
|
|
2191
|
+
return {
|
|
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,
|
|
2196
|
+
feedbackCount: nonnegative2(learning?.feedbackCount),
|
|
2197
|
+
ignoredCount: nonnegative2(learning?.ignoredCount),
|
|
2198
|
+
effectiveWeight: nonnegative2(learning?.effectiveWeight),
|
|
2199
|
+
policyVersion: typeof learning?.policyVersion === "string" ? learning.policyVersion.slice(0, 100) : null,
|
|
2200
|
+
resetAt: nonnegative2(adaptiveSettingValue(settings, "feedbackResetAt"))
|
|
2201
|
+
};
|
|
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
|
+
}
|
|
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" });
|
|
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";
|
|
2252
|
+
let publicSource = "";
|
|
2253
|
+
if (enabled !== false && typeof source?.source === "string") publicSource = publicEndpointDraft("pricingSnapshotEndpoint", source.source);
|
|
2254
|
+
return {
|
|
2255
|
+
status,
|
|
2256
|
+
label: STATUS_LABEL[status],
|
|
2257
|
+
source: publicSource,
|
|
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 ?? "")
|
|
2268
|
+
};
|
|
2269
|
+
}
|
|
2270
|
+
|
|
1981
2271
|
// .dsh-plugin/client/router-insights.jsx
|
|
1982
2272
|
var text = (value) => typeof value === "string" ? value.trim() : "";
|
|
1983
2273
|
var BAND = { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u56F0\u96BE" };
|
|
@@ -2012,23 +2302,195 @@ function Meter({ label, meter }) {
|
|
|
2012
2302
|
function useSettingsWriter(settingsScope) {
|
|
2013
2303
|
const [snapshot, setSnapshot] = import_react2.default.useState(() => settingsScope.getSnapshot());
|
|
2014
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
|
+
}, []);
|
|
2015
2314
|
import_react2.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
|
|
2016
|
-
const writable = snapshot.status === "ready" && snapshot.writable === true;
|
|
2315
|
+
const writable = snapshot.status === "ready" && snapshot.writable === true && !busy;
|
|
2017
2316
|
const write = async (key, value) => {
|
|
2018
|
-
|
|
2317
|
+
const current = settingsScope.getSnapshot();
|
|
2318
|
+
if (pending.current || current.status !== "ready" || current.writable !== true) return false;
|
|
2319
|
+
pending.current = true;
|
|
2320
|
+
setBusy(true);
|
|
2019
2321
|
setNotice("");
|
|
2020
2322
|
try {
|
|
2021
|
-
const accepted = await settingsScope.mutate([{ op: "set", path: [key], value }],
|
|
2022
|
-
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");
|
|
2023
2325
|
return accepted;
|
|
2024
2326
|
} catch (error) {
|
|
2025
|
-
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");
|
|
2026
2328
|
return false;
|
|
2329
|
+
} finally {
|
|
2330
|
+
pending.current = false;
|
|
2331
|
+
if (mounted.current) setBusy(false);
|
|
2027
2332
|
}
|
|
2028
2333
|
};
|
|
2029
|
-
return { value: snapshot.value ?? {}, writable, write, notice };
|
|
2334
|
+
return { value: snapshot.value ?? {}, writable, busy, write, notice };
|
|
2030
2335
|
}
|
|
2031
|
-
|
|
2336
|
+
var ADAPTIVE_FIELD_LABEL = {
|
|
2337
|
+
feedbackHalfLifeDays: "\u53CD\u9988\u534A\u8870\u671F\uFF08\u5929\uFF09",
|
|
2338
|
+
feedbackPriorWeight: "\u6536\u7F29\u5F3A\u5EA6",
|
|
2339
|
+
feedbackMaxAdjustment: "\u6700\u5927\u504F\u597D\u6548\u7528\u8C03\u6574",
|
|
2340
|
+
dynamicDataTtlMinutes: "\u516C\u5171\u5FEB\u7167\u68C0\u67E5\u95F4\u9694\uFF08\u5206\u949F\uFF09"
|
|
2341
|
+
};
|
|
2342
|
+
function AdaptiveNumberControl({ field: field2, value, writable, onSave }) {
|
|
2343
|
+
const [draft, setDraft] = import_react2.default.useState("");
|
|
2344
|
+
const [notice, setNotice] = import_react2.default.useState("");
|
|
2345
|
+
const save = async () => {
|
|
2346
|
+
let next;
|
|
2347
|
+
try {
|
|
2348
|
+
next = parseAdaptiveNumber(field2.key, draft);
|
|
2349
|
+
} catch {
|
|
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");
|
|
2358
|
+
}
|
|
2359
|
+
};
|
|
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(
|
|
2361
|
+
"input",
|
|
2362
|
+
{
|
|
2363
|
+
className: "mr-input mr-budget",
|
|
2364
|
+
id: `mr-${field2.key}`,
|
|
2365
|
+
type: "number",
|
|
2366
|
+
min: field2.min,
|
|
2367
|
+
max: field2.max,
|
|
2368
|
+
step: field2.step,
|
|
2369
|
+
placeholder: String(value),
|
|
2370
|
+
value: draft,
|
|
2371
|
+
disabled: !writable,
|
|
2372
|
+
onChange: (event) => setDraft(event.target.value)
|
|
2373
|
+
}
|
|
2374
|
+
), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable || draft === "", onClick: () => {
|
|
2375
|
+
void save();
|
|
2376
|
+
} }, "\u4FDD\u5B58")), notice && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-error", role: "alert" }, notice));
|
|
2377
|
+
}
|
|
2378
|
+
function PublicEndpointControl({ name, label, value, writable, onSave }) {
|
|
2379
|
+
const [draft, setDraft] = import_react2.default.useState(null);
|
|
2380
|
+
const [notice, setNotice] = import_react2.default.useState("");
|
|
2381
|
+
const current = publicEndpointDraft(name, value);
|
|
2382
|
+
const save = async () => {
|
|
2383
|
+
let next;
|
|
2384
|
+
try {
|
|
2385
|
+
next = parsePublicEndpoint(name, draft ?? current);
|
|
2386
|
+
} catch {
|
|
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");
|
|
2395
|
+
}
|
|
2396
|
+
};
|
|
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(
|
|
2398
|
+
"input",
|
|
2399
|
+
{
|
|
2400
|
+
className: "mr-input",
|
|
2401
|
+
id: `mr-${name}`,
|
|
2402
|
+
type: "url",
|
|
2403
|
+
maxLength: 2048,
|
|
2404
|
+
value: draft ?? current,
|
|
2405
|
+
disabled: !writable,
|
|
2406
|
+
placeholder: name === "liveBenchEndpoint" ? "https://livebench.ai" : "\u7559\u7A7A\uFF1A\u4E0D\u8BFB\u53D6\u8FDC\u7A0B\u4EF7\u683C\u5FEB\u7167",
|
|
2407
|
+
onChange: (event) => setDraft(event.target.value)
|
|
2408
|
+
}
|
|
2409
|
+
), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable || draft === null, onClick: () => {
|
|
2410
|
+
void save();
|
|
2411
|
+
} }, "\u4FDD\u5B58\u5730\u5740"), notice && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-error", role: "alert" }, notice));
|
|
2412
|
+
}
|
|
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)))));
|
|
2421
|
+
}
|
|
2422
|
+
function AdaptiveControls({ ledger, settings, onChanged, refreshing }) {
|
|
2423
|
+
const value = settings.value;
|
|
2424
|
+
const learning = learningView(ledger?.learning, value);
|
|
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);
|
|
2428
|
+
const save = async (key, next) => {
|
|
2429
|
+
const accepted = await settings.write(key, next);
|
|
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
|
+
}
|
|
2438
|
+
return accepted;
|
|
2439
|
+
};
|
|
2440
|
+
const reset = async () => {
|
|
2441
|
+
if (resetPending.current || !settings.writable) return;
|
|
2442
|
+
resetPending.current = true;
|
|
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);
|
|
2446
|
+
await save("feedbackResetAt", Date.now());
|
|
2447
|
+
} finally {
|
|
2448
|
+
resetPending.current = false;
|
|
2449
|
+
setResetBusy(false);
|
|
2450
|
+
}
|
|
2451
|
+
};
|
|
2452
|
+
return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("h3", { className: "mr-section-title" }, "\u6301\u7EED\u53CD\u9988\u4E0E\u672C\u5730\u504F\u597D"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u53EA\u4F7F\u7528\u4F60\u5BF9\u5DF2\u8BB0\u5F55\u7ED3\u679C\u7684\u70B9\u8D5E / \u4E0D\u597D / \u64A4\u56DE\uFF0C\u6309\u4EFB\u52A1\u7C7B\u578B\u548C\u65F6\u95F4\u8870\u51CF\u8C03\u6574\u504F\u597D\u6548\u7528\uFF1B\u4E0D\u62AC\u9AD8\u8D28\u91CF\u786C\u95E8\u69DB\u3002\u7CFB\u7EDF\u6267\u884C\u6210\u529F\u4E0D\u7B49\u4E8E\u4F60\u6EE1\u610F\uFF0C\u6A21\u578B\u590D\u6838\u4E5F\u4E0D\u4F5C\u4E3A\u4EBA\u5DE5\u8BC4\u5206\u3002\u4E0D\u4F1A\u4E3A\u4E86\u5B66\u4E60\u53D1\u8D77\u4ED8\u8D39\u63A2\u7D22\u3002"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u8303\u56F4\uFF1A\u672C\u5730 DSH_HOME \u5DE5\u4F5C\u5BA4\u5171\u4EAB\uFF0C\u4E0D\u662F\u6309\u8D26\u53F7\u9694\u79BB\uFF1B\u6700\u591A\u6700\u8FD1 200 \u6B21\u8FD0\u884C\u7684\u6ED1\u52A8\u7A97\u53E3\uFF0C\u4E0D\u4E0A\u4F20\u53CD\u9988\u6216\u4EFB\u52A1\u5185\u5BB9\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-check" }, /* @__PURE__ */ import_react2.default.createElement(
|
|
2453
|
+
"input",
|
|
2454
|
+
{
|
|
2455
|
+
type: "checkbox",
|
|
2456
|
+
checked: adaptiveSettingValue(value, "feedbackLearningEnabled") !== false,
|
|
2457
|
+
disabled: !settings.writable,
|
|
2458
|
+
onChange: (event) => {
|
|
2459
|
+
void save("feedbackLearningEnabled", event.target.checked);
|
|
2460
|
+
}
|
|
2461
|
+
}
|
|
2462
|
+
), "\u542F\u7528\u7528\u6237\u53CD\u9988\u5B66\u4E60"), ADAPTIVE_NUMBER_FIELDS.filter((field2) => field2.key !== "dynamicDataTtlMinutes").map((field2) => /* @__PURE__ */ import_react2.default.createElement(
|
|
2463
|
+
AdaptiveNumberControl,
|
|
2464
|
+
{
|
|
2465
|
+
key: field2.key,
|
|
2466
|
+
field: field2,
|
|
2467
|
+
value: adaptiveSettingValue(value, field2.key),
|
|
2468
|
+
writable: settings.writable,
|
|
2469
|
+
onSave: save
|
|
2470
|
+
}
|
|
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: () => {
|
|
2472
|
+
void reset();
|
|
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(
|
|
2474
|
+
"input",
|
|
2475
|
+
{
|
|
2476
|
+
type: "checkbox",
|
|
2477
|
+
checked: adaptiveSettingValue(value, "dynamicDataEnabled") === true,
|
|
2478
|
+
disabled: !settings.writable,
|
|
2479
|
+
onChange: (event) => {
|
|
2480
|
+
void save("dynamicDataEnabled", event.target.checked);
|
|
2481
|
+
}
|
|
2482
|
+
}
|
|
2483
|
+
), "\u542F\u7528\u52A8\u6001\u516C\u5171\u6570\u636E"), /* @__PURE__ */ import_react2.default.createElement(
|
|
2484
|
+
AdaptiveNumberControl,
|
|
2485
|
+
{
|
|
2486
|
+
field: ADAPTIVE_NUMBER_FIELDS.find((field2) => field2.key === "dynamicDataTtlMinutes"),
|
|
2487
|
+
value: adaptiveSettingValue(value, "dynamicDataTtlMinutes"),
|
|
2488
|
+
writable: settings.writable,
|
|
2489
|
+
onSave: save
|
|
2490
|
+
}
|
|
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")) })));
|
|
2492
|
+
}
|
|
2493
|
+
function CostControlCard({ ledger, settingsScope, onChanged, error, refreshing = false }) {
|
|
2032
2494
|
const settings = useSettingsWriter(settingsScope);
|
|
2033
2495
|
const [dailyDraft, setDailyDraft] = import_react2.default.useState("");
|
|
2034
2496
|
const [monthlyDraft, setMonthlyDraft] = import_react2.default.useState("");
|
|
@@ -2066,7 +2528,7 @@ function CostControlCard({ ledger, settingsScope, onChanged, error }) {
|
|
|
2066
2528
|
void set("allowManualReassign", event.target.checked);
|
|
2067
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) => {
|
|
2068
2530
|
void set("confirmUnsandboxedCli", event.target.checked);
|
|
2069
|
-
} }), "\u76F4\u63A5\u542F\u52A8\u65E0\u6C99\u7BB1 CLI \u524D\u5148\u786E\u8BA4")), 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)));
|
|
2070
2532
|
}
|
|
2071
2533
|
function DagView({ packages, renderNode, label = "\u5B50\u4EFB\u52A1\u4F9D\u8D56\u56FE" }) {
|
|
2072
2534
|
const layers = dagLayers(packages);
|
|
@@ -2082,13 +2544,13 @@ function ChannelText({ item }) {
|
|
|
2082
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");
|
|
2083
2545
|
}
|
|
2084
2546
|
function RunNode({ run, item, routes, allowReassign, busy, onRate, onRerun }) {
|
|
2085
|
-
if (item.status === "paused") return /* @__PURE__ */ import_react2.default.createElement(PausedNode, { run, item, busy, onRerun });
|
|
2086
2547
|
const [target, setTarget] = import_react2.default.useState("");
|
|
2548
|
+
if (item.status === "paused") return /* @__PURE__ */ import_react2.default.createElement(PausedNode, { run, item, busy, onRerun });
|
|
2087
2549
|
const cost = packageCost(item);
|
|
2088
2550
|
const rerun = rerunSupport(run);
|
|
2089
2551
|
const canRerun = rerun.supported && !item.ok && !busy;
|
|
2090
2552
|
const others = routes.filter((route) => `${route.provider}/${route.model}` !== `${item.provider}/${item.model}`);
|
|
2091
|
-
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(run
|
|
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: () => {
|
|
2092
2554
|
const [provider, model] = target.split("\0");
|
|
2093
2555
|
onRerun(run.id, item.id, { provider, model });
|
|
2094
2556
|
} }, "\u6539\u6D3E\u5E76\u91CD\u8DD1"))));
|
|
@@ -2173,26 +2635,26 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
|
|
|
2173
2635
|
if (blocked || busy) return;
|
|
2174
2636
|
const current = ++pendingRequest.current;
|
|
2175
2637
|
const snapshot = request();
|
|
2176
|
-
const
|
|
2638
|
+
const signature2 = inputSignature;
|
|
2177
2639
|
setPhase({ kind: "previewing" });
|
|
2178
2640
|
try {
|
|
2179
2641
|
const value = unwrapRemote(await previewRun(snapshot), "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002");
|
|
2180
|
-
if (mounted.current && current === pendingRequest.current &&
|
|
2642
|
+
if (mounted.current && current === pendingRequest.current && signature2 === latestSignature.current) setPhase({ kind: "preview", value, request: snapshot, signature: signature2 });
|
|
2181
2643
|
} catch (error) {
|
|
2182
|
-
if (mounted.current && current === pendingRequest.current &&
|
|
2644
|
+
if (mounted.current && current === pendingRequest.current && signature2 === latestSignature.current) setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002" });
|
|
2183
2645
|
}
|
|
2184
2646
|
};
|
|
2185
2647
|
const confirm2 = async () => {
|
|
2186
2648
|
if (phase.kind !== "preview" || phase.signature !== latestSignature.current) return;
|
|
2187
2649
|
const shown = phase.value;
|
|
2188
2650
|
const snapshot = phase.request;
|
|
2189
|
-
const
|
|
2190
|
-
setPhase({ kind: "running", value: shown, request: snapshot, signature });
|
|
2651
|
+
const signature2 = phase.signature;
|
|
2652
|
+
setPhase({ kind: "running", value: shown, request: snapshot, signature: signature2 });
|
|
2191
2653
|
try {
|
|
2192
2654
|
const value = unwrapRemote(await startRun({ ...snapshot, confirmedReasons: (shown.reasons ?? []).map((item) => item.code) }), "\u6267\u884C\u5931\u8D25\u3002");
|
|
2193
2655
|
if (!mounted.current) return;
|
|
2194
2656
|
if (value.status === "needs-confirmation") {
|
|
2195
|
-
setPhase(
|
|
2657
|
+
setPhase(signature2 === latestSignature.current ? { kind: "preview", value, request: snapshot, signature: signature2, changed: true } : { kind: "idle" });
|
|
2196
2658
|
return;
|
|
2197
2659
|
}
|
|
2198
2660
|
setPhase({ kind: "done", value });
|
|
@@ -11653,7 +12115,7 @@ var xterm_default = `/**
|
|
|
11653
12115
|
`;
|
|
11654
12116
|
|
|
11655
12117
|
// .dsh-plugin/client/host-version.mjs
|
|
11656
|
-
var ROUTER_CLIENT_VERSION = true ? "0.
|
|
12118
|
+
var ROUTER_CLIENT_VERSION = true ? "0.16.1" : "";
|
|
11657
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";
|
|
11658
12120
|
function isMissingRemoteMethod(message) {
|
|
11659
12121
|
const value = String(message ?? "");
|
|
@@ -12431,6 +12893,11 @@ var router_main_default = `.mr-workspace {
|
|
|
12431
12893
|
// .dsh-plugin/client/router-main.jsx
|
|
12432
12894
|
var money2 = (value) => value === null || value === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : `$${Number(value).toFixed(4)}`;
|
|
12433
12895
|
var text5 = (value) => typeof value === "string" ? value.trim() : "";
|
|
12896
|
+
var routingSettingsSignature = (value) => JSON.stringify([
|
|
12897
|
+
value.modelProfilesJson,
|
|
12898
|
+
value.routingPreset,
|
|
12899
|
+
...Object.keys(ADAPTIVE_DEFAULTS).map((key) => value[key] ?? ADAPTIVE_DEFAULTS[key])
|
|
12900
|
+
]);
|
|
12434
12901
|
function RouterPanelIcon({ size = 20, active = false }) {
|
|
12435
12902
|
return /* @__PURE__ */ import_react5.default.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ import_react5.default.createElement("path", { d: "M7 6.5h7M7 17.5h7M15 6.5v11", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }), /* @__PURE__ */ import_react5.default.createElement("circle", { cx: "5", cy: "6.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react5.default.createElement("circle", { cx: "5", cy: "17.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react5.default.createElement("circle", { cx: "17", cy: "12", r: "3", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.7" }));
|
|
12436
12903
|
}
|
|
@@ -12607,12 +13074,18 @@ var WORKSPACE_VIEWS = [
|
|
|
12607
13074
|
];
|
|
12608
13075
|
function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries }) {
|
|
12609
13076
|
const [health, setHealth] = import_react5.default.useState({ report: null, error: "", refreshing: false });
|
|
12610
|
-
const [ledger, setLedger] = import_react5.default.useState({ value: null, error: "" });
|
|
13077
|
+
const [ledger, setLedger] = import_react5.default.useState({ value: null, error: "", refreshing: false });
|
|
12611
13078
|
const [boundaries, setBoundaries] = import_react5.default.useState({ value: null, error: "" });
|
|
12612
13079
|
const [busy, setBusy] = import_react5.default.useState(false);
|
|
12613
13080
|
const mounted = import_react5.default.useRef(true);
|
|
12614
|
-
import_react5.default.
|
|
12615
|
-
|
|
13081
|
+
const ledgerRequest = import_react5.default.useRef(0);
|
|
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
|
+
};
|
|
12616
13089
|
}, []);
|
|
12617
13090
|
const call = async (operation, fallback) => {
|
|
12618
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");
|
|
@@ -12628,11 +13101,20 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
12628
13101
|
}
|
|
12629
13102
|
};
|
|
12630
13103
|
const refreshLedger = async () => {
|
|
13104
|
+
const request = ++ledgerRequest.current;
|
|
13105
|
+
if (mounted.current) setLedger((previous) => ({ ...previous, error: "", refreshing: true }));
|
|
12631
13106
|
try {
|
|
12632
13107
|
const value = await call(loadLedger, "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002");
|
|
12633
|
-
if (mounted.current) setLedger({ value, error: "" });
|
|
13108
|
+
if (mounted.current && request === ledgerRequest.current) setLedger({ value, error: "", refreshing: false });
|
|
13109
|
+
if (!mounted.current || request !== ledgerRequest.current) return null;
|
|
13110
|
+
return value;
|
|
12634
13111
|
} catch (error) {
|
|
12635
|
-
if (mounted.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
|
+
}));
|
|
13117
|
+
return null;
|
|
12636
13118
|
}
|
|
12637
13119
|
};
|
|
12638
13120
|
const refreshBoundaries = async () => {
|
|
@@ -12651,18 +13133,22 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
12651
13133
|
if (mounted.current) setHealth((previous) => ({ ...previous, error: text5(error?.message) || "\u65E0\u6CD5\u4FDD\u5B58\u4F53\u68C0\u72B6\u6001\u3002" }));
|
|
12652
13134
|
}
|
|
12653
13135
|
};
|
|
12654
|
-
const rate = async (runId, packageId, rating) => {
|
|
13136
|
+
const rate = async (runId, packageId, rating, expectedFinishedAt) => {
|
|
13137
|
+
if (mutationBusy.current) return;
|
|
13138
|
+
mutationBusy.current = true;
|
|
12655
13139
|
setBusy(true);
|
|
12656
13140
|
try {
|
|
12657
|
-
await call(() => rateResult({ runId, packageId, rating }), "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
13141
|
+
await call(() => rateResult({ runId, packageId, rating, ...expectedFinishedAt === void 0 ? {} : { expectedFinishedAt } }), "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
12658
13142
|
await refreshLedger();
|
|
12659
13143
|
} catch (error) {
|
|
12660
13144
|
if (mounted.current) setLedger((previous) => ({ ...previous, error: text5(error?.message) || "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002" }));
|
|
12661
13145
|
} finally {
|
|
13146
|
+
mutationBusy.current = false;
|
|
12662
13147
|
if (mounted.current) setBusy(false);
|
|
12663
13148
|
}
|
|
12664
13149
|
};
|
|
12665
13150
|
const rerun = async (runId, packageId, override, choice = null) => {
|
|
13151
|
+
if (mutationBusy.current) return;
|
|
12666
13152
|
const run = ledger.value?.runs?.find((item2) => item2.id === runId);
|
|
12667
13153
|
const item = run?.packages?.find((entry) => entry.id === packageId);
|
|
12668
13154
|
const reasons = rerunConfirmations({ run, item, override, choice, ledger: ledger.value, health: health.report, toolFor: toolForProvider, headlessIds: HEADLESS_TOOLS });
|
|
@@ -12671,6 +13157,8 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
12671
13157
|
${reasons.map((entry, index) => `${index + 1}. ${entry.text}`).join("\n")}
|
|
12672
13158
|
\u5168\u90E8\u786E\u8BA4\u5E76\u7EE7\u7EED\u5417\uFF1F`)) return;
|
|
12673
13159
|
const codes = new Set(reasons.map((entry) => entry.code));
|
|
13160
|
+
if (mutationBusy.current) return;
|
|
13161
|
+
mutationBusy.current = true;
|
|
12674
13162
|
setBusy(true);
|
|
12675
13163
|
try {
|
|
12676
13164
|
const request = {
|
|
@@ -12691,6 +13179,7 @@ ${reasons.map((entry, index) => `${index + 1}. ${entry.text}`).join("\n")}
|
|
|
12691
13179
|
} catch (error) {
|
|
12692
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` }));
|
|
12693
13181
|
} finally {
|
|
13182
|
+
mutationBusy.current = false;
|
|
12694
13183
|
if (mounted.current) setBusy(false);
|
|
12695
13184
|
}
|
|
12696
13185
|
};
|
|
@@ -12714,7 +13203,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12714
13203
|
const [toolProbes, setToolProbes] = import_react5.default.useState(null);
|
|
12715
13204
|
const [routingSettings, setRoutingSettings] = import_react5.default.useState(() => {
|
|
12716
13205
|
const value = settingsScope.getSnapshot().value ?? {};
|
|
12717
|
-
return
|
|
13206
|
+
return routingSettingsSignature(value);
|
|
12718
13207
|
});
|
|
12719
13208
|
const [view, setView] = import_react5.default.useState("plan");
|
|
12720
13209
|
const tabRefs = import_react5.default.useRef({});
|
|
@@ -12723,6 +13212,9 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12723
13212
|
const budgetValue = import_react5.default.useRef(budget);
|
|
12724
13213
|
const mounted = import_react5.default.useRef(false);
|
|
12725
13214
|
const catalogRequest = import_react5.default.useRef(0);
|
|
13215
|
+
const generationRequest = import_react5.default.useRef(0);
|
|
13216
|
+
const generationInputs = import_react5.default.useRef("");
|
|
13217
|
+
generationInputs.current = JSON.stringify([task, budget, mode, directKey, routingSettings, catalogState.catalog, toolProbes, workbench.health.report?.tools]);
|
|
12726
13218
|
import_react5.default.useEffect(() => {
|
|
12727
13219
|
if (plan) resultHeading.current?.focus({ preventScroll: true });
|
|
12728
13220
|
}, [plan]);
|
|
@@ -12739,10 +13231,10 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12739
13231
|
import_react5.default.useEffect(() => {
|
|
12740
13232
|
const syncBudget = () => {
|
|
12741
13233
|
const value = settingsScope.getSnapshot().value ?? {};
|
|
12742
|
-
const
|
|
13234
|
+
const signature2 = routingSettingsSignature(value);
|
|
12743
13235
|
setRoutingSettings((previous) => {
|
|
12744
|
-
if (previous ===
|
|
12745
|
-
return
|
|
13236
|
+
if (previous === signature2) return previous;
|
|
13237
|
+
return signature2;
|
|
12746
13238
|
});
|
|
12747
13239
|
if (budgetEdited.current) return;
|
|
12748
13240
|
const next = String(value.budgetUsd ?? 0);
|
|
@@ -12760,6 +13252,14 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12760
13252
|
setPlan(null);
|
|
12761
13253
|
setPlanError("");
|
|
12762
13254
|
}, [routingSettings]);
|
|
13255
|
+
const dataRevision = workbench.ledger.value?.dynamicData?.revision ?? "";
|
|
13256
|
+
const feedbackRevision = workbench.ledger.value?.learning?.revision ?? "";
|
|
13257
|
+
import_react5.default.useEffect(() => {
|
|
13258
|
+
if (plan && plan.workbenchRevision !== `${dataRevision}/${feedbackRevision}`) {
|
|
13259
|
+
setPlan(null);
|
|
13260
|
+
setPlanError("");
|
|
13261
|
+
}
|
|
13262
|
+
}, [dataRevision, feedbackRevision, plan]);
|
|
12763
13263
|
import_react5.default.useEffect(() => {
|
|
12764
13264
|
mounted.current = true;
|
|
12765
13265
|
const request = ++catalogRequest.current;
|
|
@@ -12791,15 +13291,19 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12791
13291
|
}
|
|
12792
13292
|
};
|
|
12793
13293
|
const invalidatePlan = () => {
|
|
13294
|
+
generationRequest.current += 1;
|
|
12794
13295
|
setPlan(null);
|
|
12795
13296
|
setPlanError("");
|
|
12796
13297
|
};
|
|
12797
13298
|
const handleToolProbes = import_react5.default.useCallback((snapshot) => {
|
|
13299
|
+
generationRequest.current += 1;
|
|
12798
13300
|
setToolProbes(snapshot);
|
|
12799
13301
|
setPlan(null);
|
|
12800
13302
|
setPlanError("");
|
|
12801
13303
|
}, []);
|
|
12802
|
-
const generate = () => {
|
|
13304
|
+
const generate = async () => {
|
|
13305
|
+
const request = ++generationRequest.current;
|
|
13306
|
+
const inputs = generationInputs.current;
|
|
12803
13307
|
setPlanError("");
|
|
12804
13308
|
try {
|
|
12805
13309
|
if (!text5(task)) throw new Error("\u8BF7\u5148\u63CF\u8FF0\u4EFB\u52A1\u3002");
|
|
@@ -12808,7 +13312,16 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12808
13312
|
if (!Number.isFinite(parsedBudget) || parsedBudget < 0) throw new Error("\u9884\u7B97\u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E 0 \u7684\u6570\u5B57\u3002");
|
|
12809
13313
|
const direct = mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] : null;
|
|
12810
13314
|
if (mode === "direct" && !direct) throw new Error("\u8BF7\u9009\u62E9\u8981\u76F4\u63A5\u4F7F\u7528\u7684\u6A21\u578B\u3002");
|
|
12811
|
-
|
|
13315
|
+
const signature2 = routingSettingsSignature(settingsScope.getSnapshot().value ?? {});
|
|
13316
|
+
const ledger = await workbench.refreshLedger();
|
|
13317
|
+
if (request !== generationRequest.current || inputs !== generationInputs.current || !mounted.current) return;
|
|
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");
|
|
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");
|
|
13322
|
+
const next = createWorkspacePlan(task, catalogState.catalog, {
|
|
13323
|
+
...ledger.routingData,
|
|
13324
|
+
learning: ledger.learning,
|
|
12812
13325
|
mode,
|
|
12813
13326
|
...direct ? { directProvider: direct.provider, directModel: direct.model } : {},
|
|
12814
13327
|
budgetUsd: parsedBudget,
|
|
@@ -12816,10 +13329,11 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12816
13329
|
installedToolIds: (toolProbes?.probes ?? []).filter((probe) => probe.installed).map((probe) => probe.id),
|
|
12817
13330
|
runnableToolIds: (toolProbes?.readiness ?? []).filter((item) => item.ready).map((item) => item.id),
|
|
12818
13331
|
preset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
|
|
12819
|
-
qualityBiases: workbench.ledger.value?.biases ?? null,
|
|
12820
13332
|
loggedOutToolIds: (workbench.health.report?.tools ?? []).filter((item) => item.installed && item.login?.state === "logged-out").map((item) => item.id)
|
|
12821
|
-
})
|
|
13333
|
+
});
|
|
13334
|
+
setPlan({ ...next, workbenchRevision: `${ledger.dynamicData?.revision ?? ""}/${ledger.learning?.revision ?? ""}` });
|
|
12822
13335
|
} catch (error) {
|
|
13336
|
+
if (request !== generationRequest.current || inputs !== generationInputs.current || !mounted.current) return;
|
|
12823
13337
|
setPlan(null);
|
|
12824
13338
|
setPlanError(text5(error?.message) || "\u65E0\u6CD5\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE\u3002");
|
|
12825
13339
|
}
|
|
@@ -12856,7 +13370,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12856
13370
|
ledger: workbench.ledger.value,
|
|
12857
13371
|
previewRun,
|
|
12858
13372
|
startRun,
|
|
12859
|
-
planningRevision: routingSettings
|
|
13373
|
+
planningRevision: `${routingSettings}/${dataRevision}/${feedbackRevision}`,
|
|
12860
13374
|
directRoute: mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] ?? null : null,
|
|
12861
13375
|
defaultPreset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
|
|
12862
13376
|
disabledReason: catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55\u2026" : catalogState.status === "error" ? "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u201C\u6A21\u578B\u914D\u7F6E\u201D\u4E2D\u5237\u65B0\u3002" : routes.length === 0 ? "\u8BF7\u5148\u5728\u5B98\u65B9\u201C\u6A21\u578B\u201D\u9875\u914D\u7F6E\u81F3\u5C11\u4E00\u6761\u6A21\u578B\u8DEF\u7EBF\u3002" : "",
|
|
@@ -12874,8 +13388,8 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12874
13388
|
onRefresh: () => {
|
|
12875
13389
|
void workbench.refreshLedger();
|
|
12876
13390
|
},
|
|
12877
|
-
onRate: (runId, packageId, rating) => {
|
|
12878
|
-
void workbench.rate(runId, packageId, rating);
|
|
13391
|
+
onRate: (runId, packageId, rating, expectedFinishedAt) => {
|
|
13392
|
+
void workbench.rate(runId, packageId, rating, expectedFinishedAt);
|
|
12879
13393
|
},
|
|
12880
13394
|
onRerun: (runId, packageId, override, choice) => {
|
|
12881
13395
|
void workbench.rerun(runId, packageId, override, choice);
|
|
@@ -12911,11 +13425,12 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12911
13425
|
CostControlCard,
|
|
12912
13426
|
{
|
|
12913
13427
|
ledger: workbench.ledger.value,
|
|
12914
|
-
error: workbench.ledger.error
|
|
13428
|
+
error: workbench.ledger.error || workbench.ledger.value?.storageNotice || "",
|
|
13429
|
+
refreshing: workbench.ledger.refreshing,
|
|
12915
13430
|
settingsScope,
|
|
12916
13431
|
onChanged: () => {
|
|
12917
13432
|
invalidatePlan();
|
|
12918
|
-
|
|
13433
|
+
return workbench.refreshLedger();
|
|
12919
13434
|
}
|
|
12920
13435
|
}
|
|
12921
13436
|
), /* @__PURE__ */ import_react5.default.createElement(
|
|
@@ -12987,7 +13502,13 @@ var optionalRouteText = (value, subject) => {
|
|
|
12987
13502
|
var rateRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RateRequest`, (value) => {
|
|
12988
13503
|
const request = plainObject2(value, "rate request");
|
|
12989
13504
|
if (!["up", "down", "clear"].includes(request.rating)) throw new TypeError("rating must be up, down or clear");
|
|
12990
|
-
|
|
13505
|
+
if (request.expectedFinishedAt !== void 0 && (!Number.isFinite(request.expectedFinishedAt) || request.expectedFinishedAt < 0)) throw new TypeError("expectedFinishedAt must be a non-negative timestamp");
|
|
13506
|
+
return {
|
|
13507
|
+
runId: idText(request.runId, "runId"),
|
|
13508
|
+
packageId: idText(request.packageId, "packageId"),
|
|
13509
|
+
rating: request.rating,
|
|
13510
|
+
...request.expectedFinishedAt === void 0 ? {} : { expectedFinishedAt: request.expectedFinishedAt }
|
|
13511
|
+
};
|
|
12991
13512
|
});
|
|
12992
13513
|
var rerunRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RerunRequest`, (value) => {
|
|
12993
13514
|
const request = plainObject2(value, "rerun request");
|