@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.
@@ -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
- "agentic coding": "code",
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
- return snapshot?.models?.[normalized(model)] ?? null;
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
- function effectivePricing(pricing, cacheReadRatio = 0, cacheWriteRatio = 0) {
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 compareText(left, right) {
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 compareText(routeKey(left.provider, left.model), routeKey(right.provider, right.model));
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) || compareText(left, 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, maxCost, usedRoutes, cacheReadRatio = 0, cacheWriteRatio = 0) {
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 cost = clamp(costScore(row.pricing, maxCost, cacheReadRatio, cacheWriteRatio) / Math.sqrt(reasoning.multiplier.output));
962
- 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 - duplicatePenalty - qualityGap * (task.criticality ?? 0.75) + synthesisPreference;
963
- return { score, floor, qualityGap, ...reasoning };
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 candidatePool(rows, task, weights, maxCost, text6, complexity, cacheReadRatio, cacheWriteRatio) {
996
- const eligibleRows = task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
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, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio),
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) => !source.some((other) => other !== item.row && dominates(other, item.row, task, text6, complexity, cacheReadRatio, cacheWriteRatio)));
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.slice().sort((left, right) => left.cost - right.cost || compareRowsStable(left.row, right.row))[0],
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 ordered = [...frontier, ...essential].filter((item, index, all) => all.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)).slice(0, ROUTING_CANDIDATE_LIMIT);
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, eligibleRows.length - ordered.length)
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 stateSignature(state) {
1020
- return state.assignments.map((assignment) => `${routeKey(assignment.row?.provider, assignment.row?.model)}@${assignment.decision?.reasoningEffort ?? "provider-default"}`).join("|");
1021
- }
1022
- function compareUtilityStates(left, right) {
1023
- return left.relaxedCount - right.relaxedCount || left.qualityShortfall - right.qualityShortfall || right.score - left.score || left.cost - right.cost || left.switches - right.switches || compareText(stateSignature(left), stateSignature(right));
1024
- }
1025
- function compareCostStates(left, right) {
1026
- return left.relaxedCount - right.relaxedCount || left.qualityShortfall - right.qualityShortfall || left.cost - right.cost || right.score - left.score || left.switches - right.switches || compareText(stateSignature(left), stateSignature(right));
1027
- }
1028
- function solveAssignments({ rows, tasks, weights, maxCost, text: text6, complexity, budget, cacheReadRatio, cacheWriteRatio, minimizeCost = false }) {
1029
- const pools = tasks.map((task) => candidatePool(rows, task, weights, maxCost, text6, complexity, cacheReadRatio, cacheWriteRatio));
1030
- if (pools.some((pool) => pool.options.length === 0)) return null;
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
- ...states[0],
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: suffixMinimum[0]
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 quality = clamp((asScore(liveScores?.[taskType]) ?? liveOverall ?? asScore(metadata.quality) ?? 0) + route.qualityBias);
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, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio).score;
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?.qualitySource ?? "unknown",
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?.qualitySource === "unknown" || row === null ? null : Number(qualityForTask(row, task.type).toFixed(3)),
1257
- qualitySource: row?.qualitySource ?? "unknown",
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 ? costBreakdown.reduce((sum, row) => sum + row.estimatedCost, 0) : null;
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?.qualitySource)) && baselineRows.every(({ strongest }) => ["livebench", "route", "user"].includes(strongest?.qualitySource));
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\u5168\u5C40\u7EA6\u675F\u5206\u914D"}\uFF1B\u4EFB\u52A1\u7C7B\u578B\u4E3A ${taskType}\uFF0C\u5DF2\u5BF9 ${String(subtasks.length)} \u4E2A\u5DE5\u4F5C\u5305\u8FDB\u884C Pareto \u526A\u679D\u548C\u6709\u754C\u7EC4\u5408\u641C\u7D22\u3002`;
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, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio);
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.qualitySource, pricingSource: selected.pricingSource },
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
- estimatedCost: totalEstimate === null ? null : Number(totalEstimate.toFixed(6)),
1340
+ // Preserve precision for downstream budget gates; formatting belongs to UI.
1341
+ estimatedCost: totalEstimate,
1292
1342
  costBreakdown,
1293
1343
  optimization: {
1294
- solver: "pareto-pruned quality-constrained beam assignment",
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\u4ECD\u9700\u5B9E\u9645\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",
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/client/catalog.mjs
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 = clean3(group?.id);
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 = clean3(entry?.id);
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) => clean3(item?.id ?? item)).filter(Boolean))] : [];
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: clean3(group.name) || provider,
1666
+ providerName: clean4(group.name) || provider,
1533
1667
  model,
1534
- name: clean3(entry.name) || model,
1668
+ name: clean4(entry.name) || model,
1535
1669
  reasoningKnown: reasoning !== void 0 && reasoning !== null,
1536
1670
  reasoningEfforts: efforts,
1537
- ...clean3(reasoning?.defaultEffort) ? { defaultReasoningEffort: clean3(reasoning.defaultEffort) } : {},
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 routes = applyQualityBiases(applyModelProfiles(
1549
- routesFromModelCatalog(catalog),
1550
- parseModelProfilesJson(options.modelProfilesJson ?? "[]")
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 routeKey3 = route ? profileRouteKey(route) : "";
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) === routeKey3);
1668
- const pending = drafts[routeKey3];
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[routeKey3] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
1676
- return { ...previous, [routeKey3]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
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[routeKey3];
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[routeKey3];
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: routeKey3, onChange: (event) => {
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) !== routeKey3).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: () => {
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.id, item.id, item.rating === 1 ? "clear" : "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(run.id, item.id, item.rating === -1 ? "clear" : "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: () => {
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 signature = inputSignature;
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 && signature === latestSignature.current) setPhase({ kind: "preview", value, request: snapshot, signature });
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 && signature === latestSignature.current) setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002" });
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 signature = phase.signature;
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(signature === latestSignature.current ? { kind: "preview", value, request: snapshot, signature, changed: true } : { kind: "idle" });
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.15.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 JSON.stringify([value.modelProfilesJson, value.routingPreset]);
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 signature = JSON.stringify([value.modelProfilesJson, value.routingPreset]);
13105
+ const signature2 = routingSettingsSignature(value);
12743
13106
  setRoutingSettings((previous) => {
12744
- if (previous === signature) return previous;
12745
- return signature;
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
- setPlan(createWorkspacePlan(task, catalogState.catalog, {
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
- return { runId: idText(request.runId, "runId"), packageId: idText(request.packageId, "packageId"), rating: request.rating };
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");