@ljwei-stak/dsh-model-router 0.15.0 → 0.16.0
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 +556 -167
- package/.dsh-plugin/index.mjs +115 -20
- package/.dsh-plugin/shared/adaptive-feedback.mjs +192 -0
- package/.dsh-plugin/shared/assignment-solver.mjs +86 -0
- package/.dsh-plugin/shared/dynamic-data-view.mjs +65 -0
- package/.dsh-plugin/shared/dynamic-data.mjs +294 -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 +2 -1
- package/.dsh-plugin/shared/router.mjs +115 -152
- package/.dsh-plugin/shared/run-ledger.mjs +5 -0
- package/CHANGELOG.md +18 -0
- package/INSTALLATION_GUIDE.zh.md +21 -15
- package/README.md +12 -6
- package/README.zh.md +12 -6
- package/docs/ROUTING_ADAPTIVE_LEARNING.zh.md +151 -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_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 +18 -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,88 @@ 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
|
+
function applyFeedbackProfile(routes, profile) {
|
|
1595
|
+
if (!Array.isArray(routes) || profile?.enabled !== true || !profile.adjustments || typeof profile.adjustments !== "object" || Array.isArray(profile.adjustments)) return routes;
|
|
1596
|
+
return routes.map((route) => {
|
|
1597
|
+
if (!route || typeof route !== "object") return route;
|
|
1598
|
+
const key = routeKey3(clean3(route.provider), clean3(route.model));
|
|
1599
|
+
if (!Object.hasOwn(profile.adjustments, key)) return route;
|
|
1600
|
+
const value = profile.adjustments[key];
|
|
1601
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return route;
|
|
1602
|
+
const adjustments = Object.fromEntries(Object.entries(value).filter(([, bias]) => finite2(bias) && Math.abs(bias) <= 0.1));
|
|
1603
|
+
if (!Object.keys(adjustments).length) return route;
|
|
1604
|
+
return {
|
|
1605
|
+
...route,
|
|
1606
|
+
preferenceAdjustments: adjustments,
|
|
1607
|
+
preferenceAdjustmentMeta: {
|
|
1608
|
+
policyVersion: profile.policyVersion,
|
|
1609
|
+
revision: profile.revision,
|
|
1610
|
+
scope: profile.scope,
|
|
1611
|
+
feedbackCount: profile.feedbackCount,
|
|
1612
|
+
effectiveWeight: profile.effectiveWeight,
|
|
1613
|
+
updatedAt: profile.updatedAt,
|
|
1614
|
+
halfLifeDays: profile.halfLifeDays,
|
|
1615
|
+
maxAdjustment: profile.maxAdjustment,
|
|
1616
|
+
window: profile.window
|
|
1617
|
+
}
|
|
1618
|
+
};
|
|
1619
|
+
});
|
|
1620
|
+
}
|
|
1621
|
+
|
|
1622
|
+
// .dsh-plugin/shared/dynamic-data-view.mjs
|
|
1623
|
+
var exactRouteKey = (route) => `${String(route?.provider ?? "")}\0${String(route?.model ?? "")}`;
|
|
1624
|
+
function applyPricingSnapshot(routes, snapshot) {
|
|
1625
|
+
if (snapshot?.kind !== "pricing" || !snapshot.prices || typeof snapshot.prices !== "object") return routes;
|
|
1626
|
+
return (Array.isArray(routes) ? routes : []).map((route) => {
|
|
1627
|
+
if (route?.pricingSource === "user") return route;
|
|
1628
|
+
const entry = snapshot.prices[exactRouteKey(route)];
|
|
1629
|
+
if (!entry) return route;
|
|
1630
|
+
return {
|
|
1631
|
+
...route,
|
|
1632
|
+
pricing: {
|
|
1633
|
+
input: entry.input,
|
|
1634
|
+
output: entry.output,
|
|
1635
|
+
currency: "USD",
|
|
1636
|
+
...entry.cacheRead === void 0 ? {} : { cacheRead: entry.cacheRead },
|
|
1637
|
+
...entry.cacheWrite === void 0 ? {} : { cacheWrite: entry.cacheWrite }
|
|
1638
|
+
},
|
|
1639
|
+
pricingSource: "dynamic",
|
|
1640
|
+
pricingVersion: snapshot.version,
|
|
1641
|
+
snapshotAsOf: entry.asOf
|
|
1642
|
+
};
|
|
1643
|
+
});
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// .dsh-plugin/client/catalog.mjs
|
|
1647
|
+
var clean4 = (value) => typeof value === "string" ? value.trim() : "";
|
|
1514
1648
|
function routesFromModelCatalog(catalog) {
|
|
1515
1649
|
const groups = Array.isArray(catalog?.groups) ? catalog.groups : [];
|
|
1516
1650
|
const routable = new Set(Array.isArray(catalog?.routableProviders) ? catalog.routableProviders : []);
|
|
1517
1651
|
const routes = [];
|
|
1518
1652
|
const seen = /* @__PURE__ */ new Set();
|
|
1519
1653
|
for (const group of groups) {
|
|
1520
|
-
const provider =
|
|
1654
|
+
const provider = clean4(group?.id);
|
|
1521
1655
|
if (!provider || !routable.has(provider)) continue;
|
|
1522
1656
|
for (const entry of Array.isArray(group.models) ? group.models : []) {
|
|
1523
|
-
const model =
|
|
1657
|
+
const model = clean4(entry?.id);
|
|
1524
1658
|
if (!model) continue;
|
|
1525
1659
|
const key = `${provider}\0${model}`;
|
|
1526
1660
|
if (seen.has(key)) continue;
|
|
1527
1661
|
seen.add(key);
|
|
1528
1662
|
const reasoning = entry?.reasoning;
|
|
1529
|
-
const efforts = Array.isArray(reasoning?.efforts) ? [...new Set(reasoning.efforts.map((item) =>
|
|
1663
|
+
const efforts = Array.isArray(reasoning?.efforts) ? [...new Set(reasoning.efforts.map((item) => clean4(item?.id ?? item)).filter(Boolean))] : [];
|
|
1530
1664
|
routes.push({
|
|
1531
1665
|
provider,
|
|
1532
|
-
providerName:
|
|
1666
|
+
providerName: clean4(group.name) || provider,
|
|
1533
1667
|
model,
|
|
1534
|
-
name:
|
|
1668
|
+
name: clean4(entry.name) || model,
|
|
1535
1669
|
reasoningKnown: reasoning !== void 0 && reasoning !== null,
|
|
1536
1670
|
reasoningEfforts: efforts,
|
|
1537
|
-
...
|
|
1671
|
+
...clean4(reasoning?.defaultEffort) ? { defaultReasoningEffort: clean4(reasoning.defaultEffort) } : {},
|
|
1538
1672
|
// modelCatalog deliberately omits modalities; unknown means the Host
|
|
1539
1673
|
// must still validate image capability before a real request.
|
|
1540
1674
|
inputModalities: []
|
|
@@ -1545,10 +1679,9 @@ function routesFromModelCatalog(catalog) {
|
|
|
1545
1679
|
return routes;
|
|
1546
1680
|
}
|
|
1547
1681
|
function createWorkspacePlan(task, catalog, options = {}) {
|
|
1548
|
-
const
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
), options.qualityBiases ?? null);
|
|
1682
|
+
const profiled = applyModelProfiles(routesFromModelCatalog(catalog), parseModelProfilesJson(options.modelProfilesJson ?? "[]"));
|
|
1683
|
+
const priced = applyPricingSnapshot(profiled, options.pricingSnapshot ?? null);
|
|
1684
|
+
const routes = options.learning ? applyFeedbackProfile(priced, options.learning) : applyQualityBiases(priced, options.qualityBiases ?? null);
|
|
1552
1685
|
return createPlanFromRoutes(task, routes, options);
|
|
1553
1686
|
}
|
|
1554
1687
|
|
|
@@ -1575,6 +1708,7 @@ function profileDraft(profile) {
|
|
|
1575
1708
|
cacheWrite: profile?.pricing?.cacheWrite === void 0 ? "" : String(profile.pricing.cacheWrite),
|
|
1576
1709
|
specialties: Array.isArray(profile?.specialties) ? profile.specialties.join(", ") : "",
|
|
1577
1710
|
cliModel: profile?.cliModel ?? "",
|
|
1711
|
+
benchmarkModel: profile?.benchmarkModel ?? "",
|
|
1578
1712
|
execution: profile?.execution === "official" || profile?.execution === "api" ? profile.execution : "auto",
|
|
1579
1713
|
billing: BILLING_MODES.includes(profile?.billing) ? profile.billing : DEFAULT_BILLING_MODE,
|
|
1580
1714
|
subscription: ["plan-key", "cli-login", "none"].includes(profile?.subscription) ? profile.subscription : "auto",
|
|
@@ -1586,6 +1720,8 @@ function profileFromDraft(route, draft) {
|
|
|
1586
1720
|
const model = field(route?.model);
|
|
1587
1721
|
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
1722
|
const profile = { provider, model };
|
|
1723
|
+
const benchmarkModel = field(draft?.benchmarkModel);
|
|
1724
|
+
if (benchmarkModel) profile.benchmarkModel = benchmarkModel;
|
|
1589
1725
|
const quality = nonnegativeField(draft?.quality, "\u8D28\u91CF\u8BC4\u5206", 100);
|
|
1590
1726
|
if (quality !== null) profile.quality = quality;
|
|
1591
1727
|
const input = nonnegativeField(draft?.input, "\u8F93\u5165\u5355\u4EF7");
|
|
@@ -1655,7 +1791,7 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
|
1655
1791
|
const [notice, setNotice] = import_react.default.useState(null);
|
|
1656
1792
|
import_react.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
|
|
1657
1793
|
const route = routes.find((item) => profileRouteKey(item) === selected) ?? routes[0];
|
|
1658
|
-
const
|
|
1794
|
+
const routeKey4 = route ? profileRouteKey(route) : "";
|
|
1659
1795
|
const rawJson = snapshot.value?.modelProfilesJson ?? "[]";
|
|
1660
1796
|
let profiles = [];
|
|
1661
1797
|
let loadError = "";
|
|
@@ -1664,16 +1800,16 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
|
1664
1800
|
} catch (error) {
|
|
1665
1801
|
loadError = messageOf(error);
|
|
1666
1802
|
}
|
|
1667
|
-
const saved = profiles.find((item) => profileRouteKey(item) ===
|
|
1668
|
-
const pending = drafts[
|
|
1803
|
+
const saved = profiles.find((item) => profileRouteKey(item) === routeKey4);
|
|
1804
|
+
const pending = drafts[routeKey4];
|
|
1669
1805
|
const draft = pending?.fields ?? profileDraft(saved);
|
|
1670
1806
|
const writable = snapshot.status === "ready" && snapshot.writable === true && !saving && !loadError;
|
|
1671
1807
|
const unmatched = profiles.filter((item) => !routes.some((route2) => profileRouteKey(route2) === profileRouteKey(item))).length;
|
|
1672
1808
|
const edit = (name, value) => {
|
|
1673
1809
|
if (!route || !writable) return;
|
|
1674
1810
|
setDrafts((previous) => {
|
|
1675
|
-
const previousEntry = previous[
|
|
1676
|
-
return { ...previous, [
|
|
1811
|
+
const previousEntry = previous[routeKey4] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
|
|
1812
|
+
return { ...previous, [routeKey4]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
|
|
1677
1813
|
});
|
|
1678
1814
|
setNotice(null);
|
|
1679
1815
|
};
|
|
@@ -1698,7 +1834,7 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
|
1698
1834
|
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
1835
|
setDrafts((previous) => {
|
|
1700
1836
|
const next = { ...previous };
|
|
1701
|
-
delete next[
|
|
1837
|
+
delete next[routeKey4];
|
|
1702
1838
|
return next;
|
|
1703
1839
|
});
|
|
1704
1840
|
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 +1848,15 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
|
1712
1848
|
const reload = () => {
|
|
1713
1849
|
setDrafts((previous) => {
|
|
1714
1850
|
const next = { ...previous };
|
|
1715
|
-
delete next[
|
|
1851
|
+
delete next[routeKey4];
|
|
1716
1852
|
return next;
|
|
1717
1853
|
});
|
|
1718
1854
|
setNotice(null);
|
|
1719
1855
|
};
|
|
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:
|
|
1856
|
+
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
1857
|
setSelected(event.target.value);
|
|
1722
1858
|
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) !==
|
|
1859
|
+
} }, 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
1860
|
void write(false);
|
|
1725
1861
|
} }, 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
1862
|
void write(true);
|
|
@@ -1978,6 +2114,92 @@ function rerunConfirmations({ run, item, override = null, choice = null, ledger
|
|
|
1978
2114
|
return reasons;
|
|
1979
2115
|
}
|
|
1980
2116
|
|
|
2117
|
+
// .dsh-plugin/client/adaptive-state.mjs
|
|
2118
|
+
var ADAPTIVE_DEFAULTS = Object.freeze({
|
|
2119
|
+
feedbackLearningEnabled: true,
|
|
2120
|
+
feedbackHalfLifeDays: 30,
|
|
2121
|
+
feedbackPriorWeight: 3,
|
|
2122
|
+
feedbackMaxAdjustment: 0.04,
|
|
2123
|
+
feedbackResetAt: 0,
|
|
2124
|
+
dynamicDataEnabled: false,
|
|
2125
|
+
liveBenchEndpoint: "https://livebench.ai",
|
|
2126
|
+
dynamicDataTtlMinutes: 1440,
|
|
2127
|
+
pricingSnapshotEndpoint: ""
|
|
2128
|
+
});
|
|
2129
|
+
var ADAPTIVE_NUMBER_FIELDS = Object.freeze([
|
|
2130
|
+
Object.freeze({ key: "feedbackHalfLifeDays", min: 1, max: 3650, step: 1 }),
|
|
2131
|
+
Object.freeze({ key: "feedbackPriorWeight", min: 1, max: 1e6, step: 1 }),
|
|
2132
|
+
Object.freeze({ key: "feedbackMaxAdjustment", min: 0, max: 0.1, step: 0.01 }),
|
|
2133
|
+
Object.freeze({ key: "dynamicDataTtlMinutes", min: 1, max: 10080, step: 1 })
|
|
2134
|
+
]);
|
|
2135
|
+
function adaptiveSettingValue(settings, key) {
|
|
2136
|
+
return settings?.[key] ?? ADAPTIVE_DEFAULTS[key];
|
|
2137
|
+
}
|
|
2138
|
+
function parseAdaptiveNumber(key, draft) {
|
|
2139
|
+
const field2 = ADAPTIVE_NUMBER_FIELDS.find((item) => item.key === key);
|
|
2140
|
+
if (!field2) throw new TypeError("Unknown numeric setting");
|
|
2141
|
+
if (typeof draft !== "string" && typeof draft !== "number" || String(draft).trim() === "") throw new TypeError("Enter a number");
|
|
2142
|
+
const value = Number(draft);
|
|
2143
|
+
if (!Number.isFinite(value) || value < field2.min || value > field2.max) throw new RangeError(`Expected ${field2.min}..${field2.max}`);
|
|
2144
|
+
return value;
|
|
2145
|
+
}
|
|
2146
|
+
function parsePublicEndpoint(key, draft) {
|
|
2147
|
+
if (!["liveBenchEndpoint", "pricingSnapshotEndpoint"].includes(key)) throw new TypeError("Unknown endpoint setting");
|
|
2148
|
+
if (typeof draft !== "string") throw new TypeError("Enter a public HTTPS URL");
|
|
2149
|
+
const value = draft.trim();
|
|
2150
|
+
if (key === "pricingSnapshotEndpoint" && value === "") return "";
|
|
2151
|
+
if (!value || value.length > 2048) throw new TypeError("Enter a public HTTPS URL");
|
|
2152
|
+
let parsed;
|
|
2153
|
+
try {
|
|
2154
|
+
parsed = new URL(value);
|
|
2155
|
+
} catch {
|
|
2156
|
+
throw new TypeError("Enter a public HTTPS URL");
|
|
2157
|
+
}
|
|
2158
|
+
if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.search || parsed.hash || parsed.port && parsed.port !== "443") {
|
|
2159
|
+
throw new TypeError("Use a public HTTPS URL without credentials, query or fragment");
|
|
2160
|
+
}
|
|
2161
|
+
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");
|
|
2162
|
+
return value;
|
|
2163
|
+
}
|
|
2164
|
+
function publicEndpointDraft(key, value) {
|
|
2165
|
+
try {
|
|
2166
|
+
return parsePublicEndpoint(key, value);
|
|
2167
|
+
} catch {
|
|
2168
|
+
return "";
|
|
2169
|
+
}
|
|
2170
|
+
}
|
|
2171
|
+
function feedbackRatingArguments(run, item, direction) {
|
|
2172
|
+
if (!["up", "down"].includes(direction)) throw new TypeError("Unknown rating direction");
|
|
2173
|
+
const active = direction === "up" ? item?.rating === 1 : item?.rating === -1;
|
|
2174
|
+
return [run?.id, item?.id, active ? "clear" : direction, item?.finishedAt];
|
|
2175
|
+
}
|
|
2176
|
+
var nonnegative2 = (value) => Number.isFinite(value) && value >= 0 ? value : 0;
|
|
2177
|
+
function learningView(learning, settings = {}) {
|
|
2178
|
+
return {
|
|
2179
|
+
available: Boolean(learning && typeof learning === "object"),
|
|
2180
|
+
enabled: typeof learning?.enabled === "boolean" ? learning.enabled : adaptiveSettingValue(settings, "feedbackLearningEnabled") !== false,
|
|
2181
|
+
feedbackCount: nonnegative2(learning?.feedbackCount),
|
|
2182
|
+
ignoredCount: nonnegative2(learning?.ignoredCount),
|
|
2183
|
+
effectiveWeight: nonnegative2(learning?.effectiveWeight),
|
|
2184
|
+
policyVersion: typeof learning?.policyVersion === "string" ? learning.policyVersion.slice(0, 100) : null,
|
|
2185
|
+
resetAt: nonnegative2(adaptiveSettingValue(settings, "feedbackResetAt"))
|
|
2186
|
+
};
|
|
2187
|
+
}
|
|
2188
|
+
var STATUS_LABEL = Object.freeze({ disabled: "\u672A\u542F\u7528", missing: "\u5C1A\u65E0\u5FEB\u7167", fresh: "\u5FEB\u7167\u6709\u6548", stale: "\u5FEB\u7167\u8FC7\u671F", error: "\u66F4\u65B0\u5931\u8D25" });
|
|
2189
|
+
function dynamicSourceView(source) {
|
|
2190
|
+
const status = Object.hasOwn(STATUS_LABEL, source?.status ?? "") ? source.status : "missing";
|
|
2191
|
+
let publicSource = "";
|
|
2192
|
+
if (typeof source?.source === "string") publicSource = publicEndpointDraft("pricingSnapshotEndpoint", source.source);
|
|
2193
|
+
return {
|
|
2194
|
+
status,
|
|
2195
|
+
label: STATUS_LABEL[status],
|
|
2196
|
+
source: publicSource,
|
|
2197
|
+
version: typeof source?.version === "string" ? source.version.slice(0, 160) : null,
|
|
2198
|
+
verifiedAt: Number.isFinite(source?.verifiedAt) && source.verifiedAt > 0 ? source.verifiedAt : null,
|
|
2199
|
+
count: nonnegative2(source?.modelCount ?? source?.rowCount)
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
|
|
1981
2203
|
// .dsh-plugin/client/router-insights.jsx
|
|
1982
2204
|
var text = (value) => typeof value === "string" ? value.trim() : "";
|
|
1983
2205
|
var BAND = { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u56F0\u96BE" };
|
|
@@ -2028,6 +2250,134 @@ function useSettingsWriter(settingsScope) {
|
|
|
2028
2250
|
};
|
|
2029
2251
|
return { value: snapshot.value ?? {}, writable, write, notice };
|
|
2030
2252
|
}
|
|
2253
|
+
var ADAPTIVE_FIELD_LABEL = {
|
|
2254
|
+
feedbackHalfLifeDays: "\u53CD\u9988\u534A\u8870\u671F\uFF08\u5929\uFF09",
|
|
2255
|
+
feedbackPriorWeight: "\u6536\u7F29\u5F3A\u5EA6",
|
|
2256
|
+
feedbackMaxAdjustment: "\u6700\u5927\u504F\u597D\u6548\u7528\u8C03\u6574",
|
|
2257
|
+
dynamicDataTtlMinutes: "\u516C\u5171\u5FEB\u7167\u68C0\u67E5\u95F4\u9694\uFF08\u5206\u949F\uFF09"
|
|
2258
|
+
};
|
|
2259
|
+
function AdaptiveNumberControl({ field: field2, value, writable, onSave }) {
|
|
2260
|
+
const [draft, setDraft] = import_react2.default.useState("");
|
|
2261
|
+
const [notice, setNotice] = import_react2.default.useState("");
|
|
2262
|
+
const save = async () => {
|
|
2263
|
+
try {
|
|
2264
|
+
const next = parseAdaptiveNumber(field2.key, draft);
|
|
2265
|
+
setNotice("");
|
|
2266
|
+
if (await onSave(field2.key, next)) setDraft("");
|
|
2267
|
+
} catch {
|
|
2268
|
+
setNotice(`\u8BF7\u8F93\u5165 ${field2.min} \u5230 ${field2.max} \u7684\u6570\u5B57\u3002`);
|
|
2269
|
+
}
|
|
2270
|
+
};
|
|
2271
|
+
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(
|
|
2272
|
+
"input",
|
|
2273
|
+
{
|
|
2274
|
+
className: "mr-input mr-budget",
|
|
2275
|
+
id: `mr-${field2.key}`,
|
|
2276
|
+
type: "number",
|
|
2277
|
+
min: field2.min,
|
|
2278
|
+
max: field2.max,
|
|
2279
|
+
step: field2.step,
|
|
2280
|
+
placeholder: String(value),
|
|
2281
|
+
value: draft,
|
|
2282
|
+
disabled: !writable,
|
|
2283
|
+
onChange: (event) => setDraft(event.target.value)
|
|
2284
|
+
}
|
|
2285
|
+
), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable || draft === "", onClick: () => {
|
|
2286
|
+
void save();
|
|
2287
|
+
} }, "\u4FDD\u5B58")), notice && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-error", role: "alert" }, notice));
|
|
2288
|
+
}
|
|
2289
|
+
function PublicEndpointControl({ name, label, value, writable, onSave }) {
|
|
2290
|
+
const [draft, setDraft] = import_react2.default.useState(null);
|
|
2291
|
+
const [notice, setNotice] = import_react2.default.useState("");
|
|
2292
|
+
const current = publicEndpointDraft(name, value);
|
|
2293
|
+
const save = async () => {
|
|
2294
|
+
try {
|
|
2295
|
+
const next = parsePublicEndpoint(name, draft ?? current);
|
|
2296
|
+
setNotice("");
|
|
2297
|
+
if (await onSave(name, next)) setDraft(null);
|
|
2298
|
+
} catch {
|
|
2299
|
+
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");
|
|
2300
|
+
}
|
|
2301
|
+
};
|
|
2302
|
+
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(
|
|
2303
|
+
"input",
|
|
2304
|
+
{
|
|
2305
|
+
className: "mr-input",
|
|
2306
|
+
id: `mr-${name}`,
|
|
2307
|
+
type: "url",
|
|
2308
|
+
maxLength: 2048,
|
|
2309
|
+
value: draft ?? current,
|
|
2310
|
+
disabled: !writable,
|
|
2311
|
+
placeholder: name === "liveBenchEndpoint" ? "https://livebench.ai" : "\u7559\u7A7A\uFF1A\u4E0D\u8BFB\u53D6\u8FDC\u7A0B\u4EF7\u683C\u5FEB\u7167",
|
|
2312
|
+
onChange: (event) => setDraft(event.target.value)
|
|
2313
|
+
}
|
|
2314
|
+
), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable || draft === null, onClick: () => {
|
|
2315
|
+
void save();
|
|
2316
|
+
} }, "\u4FDD\u5B58\u5730\u5740"), notice && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-error", role: "alert" }, notice));
|
|
2317
|
+
}
|
|
2318
|
+
function PublicSourceStatus({ label, source }) {
|
|
2319
|
+
const view = dynamicSourceView(source);
|
|
2320
|
+
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, label, "\uFF1A", view.label), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, view.version ? `\u7248\u672C ${view.version} \xB7 ` : "", view.count, " \u6761\u8BB0\u5F55", view.verifiedAt ? ` \xB7 \u6700\u8FD1\u6821\u9A8C ${new Date(view.verifiedAt).toLocaleString()}` : ""), view.source && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u516C\u5F00\u6765\u6E90\uFF1A", view.source), view.status === "error" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u66F4\u65B0\u5931\u8D25\uFF1B\u662F\u5426\u53EF\u7528\u7531 Host \u6821\u9A8C\uFF0C\u4E0D\u5C06\u7F3A\u5931\u6570\u636E\u5F53\u4F5C\u96F6\u6210\u672C\u6216\u6EE1\u5206\u3002"));
|
|
2321
|
+
}
|
|
2322
|
+
function AdaptiveControls({ ledger, settings, onChanged }) {
|
|
2323
|
+
const value = settings.value;
|
|
2324
|
+
const learning = learningView(ledger?.learning, value);
|
|
2325
|
+
const [resetBusy, setResetBusy] = import_react2.default.useState(false);
|
|
2326
|
+
const save = async (key, next) => {
|
|
2327
|
+
const accepted = await settings.write(key, next);
|
|
2328
|
+
if (accepted) onChanged?.();
|
|
2329
|
+
return accepted;
|
|
2330
|
+
};
|
|
2331
|
+
const reset = async () => {
|
|
2332
|
+
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;
|
|
2333
|
+
setResetBusy(true);
|
|
2334
|
+
try {
|
|
2335
|
+
await save("feedbackResetAt", Date.now());
|
|
2336
|
+
} finally {
|
|
2337
|
+
setResetBusy(false);
|
|
2338
|
+
}
|
|
2339
|
+
};
|
|
2340
|
+
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(
|
|
2341
|
+
"input",
|
|
2342
|
+
{
|
|
2343
|
+
type: "checkbox",
|
|
2344
|
+
checked: adaptiveSettingValue(value, "feedbackLearningEnabled") !== false,
|
|
2345
|
+
disabled: !settings.writable,
|
|
2346
|
+
onChange: (event) => {
|
|
2347
|
+
void save("feedbackLearningEnabled", event.target.checked);
|
|
2348
|
+
}
|
|
2349
|
+
}
|
|
2350
|
+
), "\u542F\u7528\u7528\u6237\u53CD\u9988\u5B66\u4E60"), ADAPTIVE_NUMBER_FIELDS.filter((field2) => field2.key !== "dynamicDataTtlMinutes").map((field2) => /* @__PURE__ */ import_react2.default.createElement(
|
|
2351
|
+
AdaptiveNumberControl,
|
|
2352
|
+
{
|
|
2353
|
+
key: field2.key,
|
|
2354
|
+
field: field2,
|
|
2355
|
+
value: adaptiveSettingValue(value, field2.key),
|
|
2356
|
+
writable: settings.writable,
|
|
2357
|
+
onSave: save
|
|
2358
|
+
}
|
|
2359
|
+
))), learning.available ? /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, learning.enabled ? "\u5B66\u4E60\u5DF2\u542F\u7528" : "\u5B66\u4E60\u5DF2\u505C\u7528", " \xB7 \u6709\u6548\u53CD\u9988 ", learning.feedbackCount, " \xB7 \u6709\u6548\u6743\u91CD ", learning.effectiveWeight.toFixed(2), " \xB7 \u5FFD\u7565 ", learning.ignoredCount, learning.policyVersion ? ` \xB7 \u7B56\u7565 ${learning.policyVersion}` : "") : /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, "\u5B66\u4E60\u6458\u8981\u5C1A\u672A\u8BFB\u53D6\uFF1B\u4FDD\u5B58\u540E\u5237\u65B0\u6267\u884C\u8BB0\u5F55\u67E5\u770B\u3002"), learning.resetAt > 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u5B66\u4E60\u8D77\u70B9\uFF1A", new Date(learning.resetAt).toLocaleString(), "\uFF0C\u6B64\u524D\u53CD\u9988\u4E0D\u518D\u53C2\u4E0E\u8C03\u6574\u3002"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !settings.writable || resetBusy, onClick: () => {
|
|
2360
|
+
void reset();
|
|
2361
|
+
} }, resetBusy ? "\u6B63\u5728\u8BBE\u7F6E\u2026" : "\u4ECE\u73B0\u5728\u91CD\u65B0\u5B66\u4E60"), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "mr-section-title" }, "\u52A8\u6001\u516C\u5171\u6570\u636E"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u9ED8\u8BA4\u5173\u95ED\u3002\u660E\u786E\u542F\u7528\u540E\uFF0C\u5728\u6253\u5F00 / \u5237\u65B0\u8D26\u672C\u53CA\u6267\u884C\u524D\u6309\u68C0\u67E5\u95F4\u9694\u8BFB\u53D6\u516C\u5F00 LiveBench \u548C\u53EF\u9009\u4EF7\u683C\u5FEB\u7167\uFF1B\u4EF7\u683C\u4E0E\u80FD\u529B\u5148\u9A8C\u4FDD\u7559\u7248\u672C\uFF0C\u4E0D\u4EE3\u8868\u771F\u5B9E\u4EFB\u52A1\u6536\u76CA\u3002\u53EA\u6709\u6253\u5F00\u516C\u5171\u6E90\u5F00\u5173\u624D\u4F1A\u8BFB\u53D6\u8FDC\u7A0B\u6570\u636E\uFF0C\u4E0D\u53D1\u9001\u4EFB\u52A1\u6216\u53CD\u9988\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-check" }, /* @__PURE__ */ import_react2.default.createElement(
|
|
2362
|
+
"input",
|
|
2363
|
+
{
|
|
2364
|
+
type: "checkbox",
|
|
2365
|
+
checked: adaptiveSettingValue(value, "dynamicDataEnabled") === true,
|
|
2366
|
+
disabled: !settings.writable,
|
|
2367
|
+
onChange: (event) => {
|
|
2368
|
+
void save("dynamicDataEnabled", event.target.checked);
|
|
2369
|
+
}
|
|
2370
|
+
}
|
|
2371
|
+
), "\u542F\u7528\u52A8\u6001\u516C\u5171\u6570\u636E"), /* @__PURE__ */ import_react2.default.createElement(
|
|
2372
|
+
AdaptiveNumberControl,
|
|
2373
|
+
{
|
|
2374
|
+
field: ADAPTIVE_NUMBER_FIELDS.find((field2) => field2.key === "dynamicDataTtlMinutes"),
|
|
2375
|
+
value: adaptiveSettingValue(value, "dynamicDataTtlMinutes"),
|
|
2376
|
+
writable: settings.writable,
|
|
2377
|
+
onSave: save
|
|
2378
|
+
}
|
|
2379
|
+
), /* @__PURE__ */ import_react2.default.createElement(PublicEndpointControl, { name: "liveBenchEndpoint", label: "LiveBench \u516C\u5F00\u6E90", value: adaptiveSettingValue(value, "liveBenchEndpoint"), writable: settings.writable, onSave: save }), /* @__PURE__ */ import_react2.default.createElement(PublicEndpointControl, { name: "pricingSnapshotEndpoint", label: "\u4EF7\u683C\u5FEB\u7167\u516C\u5F00\u6E90\uFF08\u53EF\u9009\uFF09", value: adaptiveSettingValue(value, "pricingSnapshotEndpoint"), writable: settings.writable, onSave: save }), /* @__PURE__ */ import_react2.default.createElement(PublicSourceStatus, { label: "LiveBench", source: ledger?.dynamicData?.liveBench }), /* @__PURE__ */ import_react2.default.createElement(PublicSourceStatus, { label: "\u4EF7\u683C", source: ledger?.dynamicData?.pricing })));
|
|
2380
|
+
}
|
|
2031
2381
|
function CostControlCard({ ledger, settingsScope, onChanged, error }) {
|
|
2032
2382
|
const settings = useSettingsWriter(settingsScope);
|
|
2033
2383
|
const [dailyDraft, setDailyDraft] = import_react2.default.useState("");
|
|
@@ -2066,7 +2416,7 @@ function CostControlCard({ ledger, settingsScope, onChanged, error }) {
|
|
|
2066
2416
|
void set("allowManualReassign", event.target.checked);
|
|
2067
2417
|
} }), "\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
2418
|
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)));
|
|
2419
|
+
} }), "\u76F4\u63A5\u542F\u52A8\u65E0\u6C99\u7BB1 CLI \u524D\u5148\u786E\u8BA4")), /* @__PURE__ */ import_react2.default.createElement(AdaptiveControls, { ledger, settings, onChanged }), settings.notice && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, settings.notice)));
|
|
2070
2420
|
}
|
|
2071
2421
|
function DagView({ packages, renderNode, label = "\u5B50\u4EFB\u52A1\u4F9D\u8D56\u56FE" }) {
|
|
2072
2422
|
const layers = dagLayers(packages);
|
|
@@ -2088,7 +2438,7 @@ function RunNode({ run, item, routes, allowReassign, busy, onRate, onRerun }) {
|
|
|
2088
2438
|
const rerun = rerunSupport(run);
|
|
2089
2439
|
const canRerun = rerun.supported && !item.ok && !busy;
|
|
2090
2440
|
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
|
|
2441
|
+
return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-route" }, item.provider, "/", item.model, item.reassigned ? "\uFF08\u5DF2\u6539\u6D3E\uFF09" : ""), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelText, { item }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u9884\u4F30 ", money(item.estimatedCost), " \xB7 \u5B9E\u9645 ", cost.budget, item.difficulty ? ` \xB7 \u96BE\u5EA6 ${BAND[item.difficulty] ?? item.difficulty}` : "")), cost.reference && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, cost.reference), item.actualModel && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "CLI \u56DE\u62A5\u6A21\u578B\uFF1A", item.actualModel), (item.billing || item.billingMode) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u8BA1\u8D39\uFF1A", BILLING_CHANNEL_LABEL[item.billing] ?? "\u2014", item.subscriptionRoute ? ` \xB7 \u5957\u9910\u8DEF\u7EBF ${item.subscriptionRoute.provider}/${item.subscriptionRoute.model}` : "", item.billingMode && item.billingMode !== "subscription-first" ? ` \xB7 ${BILLING_MODE_LABEL[item.billingMode] ?? item.billingMode}` : ""), billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-warn-text" }, "\u8BA1\u8D39\u5207\u6362\uFF1A", billingSwitchText(item)), item.fallback?.reason && item.fallback.reason !== billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u56DE\u9000\u539F\u56E0\uFF1A", item.fallback.reason), item.fallback?.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, "CLI \u539F\u59CB\u9519\u8BEF\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, item.fallback.error)), !item.ok && item.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, item.error), item.review && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5F3A\u6A21\u578B\u62BD\u67E5\uFF08", item.review.provider, "/", item.review.model, "\uFF09\uFF1A", item.review.score ? `${item.review.score}/5` : "\u672A\u8BC4\u5206", " ", item.review.summary), item.answer && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u7ED3\u679C"), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.answer, item.answerTruncated ? "\n\u2026\uFF08\u5DF2\u622A\u65AD\uFF09" : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-node-actions" }, item.ok && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", "aria-pressed": item.rating === 1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "up")) }, "\u{1F44D} \u6709\u7528"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", "aria-pressed": item.rating === -1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "down")) }, "\u{1F44E} \u4E0D\u597D")), canRerun && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-mini", type: "button", title: rerun.confirm || void 0, onClick: () => onRerun(run.id, item.id, null) }, run.kind === "tool" ? "\u91CD\u65B0\u6267\u884C\u6B64\u8C03\u7528" : rerun.writes ? "\u5728\u65B0\u5DE5\u4F5C\u533A\u7EED\u8DD1" : "\u91CD\u8DD1\u6B64\u6B65"), !rerun.supported && !item.ok && item.ran && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, rerun.reason), rerun.supported && rerun.reassign && !(rerun.writes && item.ok) && allowReassign && others.length > 0 && !busy && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement("select", { className: "mr-input mr-mini-select", "aria-label": "\u6539\u6D3E\u5230", value: target, onChange: (event) => setTarget(event.target.value) }, /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "\u6539\u6D3E\u5230\u2026"), others.map((route) => /* @__PURE__ */ import_react2.default.createElement("option", { key: `${route.provider}/${route.model}`, value: `${route.provider}\0${route.model}` }, route.provider, "/", route.model))), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", disabled: !target, onClick: () => {
|
|
2092
2442
|
const [provider, model] = target.split("\0");
|
|
2093
2443
|
onRerun(run.id, item.id, { provider, model });
|
|
2094
2444
|
} }, "\u6539\u6D3E\u5E76\u91CD\u8DD1"))));
|
|
@@ -2173,26 +2523,26 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
|
|
|
2173
2523
|
if (blocked || busy) return;
|
|
2174
2524
|
const current = ++pendingRequest.current;
|
|
2175
2525
|
const snapshot = request();
|
|
2176
|
-
const
|
|
2526
|
+
const signature2 = inputSignature;
|
|
2177
2527
|
setPhase({ kind: "previewing" });
|
|
2178
2528
|
try {
|
|
2179
2529
|
const value = unwrapRemote(await previewRun(snapshot), "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002");
|
|
2180
|
-
if (mounted.current && current === pendingRequest.current &&
|
|
2530
|
+
if (mounted.current && current === pendingRequest.current && signature2 === latestSignature.current) setPhase({ kind: "preview", value, request: snapshot, signature: signature2 });
|
|
2181
2531
|
} catch (error) {
|
|
2182
|
-
if (mounted.current && current === pendingRequest.current &&
|
|
2532
|
+
if (mounted.current && current === pendingRequest.current && signature2 === latestSignature.current) setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002" });
|
|
2183
2533
|
}
|
|
2184
2534
|
};
|
|
2185
2535
|
const confirm2 = async () => {
|
|
2186
2536
|
if (phase.kind !== "preview" || phase.signature !== latestSignature.current) return;
|
|
2187
2537
|
const shown = phase.value;
|
|
2188
2538
|
const snapshot = phase.request;
|
|
2189
|
-
const
|
|
2190
|
-
setPhase({ kind: "running", value: shown, request: snapshot, signature });
|
|
2539
|
+
const signature2 = phase.signature;
|
|
2540
|
+
setPhase({ kind: "running", value: shown, request: snapshot, signature: signature2 });
|
|
2191
2541
|
try {
|
|
2192
2542
|
const value = unwrapRemote(await startRun({ ...snapshot, confirmedReasons: (shown.reasons ?? []).map((item) => item.code) }), "\u6267\u884C\u5931\u8D25\u3002");
|
|
2193
2543
|
if (!mounted.current) return;
|
|
2194
2544
|
if (value.status === "needs-confirmation") {
|
|
2195
|
-
setPhase(
|
|
2545
|
+
setPhase(signature2 === latestSignature.current ? { kind: "preview", value, request: snapshot, signature: signature2, changed: true } : { kind: "idle" });
|
|
2196
2546
|
return;
|
|
2197
2547
|
}
|
|
2198
2548
|
setPhase({ kind: "done", value });
|
|
@@ -11653,7 +12003,7 @@ var xterm_default = `/**
|
|
|
11653
12003
|
`;
|
|
11654
12004
|
|
|
11655
12005
|
// .dsh-plugin/client/host-version.mjs
|
|
11656
|
-
var ROUTER_CLIENT_VERSION = true ? "0.
|
|
12006
|
+
var ROUTER_CLIENT_VERSION = true ? "0.16.0" : "";
|
|
11657
12007
|
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
12008
|
function isMissingRemoteMethod(message) {
|
|
11659
12009
|
const value = String(message ?? "");
|
|
@@ -12431,6 +12781,11 @@ var router_main_default = `.mr-workspace {
|
|
|
12431
12781
|
// .dsh-plugin/client/router-main.jsx
|
|
12432
12782
|
var money2 = (value) => value === null || value === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : `$${Number(value).toFixed(4)}`;
|
|
12433
12783
|
var text5 = (value) => typeof value === "string" ? value.trim() : "";
|
|
12784
|
+
var routingSettingsSignature = (value) => JSON.stringify([
|
|
12785
|
+
value.modelProfilesJson,
|
|
12786
|
+
value.routingPreset,
|
|
12787
|
+
...Object.keys(ADAPTIVE_DEFAULTS).map((key) => value[key] ?? ADAPTIVE_DEFAULTS[key])
|
|
12788
|
+
]);
|
|
12434
12789
|
function RouterPanelIcon({ size = 20, active = false }) {
|
|
12435
12790
|
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
12791
|
}
|
|
@@ -12611,6 +12966,7 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
12611
12966
|
const [boundaries, setBoundaries] = import_react5.default.useState({ value: null, error: "" });
|
|
12612
12967
|
const [busy, setBusy] = import_react5.default.useState(false);
|
|
12613
12968
|
const mounted = import_react5.default.useRef(true);
|
|
12969
|
+
const ledgerRequest = import_react5.default.useRef(0);
|
|
12614
12970
|
import_react5.default.useEffect(() => () => {
|
|
12615
12971
|
mounted.current = false;
|
|
12616
12972
|
}, []);
|
|
@@ -12628,11 +12984,15 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
12628
12984
|
}
|
|
12629
12985
|
};
|
|
12630
12986
|
const refreshLedger = async () => {
|
|
12987
|
+
const request = ++ledgerRequest.current;
|
|
12631
12988
|
try {
|
|
12632
12989
|
const value = await call(loadLedger, "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002");
|
|
12633
|
-
if (mounted.current) setLedger({ value, error: "" });
|
|
12990
|
+
if (mounted.current && request === ledgerRequest.current) setLedger({ value, error: "" });
|
|
12991
|
+
if (request !== ledgerRequest.current) return null;
|
|
12992
|
+
return value;
|
|
12634
12993
|
} catch (error) {
|
|
12635
|
-
if (mounted.current) setLedger((previous) => ({ ...previous, error: text5(error?.message) || "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002" }));
|
|
12994
|
+
if (mounted.current && request === ledgerRequest.current) setLedger((previous) => ({ ...previous, error: text5(error?.message) || "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002" }));
|
|
12995
|
+
return null;
|
|
12636
12996
|
}
|
|
12637
12997
|
};
|
|
12638
12998
|
const refreshBoundaries = async () => {
|
|
@@ -12651,10 +13011,10 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
|
|
|
12651
13011
|
if (mounted.current) setHealth((previous) => ({ ...previous, error: text5(error?.message) || "\u65E0\u6CD5\u4FDD\u5B58\u4F53\u68C0\u72B6\u6001\u3002" }));
|
|
12652
13012
|
}
|
|
12653
13013
|
};
|
|
12654
|
-
const rate = async (runId, packageId, rating) => {
|
|
13014
|
+
const rate = async (runId, packageId, rating, expectedFinishedAt) => {
|
|
12655
13015
|
setBusy(true);
|
|
12656
13016
|
try {
|
|
12657
|
-
await call(() => rateResult({ runId, packageId, rating }), "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
13017
|
+
await call(() => rateResult({ runId, packageId, rating, ...expectedFinishedAt === void 0 ? {} : { expectedFinishedAt } }), "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002");
|
|
12658
13018
|
await refreshLedger();
|
|
12659
13019
|
} catch (error) {
|
|
12660
13020
|
if (mounted.current) setLedger((previous) => ({ ...previous, error: text5(error?.message) || "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002" }));
|
|
@@ -12714,7 +13074,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12714
13074
|
const [toolProbes, setToolProbes] = import_react5.default.useState(null);
|
|
12715
13075
|
const [routingSettings, setRoutingSettings] = import_react5.default.useState(() => {
|
|
12716
13076
|
const value = settingsScope.getSnapshot().value ?? {};
|
|
12717
|
-
return
|
|
13077
|
+
return routingSettingsSignature(value);
|
|
12718
13078
|
});
|
|
12719
13079
|
const [view, setView] = import_react5.default.useState("plan");
|
|
12720
13080
|
const tabRefs = import_react5.default.useRef({});
|
|
@@ -12723,6 +13083,9 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12723
13083
|
const budgetValue = import_react5.default.useRef(budget);
|
|
12724
13084
|
const mounted = import_react5.default.useRef(false);
|
|
12725
13085
|
const catalogRequest = import_react5.default.useRef(0);
|
|
13086
|
+
const generationRequest = import_react5.default.useRef(0);
|
|
13087
|
+
const generationInputs = import_react5.default.useRef("");
|
|
13088
|
+
generationInputs.current = JSON.stringify([task, budget, mode, directKey, routingSettings, catalogState.catalog, toolProbes, workbench.health.report?.tools]);
|
|
12726
13089
|
import_react5.default.useEffect(() => {
|
|
12727
13090
|
if (plan) resultHeading.current?.focus({ preventScroll: true });
|
|
12728
13091
|
}, [plan]);
|
|
@@ -12739,10 +13102,10 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12739
13102
|
import_react5.default.useEffect(() => {
|
|
12740
13103
|
const syncBudget = () => {
|
|
12741
13104
|
const value = settingsScope.getSnapshot().value ?? {};
|
|
12742
|
-
const
|
|
13105
|
+
const signature2 = routingSettingsSignature(value);
|
|
12743
13106
|
setRoutingSettings((previous) => {
|
|
12744
|
-
if (previous ===
|
|
12745
|
-
return
|
|
13107
|
+
if (previous === signature2) return previous;
|
|
13108
|
+
return signature2;
|
|
12746
13109
|
});
|
|
12747
13110
|
if (budgetEdited.current) return;
|
|
12748
13111
|
const next = String(value.budgetUsd ?? 0);
|
|
@@ -12760,6 +13123,14 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12760
13123
|
setPlan(null);
|
|
12761
13124
|
setPlanError("");
|
|
12762
13125
|
}, [routingSettings]);
|
|
13126
|
+
const dataRevision = workbench.ledger.value?.dynamicData?.revision ?? "";
|
|
13127
|
+
const feedbackRevision = workbench.ledger.value?.learning?.revision ?? "";
|
|
13128
|
+
import_react5.default.useEffect(() => {
|
|
13129
|
+
if (plan && plan.workbenchRevision !== `${dataRevision}/${feedbackRevision}`) {
|
|
13130
|
+
setPlan(null);
|
|
13131
|
+
setPlanError("");
|
|
13132
|
+
}
|
|
13133
|
+
}, [dataRevision, feedbackRevision, plan]);
|
|
12763
13134
|
import_react5.default.useEffect(() => {
|
|
12764
13135
|
mounted.current = true;
|
|
12765
13136
|
const request = ++catalogRequest.current;
|
|
@@ -12791,15 +13162,19 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12791
13162
|
}
|
|
12792
13163
|
};
|
|
12793
13164
|
const invalidatePlan = () => {
|
|
13165
|
+
generationRequest.current += 1;
|
|
12794
13166
|
setPlan(null);
|
|
12795
13167
|
setPlanError("");
|
|
12796
13168
|
};
|
|
12797
13169
|
const handleToolProbes = import_react5.default.useCallback((snapshot) => {
|
|
13170
|
+
generationRequest.current += 1;
|
|
12798
13171
|
setToolProbes(snapshot);
|
|
12799
13172
|
setPlan(null);
|
|
12800
13173
|
setPlanError("");
|
|
12801
13174
|
}, []);
|
|
12802
|
-
const generate = () => {
|
|
13175
|
+
const generate = async () => {
|
|
13176
|
+
const request = ++generationRequest.current;
|
|
13177
|
+
const inputs = generationInputs.current;
|
|
12803
13178
|
setPlanError("");
|
|
12804
13179
|
try {
|
|
12805
13180
|
if (!text5(task)) throw new Error("\u8BF7\u5148\u63CF\u8FF0\u4EFB\u52A1\u3002");
|
|
@@ -12808,7 +13183,14 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12808
13183
|
if (!Number.isFinite(parsedBudget) || parsedBudget < 0) throw new Error("\u9884\u7B97\u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E 0 \u7684\u6570\u5B57\u3002");
|
|
12809
13184
|
const direct = mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] : null;
|
|
12810
13185
|
if (mode === "direct" && !direct) throw new Error("\u8BF7\u9009\u62E9\u8981\u76F4\u63A5\u4F7F\u7528\u7684\u6A21\u578B\u3002");
|
|
12811
|
-
|
|
13186
|
+
const signature2 = routingSettingsSignature(settingsScope.getSnapshot().value ?? {});
|
|
13187
|
+
const ledger = await workbench.refreshLedger();
|
|
13188
|
+
if (request !== generationRequest.current || inputs !== generationInputs.current || !mounted.current) return;
|
|
13189
|
+
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");
|
|
13190
|
+
if (signature2 !== routingSettingsSignature(settingsScope.getSnapshot().value ?? {})) throw new Error("\u89C4\u5212\u8BBE\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\u3002");
|
|
13191
|
+
const next = createWorkspacePlan(task, catalogState.catalog, {
|
|
13192
|
+
...ledger.routingData,
|
|
13193
|
+
learning: ledger.learning,
|
|
12812
13194
|
mode,
|
|
12813
13195
|
...direct ? { directProvider: direct.provider, directModel: direct.model } : {},
|
|
12814
13196
|
budgetUsd: parsedBudget,
|
|
@@ -12816,10 +13198,11 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12816
13198
|
installedToolIds: (toolProbes?.probes ?? []).filter((probe) => probe.installed).map((probe) => probe.id),
|
|
12817
13199
|
runnableToolIds: (toolProbes?.readiness ?? []).filter((item) => item.ready).map((item) => item.id),
|
|
12818
13200
|
preset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
|
|
12819
|
-
qualityBiases: workbench.ledger.value?.biases ?? null,
|
|
12820
13201
|
loggedOutToolIds: (workbench.health.report?.tools ?? []).filter((item) => item.installed && item.login?.state === "logged-out").map((item) => item.id)
|
|
12821
|
-
})
|
|
13202
|
+
});
|
|
13203
|
+
setPlan({ ...next, workbenchRevision: `${ledger.dynamicData?.revision ?? ""}/${ledger.learning?.revision ?? ""}` });
|
|
12822
13204
|
} catch (error) {
|
|
13205
|
+
if (request !== generationRequest.current || inputs !== generationInputs.current || !mounted.current) return;
|
|
12823
13206
|
setPlan(null);
|
|
12824
13207
|
setPlanError(text5(error?.message) || "\u65E0\u6CD5\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE\u3002");
|
|
12825
13208
|
}
|
|
@@ -12856,7 +13239,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12856
13239
|
ledger: workbench.ledger.value,
|
|
12857
13240
|
previewRun,
|
|
12858
13241
|
startRun,
|
|
12859
|
-
planningRevision: routingSettings
|
|
13242
|
+
planningRevision: `${routingSettings}/${dataRevision}/${feedbackRevision}`,
|
|
12860
13243
|
directRoute: mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] ?? null : null,
|
|
12861
13244
|
defaultPreset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
|
|
12862
13245
|
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 +13257,8 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
12874
13257
|
onRefresh: () => {
|
|
12875
13258
|
void workbench.refreshLedger();
|
|
12876
13259
|
},
|
|
12877
|
-
onRate: (runId, packageId, rating) => {
|
|
12878
|
-
void workbench.rate(runId, packageId, rating);
|
|
13260
|
+
onRate: (runId, packageId, rating, expectedFinishedAt) => {
|
|
13261
|
+
void workbench.rate(runId, packageId, rating, expectedFinishedAt);
|
|
12879
13262
|
},
|
|
12880
13263
|
onRerun: (runId, packageId, override, choice) => {
|
|
12881
13264
|
void workbench.rerun(runId, packageId, override, choice);
|
|
@@ -12987,7 +13370,13 @@ var optionalRouteText = (value, subject) => {
|
|
|
12987
13370
|
var rateRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RateRequest`, (value) => {
|
|
12988
13371
|
const request = plainObject2(value, "rate request");
|
|
12989
13372
|
if (!["up", "down", "clear"].includes(request.rating)) throw new TypeError("rating must be up, down or clear");
|
|
12990
|
-
|
|
13373
|
+
if (request.expectedFinishedAt !== void 0 && (!Number.isFinite(request.expectedFinishedAt) || request.expectedFinishedAt < 0)) throw new TypeError("expectedFinishedAt must be a non-negative timestamp");
|
|
13374
|
+
return {
|
|
13375
|
+
runId: idText(request.runId, "runId"),
|
|
13376
|
+
packageId: idText(request.packageId, "packageId"),
|
|
13377
|
+
rating: request.rating,
|
|
13378
|
+
...request.expectedFinishedAt === void 0 ? {} : { expectedFinishedAt: request.expectedFinishedAt }
|
|
13379
|
+
};
|
|
12991
13380
|
});
|
|
12992
13381
|
var rerunRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RerunRequest`, (value) => {
|
|
12993
13382
|
const request = plainObject2(value, "rerun request");
|