@ljwei-stak/dsh-model-router 0.15.0 → 0.16.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,101 @@ 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
+ var record = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
1595
+ function applyFeedbackProfile(routes, profile) {
1596
+ if (!Array.isArray(routes) || !record(profile) || typeof profile.enabled !== "boolean") return routes;
1597
+ return routes.map((route) => {
1598
+ if (!record(route)) return route;
1599
+ let base = route;
1600
+ if (Object.hasOwn(route, "preferenceAdjustments") || Object.hasOwn(route, "preferenceAdjustmentMeta")) {
1601
+ base = { ...route };
1602
+ delete base.preferenceAdjustments;
1603
+ delete base.preferenceAdjustmentMeta;
1604
+ }
1605
+ if (profile.enabled !== true || !record(profile.adjustments)) return base;
1606
+ const key = routeKey3(clean3(route.provider), clean3(route.model));
1607
+ if (!Object.hasOwn(profile.adjustments, key)) return base;
1608
+ const value = profile.adjustments[key];
1609
+ if (!record(value)) return base;
1610
+ const limit = finite2(profile.maxAdjustment) && profile.maxAdjustment >= 0 && profile.maxAdjustment <= 0.1 ? profile.maxAdjustment : 0.1;
1611
+ const adjustments = Object.fromEntries(Object.entries(value).filter(([, bias]) => finite2(bias) && Math.abs(bias) <= limit));
1612
+ if (!Object.keys(adjustments).length) return base;
1613
+ return {
1614
+ ...base,
1615
+ preferenceAdjustments: adjustments,
1616
+ preferenceAdjustmentMeta: {
1617
+ policyVersion: profile.policyVersion,
1618
+ revision: profile.revision,
1619
+ scope: profile.scope,
1620
+ feedbackCount: profile.feedbackCount,
1621
+ effectiveWeight: profile.effectiveWeight,
1622
+ updatedAt: profile.updatedAt,
1623
+ halfLifeDays: profile.halfLifeDays,
1624
+ maxAdjustment: profile.maxAdjustment,
1625
+ window: profile.window,
1626
+ cellStats: Object.hasOwn(profile.cellStats ?? {}, key) ? profile.cellStats[key] : {},
1627
+ evaluatedAt: profile.evaluatedAt,
1628
+ decayRefreshMs: profile.decayRefreshMs,
1629
+ revisionSemantics: profile.revisionSemantics
1630
+ }
1631
+ };
1632
+ });
1633
+ }
1634
+
1635
+ // .dsh-plugin/shared/dynamic-data-view.mjs
1636
+ var exactRouteKey = (route) => `${String(route?.provider ?? "")}\0${String(route?.model ?? "")}`;
1637
+ function applyPricingSnapshot(routes, snapshot) {
1638
+ if (snapshot?.kind !== "pricing" || !snapshot.prices || typeof snapshot.prices !== "object") return routes;
1639
+ return (Array.isArray(routes) ? routes : []).map((route) => {
1640
+ if (route?.pricingSource === "user") return route;
1641
+ const entry = snapshot.prices[exactRouteKey(route)];
1642
+ if (!entry) return route;
1643
+ return {
1644
+ ...route,
1645
+ pricing: {
1646
+ input: entry.input,
1647
+ output: entry.output,
1648
+ currency: "USD",
1649
+ ...entry.cacheRead === void 0 ? {} : { cacheRead: entry.cacheRead },
1650
+ ...entry.cacheWrite === void 0 ? {} : { cacheWrite: entry.cacheWrite }
1651
+ },
1652
+ pricingSource: "dynamic",
1653
+ pricingVersion: snapshot.version,
1654
+ snapshotAsOf: entry.asOf
1655
+ };
1656
+ });
1657
+ }
1658
+
1659
+ // .dsh-plugin/client/catalog.mjs
1660
+ var clean4 = (value) => typeof value === "string" ? value.trim() : "";
1514
1661
  function routesFromModelCatalog(catalog) {
1515
1662
  const groups = Array.isArray(catalog?.groups) ? catalog.groups : [];
1516
1663
  const routable = new Set(Array.isArray(catalog?.routableProviders) ? catalog.routableProviders : []);
1517
1664
  const routes = [];
1518
1665
  const seen = /* @__PURE__ */ new Set();
1519
1666
  for (const group of groups) {
1520
- const provider = clean3(group?.id);
1667
+ const provider = clean4(group?.id);
1521
1668
  if (!provider || !routable.has(provider)) continue;
1522
1669
  for (const entry of Array.isArray(group.models) ? group.models : []) {
1523
- const model = clean3(entry?.id);
1670
+ const model = clean4(entry?.id);
1524
1671
  if (!model) continue;
1525
1672
  const key = `${provider}\0${model}`;
1526
1673
  if (seen.has(key)) continue;
1527
1674
  seen.add(key);
1528
1675
  const reasoning = entry?.reasoning;
1529
- const efforts = Array.isArray(reasoning?.efforts) ? [...new Set(reasoning.efforts.map((item) => clean3(item?.id ?? item)).filter(Boolean))] : [];
1676
+ const efforts = Array.isArray(reasoning?.efforts) ? [...new Set(reasoning.efforts.map((item) => clean4(item?.id ?? item)).filter(Boolean))] : [];
1530
1677
  routes.push({
1531
1678
  provider,
1532
- providerName: clean3(group.name) || provider,
1679
+ providerName: clean4(group.name) || provider,
1533
1680
  model,
1534
- name: clean3(entry.name) || model,
1681
+ name: clean4(entry.name) || model,
1535
1682
  reasoningKnown: reasoning !== void 0 && reasoning !== null,
1536
1683
  reasoningEfforts: efforts,
1537
- ...clean3(reasoning?.defaultEffort) ? { defaultReasoningEffort: clean3(reasoning.defaultEffort) } : {},
1684
+ ...clean4(reasoning?.defaultEffort) ? { defaultReasoningEffort: clean4(reasoning.defaultEffort) } : {},
1538
1685
  // modelCatalog deliberately omits modalities; unknown means the Host
1539
1686
  // must still validate image capability before a real request.
1540
1687
  inputModalities: []
@@ -1545,10 +1692,9 @@ function routesFromModelCatalog(catalog) {
1545
1692
  return routes;
1546
1693
  }
1547
1694
  function createWorkspacePlan(task, catalog, options = {}) {
1548
- const routes = applyQualityBiases(applyModelProfiles(
1549
- routesFromModelCatalog(catalog),
1550
- parseModelProfilesJson(options.modelProfilesJson ?? "[]")
1551
- ), options.qualityBiases ?? null);
1695
+ const profiled = applyModelProfiles(routesFromModelCatalog(catalog), parseModelProfilesJson(options.modelProfilesJson ?? "[]"));
1696
+ const priced = applyPricingSnapshot(profiled, options.pricingSnapshot ?? null);
1697
+ const routes = options.learning ? applyFeedbackProfile(priced, options.learning) : applyQualityBiases(priced, options.qualityBiases ?? null);
1552
1698
  return createPlanFromRoutes(task, routes, options);
1553
1699
  }
1554
1700
 
@@ -1575,6 +1721,7 @@ function profileDraft(profile) {
1575
1721
  cacheWrite: profile?.pricing?.cacheWrite === void 0 ? "" : String(profile.pricing.cacheWrite),
1576
1722
  specialties: Array.isArray(profile?.specialties) ? profile.specialties.join(", ") : "",
1577
1723
  cliModel: profile?.cliModel ?? "",
1724
+ benchmarkModel: profile?.benchmarkModel ?? "",
1578
1725
  execution: profile?.execution === "official" || profile?.execution === "api" ? profile.execution : "auto",
1579
1726
  billing: BILLING_MODES.includes(profile?.billing) ? profile.billing : DEFAULT_BILLING_MODE,
1580
1727
  subscription: ["plan-key", "cli-login", "none"].includes(profile?.subscription) ? profile.subscription : "auto",
@@ -1586,6 +1733,8 @@ function profileFromDraft(route, draft) {
1586
1733
  const model = field(route?.model);
1587
1734
  if (!provider || !model) throw new Error("\u8BF7\u5148\u4ECE\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u9009\u62E9\u4E00\u6761\u51C6\u786E\u7684\u6A21\u578B\u8DEF\u7EBF\u3002");
1588
1735
  const profile = { provider, model };
1736
+ const benchmarkModel = field(draft?.benchmarkModel);
1737
+ if (benchmarkModel) profile.benchmarkModel = benchmarkModel;
1589
1738
  const quality = nonnegativeField(draft?.quality, "\u8D28\u91CF\u8BC4\u5206", 100);
1590
1739
  if (quality !== null) profile.quality = quality;
1591
1740
  const input = nonnegativeField(draft?.input, "\u8F93\u5165\u5355\u4EF7");
@@ -1655,7 +1804,7 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
1655
1804
  const [notice, setNotice] = import_react.default.useState(null);
1656
1805
  import_react.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
1657
1806
  const route = routes.find((item) => profileRouteKey(item) === selected) ?? routes[0];
1658
- const routeKey3 = route ? profileRouteKey(route) : "";
1807
+ const routeKey4 = route ? profileRouteKey(route) : "";
1659
1808
  const rawJson = snapshot.value?.modelProfilesJson ?? "[]";
1660
1809
  let profiles = [];
1661
1810
  let loadError = "";
@@ -1664,16 +1813,16 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
1664
1813
  } catch (error) {
1665
1814
  loadError = messageOf(error);
1666
1815
  }
1667
- const saved = profiles.find((item) => profileRouteKey(item) === routeKey3);
1668
- const pending = drafts[routeKey3];
1816
+ const saved = profiles.find((item) => profileRouteKey(item) === routeKey4);
1817
+ const pending = drafts[routeKey4];
1669
1818
  const draft = pending?.fields ?? profileDraft(saved);
1670
1819
  const writable = snapshot.status === "ready" && snapshot.writable === true && !saving && !loadError;
1671
1820
  const unmatched = profiles.filter((item) => !routes.some((route2) => profileRouteKey(route2) === profileRouteKey(item))).length;
1672
1821
  const edit = (name, value) => {
1673
1822
  if (!route || !writable) return;
1674
1823
  setDrafts((previous) => {
1675
- const previousEntry = previous[routeKey3] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
1676
- return { ...previous, [routeKey3]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
1824
+ const previousEntry = previous[routeKey4] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
1825
+ return { ...previous, [routeKey4]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
1677
1826
  });
1678
1827
  setNotice(null);
1679
1828
  };
@@ -1698,7 +1847,7 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
1698
1847
  if (!accepted) throw new Error("\u8BBE\u7F6E\u672A\u88AB\u4FDD\u5B58\uFF0C\u53EF\u80FD\u88AB\u5176\u4ED6\u9875\u9762\u4FEE\u6539\u3002\u8F93\u5165\u5DF2\u4FDD\u7559\uFF0C\u8BF7\u91CD\u65B0\u52A0\u8F7D\u540E\u6838\u5BF9\u3002");
1699
1848
  setDrafts((previous) => {
1700
1849
  const next = { ...previous };
1701
- delete next[routeKey3];
1850
+ delete next[routeKey4];
1702
1851
  return next;
1703
1852
  });
1704
1853
  setNotice({ tone: "success", text: remove ? "\u5DF2\u5220\u9664\u8BE5\u6A21\u578B\u7684\u81EA\u62A5\u914D\u7F6E\u3002" : "\u8BE5\u6A21\u578B\u914D\u7F6E\u5DF2\u4FDD\u5B58\uFF1B\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\u5373\u53EF\u4F7F\u7528\u3002" });
@@ -1712,15 +1861,15 @@ function ModelProfileEditor({ routes, settingsScope, onSaved }) {
1712
1861
  const reload = () => {
1713
1862
  setDrafts((previous) => {
1714
1863
  const next = { ...previous };
1715
- delete next[routeKey3];
1864
+ delete next[routeKey4];
1716
1865
  return next;
1717
1866
  });
1718
1867
  setNotice(null);
1719
1868
  };
1720
- return /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card mr-profile-card", "aria-label": "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u9009\u62E9\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E\u8DEF\u7EBF\uFF0C\u586B\u5199\u4F60\u638C\u63E1\u7684\u8D28\u91CF\u8BC4\u5206\u4E0E\u5355\u4EF7\u3002\u914D\u7F6E\u7531\u4F60\u63D0\u4F9B\uFF0C\u63D2\u4EF6\u4E0D\u4F1A\u8BFB\u53D6\u8D26\u53F7\u5BC6\u94A5\u3002"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, loadError && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "alert" }, "\u5DF2\u6709\u914D\u7F6E\u65E0\u6CD5\u89E3\u6790\uFF1A", loadError, "\u3002\u8BF7\u5148\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u4FEE\u6B63 JSON\u3002"), snapshot.status !== "ready" && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u8BBE\u7F6E\u72B6\u6001\uFF1A", snapshot.status === "loading" ? "\u6B63\u5728\u52A0\u8F7D" : "\u5F53\u524D\u4E0D\u53EF\u7528"), snapshot.status === "ready" && !snapshot.writable && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "status" }, "\u5F53\u524D\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\uFF0C\u8BF7\u5728\u53EF\u5199\u7684\u672C\u673A\u73AF\u5883\u914D\u7F6E\u3002"), routes.length === 0 ? /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-empty" }, "\u8BF7\u5148\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\uFF0C\u518D\u8FD4\u56DE\u8FD9\u91CC\u586B\u5199\u4EF7\u683C\u4E0E\u80FD\u529B\u3002") : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-label", htmlFor: "mr-profile-route" }, "\u6A21\u578B\u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", id: "mr-profile-route", value: routeKey3, onChange: (event) => {
1869
+ return /* @__PURE__ */ import_react.default.createElement("section", { className: "mr-card mr-profile-card", "aria-label": "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react.default.createElement("div", null, /* @__PURE__ */ import_react.default.createElement("h2", { className: "mr-card-title" }, "\u9010\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B"), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-card-copy" }, "\u9009\u62E9\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E\u8DEF\u7EBF\uFF0C\u586B\u5199\u4F60\u638C\u63E1\u7684\u8D28\u91CF\u8BC4\u5206\u4E0E\u5355\u4EF7\u3002\u914D\u7F6E\u7531\u4F60\u63D0\u4F9B\uFF0C\u63D2\u4EF6\u4E0D\u4F1A\u8BFB\u53D6\u8D26\u53F7\u5BC6\u94A5\u3002"))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-card-body" }, loadError && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "alert" }, "\u5DF2\u6709\u914D\u7F6E\u65E0\u6CD5\u89E3\u6790\uFF1A", loadError, "\u3002\u8BF7\u5148\u5728\u63D2\u4EF6\u8BBE\u7F6E\u9875\u4FEE\u6B63 JSON\u3002"), snapshot.status !== "ready" && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u8BBE\u7F6E\u72B6\u6001\uFF1A", snapshot.status === "loading" ? "\u6B63\u5728\u52A0\u8F7D" : "\u5F53\u524D\u4E0D\u53EF\u7528"), snapshot.status === "ready" && !snapshot.writable && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-error", role: "status" }, "\u5F53\u524D\u8BBE\u7F6E\u4E3A\u53EA\u8BFB\uFF0C\u8BF7\u5728\u53EF\u5199\u7684\u672C\u673A\u73AF\u5883\u914D\u7F6E\u3002"), routes.length === 0 ? /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-empty" }, "\u8BF7\u5148\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u6DFB\u52A0\u6A21\u578B\uFF0C\u518D\u8FD4\u56DE\u8FD9\u91CC\u586B\u5199\u4EF7\u683C\u4E0E\u80FD\u529B\u3002") : /* @__PURE__ */ import_react.default.createElement(import_react.default.Fragment, null, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-label", htmlFor: "mr-profile-route" }, "\u6A21\u578B\u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", id: "mr-profile-route", value: routeKey4, onChange: (event) => {
1721
1870
  setSelected(event.target.value);
1722
1871
  setNotice(null);
1723
- } }, routes.map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: profileRouteKey(item), value: profileRouteKey(item) }, item.provider, "/", item.model))), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption mr-profile-state" }, saved ? `\u5DF2\u914D\u7F6E${saved.pricing ? "\u5355\u4EF7" : "\u80FD\u529B\uFF0C\u4EF7\u683C\u672A\u77E5"}` : "\u672A\u914D\u7F6E\uFF0C\u4EF7\u683C\u4E0E\u8D28\u91CF\u6765\u6E90\u672A\u77E5", pending ? " \xB7 \u5F53\u524D\u6709\u672A\u4FDD\u5B58\u8F93\u5165" : "", unmatched > 0 ? ` \xB7 \u53E6\u6709 ${unmatched} \u6761\u914D\u7F6E\u4E0D\u5728\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u4E2D\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u4FDD\u7559` : ""), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8D28\u91CF\u8BC4\u5206\uFF080\u2013100\uFF0C\u81EA\u62A5\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", max: "100", step: "any", value: draft.quality, disabled: !writable, onChange: (event) => edit("quality", event.target.value), placeholder: "\u4F8B\u5982 85\uFF1B\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u5165\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.input, disabled: !writable, onChange: (event) => edit("input", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u51FA\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.output, disabled: !writable, onChange: (event) => edit("output", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u64C5\u957F\u65B9\u5411\uFF08\u82F1\u6587\u6807\u7B7E\uFF0C\u9017\u53F7\u5206\u9694\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.specialties, disabled: !writable, onChange: (event) => edit("specialties", event.target.value), placeholder: PROFILE_SPECIALTY_HINT })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u5B98\u65B9 CLI \u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.cliModel, disabled: !writable, onChange: (event) => edit("cliModel", event.target.value), placeholder: "\u4EC5\u5728\u5382\u5546 CLI \u652F\u6301\u8BE5\u51C6\u786E\u540D\u79F0\u65F6\u586B\u5199" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u6267\u884C\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.execution || "auto", disabled: !writable, onChange: (event) => edit("execution", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u5DF2\u5B89\u88C5\u5219\u7528\u5B98\u65B9\u5DE5\u5177\uFF0C\u5931\u8D25\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "official" }, "\u5B98\u65B9\u5DE5\u5177\uFF1A\u5931\u8D25\u6216\u672A\u5B89\u88C5\u65F6\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api" }, "\u4EC5\u6A21\u578B\u76EE\u5F55 API"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA1\u8D39\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.billing, disabled: !writable, onChange: (event) => edit("billing", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-first" }, "\u8BA2\u9605\u4F18\u5148\uFF1A\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u65F6\u5207\u6362 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api-only" }, "\u53EA\u7528 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-only" }, "\u53EA\u7528\u8BA2\u9605\uFF1A\u989D\u5EA6\u7528\u5C3D\u65F6\u4E0D\u5207\u6362"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA2\u9605\u6765\u6E90"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.subscription, disabled: !writable, onChange: (event) => edit("subscription", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u6709\u5B98\u65B9 CLI \u65F6\u7528 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "plan-key" }, "\u7F16\u7A0B\u5957\u9910 Key\uFF1A\u8BE5\u8DEF\u7EBF\u672C\u8EAB\u662F\u5957\u9910\u7AEF\u70B9"), /* @__PURE__ */ import_react.default.createElement("option", { value: "cli-login" }, "\u5B98\u65B9 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "none" }, "\u65E0\u8BA2\u9605\uFF1A\u59CB\u7EC8\u6309 API \u8BA1\u8D39"))), draft.subscription === "plan-key" && /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u989D\u5EA6\u7528\u5C3D\u65F6\u56DE\u9000\u7684 API \u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.apiRoute, disabled: !writable, onChange: (event) => edit("apiRoute", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "" }, "\u4E0D\u56DE\u9000\uFF08\u672A\u914D\u7F6E API \u8DEF\u7EBF\uFF09"), routes.filter((item) => profileRouteKey(item) !== 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: () => {
1872
+ } }, routes.map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: profileRouteKey(item), value: profileRouteKey(item) }, item.provider, "/", item.model))), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption mr-profile-state" }, saved ? `\u5DF2\u914D\u7F6E${saved.pricing ? "\u5355\u4EF7" : "\u80FD\u529B\uFF0C\u4EF7\u683C\u672A\u77E5"}` : "\u672A\u914D\u7F6E\uFF0C\u4EF7\u683C\u4E0E\u8D28\u91CF\u6765\u6E90\u672A\u77E5", pending ? " \xB7 \u5F53\u524D\u6709\u672A\u4FDD\u5B58\u8F93\u5165" : "", unmatched > 0 ? ` \xB7 \u53E6\u6709 ${unmatched} \u6761\u914D\u7F6E\u4E0D\u5728\u5F53\u524D\u6A21\u578B\u76EE\u5F55\u4E2D\uFF0C\u4FDD\u5B58\u65F6\u4F1A\u4FDD\u7559` : ""), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8D28\u91CF\u8BC4\u5206\uFF080\u2013100\uFF0C\u81EA\u62A5\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", max: "100", step: "any", value: draft.quality, disabled: !writable, onChange: (event) => edit("quality", event.target.value), placeholder: "\u4F8B\u5982 85\uFF1B\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u5165\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.input, disabled: !writable, onChange: (event) => edit("input", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8F93\u51FA\u5355\u4EF7\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.output, disabled: !writable, onChange: (event) => edit("output", event.target.value), placeholder: "\u7559\u7A7A\u8868\u793A\u672A\u77E5" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u64C5\u957F\u65B9\u5411\uFF08\u82F1\u6587\u6807\u7B7E\uFF0C\u9017\u53F7\u5206\u9694\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.specialties, disabled: !writable, onChange: (event) => edit("specialties", event.target.value), placeholder: PROFILE_SPECIALTY_HINT })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u5B98\u65B9 CLI \u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.cliModel, disabled: !writable, onChange: (event) => edit("cliModel", event.target.value), placeholder: "\u4EC5\u5728\u5382\u5546 CLI \u652F\u6301\u8BE5\u51C6\u786E\u540D\u79F0\u65F6\u586B\u5199" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "LiveBench \u51C6\u786E\u6A21\u578B\u540D\u79F0\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "text", value: draft.benchmarkModel ?? "", disabled: !writable, onChange: (event) => edit("benchmarkModel", event.target.value), placeholder: "\u786E\u8BA4\u7248\u672C\u4E0E\u63A8\u7406\u6863\u4F4D\u4E00\u81F4\uFF1B\u4E0D\u81EA\u52A8\u731C\u6D4B\u522B\u540D" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u6267\u884C\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.execution || "auto", disabled: !writable, onChange: (event) => edit("execution", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u5DF2\u5B89\u88C5\u5219\u7528\u5B98\u65B9\u5DE5\u5177\uFF0C\u5931\u8D25\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "official" }, "\u5B98\u65B9\u5DE5\u5177\uFF1A\u5931\u8D25\u6216\u672A\u5B89\u88C5\u65F6\u56DE\u9000 API"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api" }, "\u4EC5\u6A21\u578B\u76EE\u5F55 API"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA1\u8D39\u65B9\u5F0F"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.billing, disabled: !writable, onChange: (event) => edit("billing", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-first" }, "\u8BA2\u9605\u4F18\u5148\uFF1A\u989D\u5EA6\u7528\u5C3D\u6216\u9650\u6D41\u65F6\u5207\u6362 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "api-only" }, "\u53EA\u7528 API Key"), /* @__PURE__ */ import_react.default.createElement("option", { value: "subscription-only" }, "\u53EA\u7528\u8BA2\u9605\uFF1A\u989D\u5EA6\u7528\u5C3D\u65F6\u4E0D\u5207\u6362"))), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u8BA2\u9605\u6765\u6E90"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.subscription, disabled: !writable, onChange: (event) => edit("subscription", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "auto" }, "\u81EA\u52A8\uFF1A\u6709\u5B98\u65B9 CLI \u65F6\u7528 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "plan-key" }, "\u7F16\u7A0B\u5957\u9910 Key\uFF1A\u8BE5\u8DEF\u7EBF\u672C\u8EAB\u662F\u5957\u9910\u7AEF\u70B9"), /* @__PURE__ */ import_react.default.createElement("option", { value: "cli-login" }, "\u5B98\u65B9 CLI \u8D26\u53F7\u767B\u5F55"), /* @__PURE__ */ import_react.default.createElement("option", { value: "none" }, "\u65E0\u8BA2\u9605\uFF1A\u59CB\u7EC8\u6309 API \u8BA1\u8D39"))), draft.subscription === "plan-key" && /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u989D\u5EA6\u7528\u5C3D\u65F6\u56DE\u9000\u7684 API \u8DEF\u7EBF"), /* @__PURE__ */ import_react.default.createElement("select", { className: "mr-input", value: draft.apiRoute, disabled: !writable, onChange: (event) => edit("apiRoute", event.target.value) }, /* @__PURE__ */ import_react.default.createElement("option", { value: "" }, "\u4E0D\u56DE\u9000\uFF08\u672A\u914D\u7F6E API \u8DEF\u7EBF\uFF09"), routes.filter((item) => profileRouteKey(item) !== routeKey4).map((item) => /* @__PURE__ */ import_react.default.createElement("option", { key: profileRouteKey(item), value: profileRouteKey(item) }, item.provider, "/", item.model))))), /* @__PURE__ */ import_react.default.createElement("details", { className: "mr-profile-advanced" }, /* @__PURE__ */ import_react.default.createElement("summary", null, "\u7F13\u5B58\u5355\u4EF7\uFF08\u53EF\u9009\uFF09"), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-profile-grid" }, /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u7F13\u5B58\u8BFB\u53D6\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.cacheRead, disabled: !writable, onChange: (event) => edit("cacheRead", event.target.value), placeholder: "\u7559\u7A7A\u6309\u666E\u901A\u8F93\u5165\u4EF7\u683C\u4F30\u7B97" })), /* @__PURE__ */ import_react.default.createElement("label", { className: "mr-profile-field" }, /* @__PURE__ */ import_react.default.createElement("span", null, "\u7F13\u5B58\u5199\u5165\uFF08USD / \u767E\u4E07 token\uFF09"), /* @__PURE__ */ import_react.default.createElement("input", { className: "mr-input", type: "number", min: "0", step: "any", value: draft.cacheWrite, disabled: !writable, onChange: (event) => edit("cacheWrite", event.target.value), placeholder: "\u7559\u7A7A\u6309\u666E\u901A\u8F93\u5165\u4EF7\u683C\u4F30\u7B97" })))), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button", type: "button", disabled: !writable || !pending, onClick: () => {
1724
1873
  void write(false);
1725
1874
  } }, saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58\u6B64\u6A21\u578B"), /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !pending || saving, onClick: reload }, "\u91CD\u65B0\u52A0\u8F7D\u6B64\u8DEF\u7EBF"), saved && /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable, onClick: () => {
1726
1875
  void write(true);
@@ -1978,6 +2127,147 @@ function rerunConfirmations({ run, item, override = null, choice = null, ledger
1978
2127
  return reasons;
1979
2128
  }
1980
2129
 
2130
+ // .dsh-plugin/client/adaptive-state.mjs
2131
+ var ADAPTIVE_DEFAULTS = Object.freeze({
2132
+ feedbackLearningEnabled: true,
2133
+ feedbackHalfLifeDays: 30,
2134
+ feedbackPriorWeight: 3,
2135
+ feedbackMaxAdjustment: 0.04,
2136
+ feedbackResetAt: 0,
2137
+ dynamicDataEnabled: false,
2138
+ liveBenchEndpoint: "https://livebench.ai",
2139
+ dynamicDataTtlMinutes: 1440,
2140
+ pricingSnapshotEndpoint: ""
2141
+ });
2142
+ var ADAPTIVE_NUMBER_FIELDS = Object.freeze([
2143
+ Object.freeze({ key: "feedbackHalfLifeDays", min: 1, max: 3650, step: 1 }),
2144
+ Object.freeze({ key: "feedbackPriorWeight", min: 1, max: 1e6, step: 1 }),
2145
+ Object.freeze({ key: "feedbackMaxAdjustment", min: 0, max: 0.1, step: 0.01 }),
2146
+ Object.freeze({ key: "dynamicDataTtlMinutes", min: 1, max: 10080, step: 1 })
2147
+ ]);
2148
+ function adaptiveSettingValue(settings, key) {
2149
+ return settings?.[key] ?? ADAPTIVE_DEFAULTS[key];
2150
+ }
2151
+ function parseAdaptiveNumber(key, draft) {
2152
+ const field2 = ADAPTIVE_NUMBER_FIELDS.find((item) => item.key === key);
2153
+ if (!field2) throw new TypeError("Unknown numeric setting");
2154
+ if (typeof draft !== "string" && typeof draft !== "number" || String(draft).trim() === "") throw new TypeError("Enter a number");
2155
+ const value = Number(draft);
2156
+ if (!Number.isFinite(value) || value < field2.min || value > field2.max) throw new RangeError(`Expected ${field2.min}..${field2.max}`);
2157
+ return value;
2158
+ }
2159
+ function parsePublicEndpoint(key, draft) {
2160
+ if (!["liveBenchEndpoint", "pricingSnapshotEndpoint"].includes(key)) throw new TypeError("Unknown endpoint setting");
2161
+ if (typeof draft !== "string") throw new TypeError("Enter a public HTTPS URL");
2162
+ const value = draft.trim();
2163
+ if (key === "pricingSnapshotEndpoint" && value === "") return "";
2164
+ if (!value || value.length > 2048) throw new TypeError("Enter a public HTTPS URL");
2165
+ let parsed;
2166
+ try {
2167
+ parsed = new URL(value);
2168
+ } catch {
2169
+ throw new TypeError("Enter a public HTTPS URL");
2170
+ }
2171
+ if (parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.search || parsed.hash || parsed.port && parsed.port !== "443") {
2172
+ throw new TypeError("Use a public HTTPS URL without credentials, query or fragment");
2173
+ }
2174
+ if (!/^(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z]{2,}$/iu.test(parsed.hostname) || /(?:^|\.)(?:localhost|local|internal|lan|home|test|invalid)$/iu.test(parsed.hostname)) throw new TypeError("Use a public hostname");
2175
+ return value;
2176
+ }
2177
+ function publicEndpointDraft(key, value) {
2178
+ try {
2179
+ return parsePublicEndpoint(key, value);
2180
+ } catch {
2181
+ return "";
2182
+ }
2183
+ }
2184
+ function feedbackRatingArguments(run, item, direction) {
2185
+ if (!["up", "down"].includes(direction)) throw new TypeError("Unknown rating direction");
2186
+ const active = direction === "up" ? item?.rating === 1 : item?.rating === -1;
2187
+ return [run?.id, item?.id, active ? "clear" : direction, item?.finishedAt];
2188
+ }
2189
+ var nonnegative2 = (value) => Number.isFinite(value) && value >= 0 ? value : 0;
2190
+ function learningView(learning, settings = {}) {
2191
+ return {
2192
+ available: Boolean(learning && typeof learning === "object" && !Array.isArray(learning)),
2193
+ // Current settings govern the next plan; a failed refresh must not make an
2194
+ // older profile claim that a newly disabled policy is still enabled.
2195
+ enabled: adaptiveSettingValue(settings, "feedbackLearningEnabled") !== false,
2196
+ feedbackCount: nonnegative2(learning?.feedbackCount),
2197
+ ignoredCount: nonnegative2(learning?.ignoredCount),
2198
+ effectiveWeight: nonnegative2(learning?.effectiveWeight),
2199
+ policyVersion: typeof learning?.policyVersion === "string" ? learning.policyVersion.slice(0, 100) : null,
2200
+ resetAt: nonnegative2(adaptiveSettingValue(settings, "feedbackResetAt"))
2201
+ };
2202
+ }
2203
+ function learningEvidenceRows(learning, limit = 12) {
2204
+ const cells = learning?.cellStats;
2205
+ if (!cells || typeof cells !== "object" || Array.isArray(cells)) return { rows: [], total: 0 };
2206
+ const rows = [];
2207
+ for (const [key, domains] of Object.entries(cells)) {
2208
+ const identity = key.split("\0");
2209
+ if (identity.length !== 2 || identity.some((part) => !part.trim()) || !domains || typeof domains !== "object" || Array.isArray(domains)) continue;
2210
+ for (const [domain, cell] of Object.entries(domains)) {
2211
+ if (!domain.trim() || !cell || typeof cell !== "object" || Array.isArray(cell) || !Number.isFinite(cell.adjustment) || Math.abs(cell.adjustment) > 0.1 || ["feedbackCount", "positiveCount", "negativeCount", "effectiveWeight", "effectiveSampleSize"].some((name) => !Number.isFinite(cell[name]) || cell[name] < 0) || !Number.isFinite(cell.shrinkage) || cell.shrinkage < 0 || cell.shrinkage > 1) continue;
2212
+ rows.push({
2213
+ id: JSON.stringify([key, domain]),
2214
+ route: identity.map((part) => part.slice(0, 100)).join("/"),
2215
+ domain: domain.slice(0, 80),
2216
+ feedbackCount: nonnegative2(cell.feedbackCount),
2217
+ positiveCount: nonnegative2(cell.positiveCount),
2218
+ negativeCount: nonnegative2(cell.negativeCount),
2219
+ effectiveWeight: nonnegative2(cell.effectiveWeight),
2220
+ effectiveSampleSize: nonnegative2(cell.effectiveSampleSize),
2221
+ shrinkage: Number.isFinite(cell.shrinkage) && cell.shrinkage >= 0 && cell.shrinkage <= 1 ? cell.shrinkage : 0,
2222
+ adjustment: cell.adjustment
2223
+ });
2224
+ }
2225
+ }
2226
+ rows.sort((left, right) => left.route === right.route ? left.domain.localeCompare(right.domain) : left.route.localeCompare(right.route));
2227
+ const count = Number.isInteger(limit) && limit > 0 ? Math.min(limit, 50) : 12;
2228
+ return { rows: rows.slice(0, count), total: rows.length };
2229
+ }
2230
+ var EXCLUSION_LABELS = Object.freeze({
2231
+ "unrated": "\u5C1A\u672A\u8BC4\u4EF7\uFF08\u4E0D\u662F\u4E0D\u6EE1\u610F\uFF09",
2232
+ "not-successful": "\u6CA1\u6709\u6210\u529F\u6267\u884C\u7ED3\u679C",
2233
+ "cli-model-unverified": "CLI \u5B9E\u9645\u6A21\u578B\u672A\u6838\u9A8C",
2234
+ "before-reset": "\u7ED3\u679C\u65E9\u4E8E\u5B66\u4E60\u8D77\u70B9",
2235
+ "superseded": "\u5DF2\u88AB\u540C\u4E00\u7ED3\u679C\u7684\u65B0\u8BB0\u5F55\u66FF\u4EE3",
2236
+ "missing-identity": "\u7F3A\u5C11\u8FD0\u884C\u6216\u6B65\u9AA4\u6807\u8BC6",
2237
+ "invalid-route": "\u8DEF\u7EBF\u6807\u8BC6\u65E0\u6548",
2238
+ "invalid-time": "\u7ED3\u679C\u6216\u8BC4\u4EF7\u65F6\u95F4\u65E0\u6548",
2239
+ "future-time": "\u65F6\u95F4\u665A\u4E8E\u5F53\u524D\u65F6\u523B"
2240
+ });
2241
+ function learningExclusionRows(learning) {
2242
+ return Object.entries(EXCLUSION_LABELS).flatMap(([reason, label]) => {
2243
+ const count = nonnegative2(learning?.exclusionReasons?.[reason]);
2244
+ return count > 0 ? [{ reason, label, count }] : [];
2245
+ });
2246
+ }
2247
+ var STATUS_LABEL = Object.freeze({ disabled: "\u672A\u542F\u7528", missing: "\u5C1A\u65E0\u5FEB\u7167", fresh: "\u5FEB\u7167\u6709\u6548", stale: "\u5FEB\u7167\u8FC7\u671F", error: "\u66F4\u65B0\u5931\u8D25" });
2248
+ var PUBLIC_ERROR_CODES = /* @__PURE__ */ new Set(["refresh-failed", "snapshot-rollback-rejected", "storage-failed", "refresh-interrupted"]);
2249
+ var positiveTime = (value) => Number.isFinite(value) && value > 0 ? value : null;
2250
+ function dynamicSourceView(source, enabled = true) {
2251
+ const status = enabled === false ? "disabled" : Object.hasOwn(STATUS_LABEL, source?.status ?? "") ? source.status : "missing";
2252
+ let publicSource = "";
2253
+ if (enabled !== false && typeof source?.source === "string") publicSource = publicEndpointDraft("pricingSnapshotEndpoint", source.source);
2254
+ return {
2255
+ status,
2256
+ label: STATUS_LABEL[status],
2257
+ source: publicSource,
2258
+ version: enabled !== false && typeof source?.version === "string" ? source.version.slice(0, 160) : null,
2259
+ verifiedAt: enabled !== false && Number.isFinite(source?.verifiedAt) && source.verifiedAt > 0 ? source.verifiedAt : null,
2260
+ count: enabled === false ? 0 : nonnegative2(source?.modelCount ?? source?.rowCount),
2261
+ refreshing: enabled !== false && source?.refreshing === true,
2262
+ pending: enabled !== false && source?.pending === true,
2263
+ lastAttemptAt: enabled === false ? null : positiveTime(source?.lastAttemptAt),
2264
+ lastSuccessAt: enabled === false ? null : positiveTime(source?.lastSuccessAt),
2265
+ errorCode: enabled !== false && PUBLIC_ERROR_CODES.has(source?.error) ? source.error : "",
2266
+ endpoint: enabled === false ? "" : publicEndpointDraft("pricingSnapshotEndpoint", source?.configuredEndpoint ?? source?.endpoint ?? ""),
2267
+ lastGoodEndpoint: enabled === false ? "" : publicEndpointDraft("pricingSnapshotEndpoint", source?.lastGoodEndpoint ?? "")
2268
+ };
2269
+ }
2270
+
1981
2271
  // .dsh-plugin/client/router-insights.jsx
1982
2272
  var text = (value) => typeof value === "string" ? value.trim() : "";
1983
2273
  var BAND = { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u56F0\u96BE" };
@@ -2012,23 +2302,195 @@ function Meter({ label, meter }) {
2012
2302
  function useSettingsWriter(settingsScope) {
2013
2303
  const [snapshot, setSnapshot] = import_react2.default.useState(() => settingsScope.getSnapshot());
2014
2304
  const [notice, setNotice] = import_react2.default.useState("");
2305
+ const [busy, setBusy] = import_react2.default.useState(false);
2306
+ const pending = import_react2.default.useRef(false);
2307
+ const mounted = import_react2.default.useRef(true);
2308
+ import_react2.default.useEffect(() => {
2309
+ mounted.current = true;
2310
+ return () => {
2311
+ mounted.current = false;
2312
+ };
2313
+ }, []);
2015
2314
  import_react2.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
2016
- const writable = snapshot.status === "ready" && snapshot.writable === true;
2315
+ const writable = snapshot.status === "ready" && snapshot.writable === true && !busy;
2017
2316
  const write = async (key, value) => {
2018
- if (!writable) return false;
2317
+ const current = settingsScope.getSnapshot();
2318
+ if (pending.current || current.status !== "ready" || current.writable !== true) return false;
2319
+ pending.current = true;
2320
+ setBusy(true);
2019
2321
  setNotice("");
2020
2322
  try {
2021
- const accepted = await settingsScope.mutate([{ op: "set", path: [key], value }], snapshot.revision);
2022
- if (!accepted) setNotice("\u8BBE\u7F6E\u672A\u4FDD\u5B58\uFF0C\u53EF\u80FD\u5DF2\u5728\u5176\u4ED6\u9875\u9762\u4FEE\u6539\uFF0C\u8BF7\u91CD\u8BD5\u3002");
2323
+ const accepted = await settingsScope.mutate([{ op: "set", path: [key], value }], current.revision);
2324
+ if (!accepted && mounted.current) setNotice("\u8BBE\u7F6E\u672A\u4FDD\u5B58\uFF0C\u53EF\u80FD\u5DF2\u5728\u5176\u4ED6\u9875\u9762\u4FEE\u6539\uFF0C\u8BF7\u91CD\u8BD5\u3002");
2023
2325
  return accepted;
2024
2326
  } catch (error) {
2025
- setNotice(text(error?.message) || "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
2327
+ if (mounted.current) setNotice(text(error?.message) || "\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002");
2026
2328
  return false;
2329
+ } finally {
2330
+ pending.current = false;
2331
+ if (mounted.current) setBusy(false);
2027
2332
  }
2028
2333
  };
2029
- return { value: snapshot.value ?? {}, writable, write, notice };
2334
+ return { value: snapshot.value ?? {}, writable, busy, write, notice };
2030
2335
  }
2031
- function CostControlCard({ ledger, settingsScope, onChanged, error }) {
2336
+ var ADAPTIVE_FIELD_LABEL = {
2337
+ feedbackHalfLifeDays: "\u53CD\u9988\u534A\u8870\u671F\uFF08\u5929\uFF09",
2338
+ feedbackPriorWeight: "\u6536\u7F29\u5F3A\u5EA6",
2339
+ feedbackMaxAdjustment: "\u6700\u5927\u504F\u597D\u6548\u7528\u8C03\u6574",
2340
+ dynamicDataTtlMinutes: "\u516C\u5171\u5FEB\u7167\u68C0\u67E5\u95F4\u9694\uFF08\u5206\u949F\uFF09"
2341
+ };
2342
+ function AdaptiveNumberControl({ field: field2, value, writable, onSave }) {
2343
+ const [draft, setDraft] = import_react2.default.useState("");
2344
+ const [notice, setNotice] = import_react2.default.useState("");
2345
+ const save = async () => {
2346
+ let next;
2347
+ try {
2348
+ next = parseAdaptiveNumber(field2.key, draft);
2349
+ } catch {
2350
+ setNotice(`\u8BF7\u8F93\u5165 ${field2.min} \u5230 ${field2.max} \u7684\u6570\u5B57\u3002`);
2351
+ return;
2352
+ }
2353
+ setNotice("");
2354
+ try {
2355
+ if (await onSave(field2.key, next)) setDraft((current) => current === draft ? "" : current);
2356
+ } catch {
2357
+ setNotice("\u8BBE\u7F6E\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
2358
+ }
2359
+ };
2360
+ return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: `mr-${field2.key}` }, ADAPTIVE_FIELD_LABEL[field2.key]), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement(
2361
+ "input",
2362
+ {
2363
+ className: "mr-input mr-budget",
2364
+ id: `mr-${field2.key}`,
2365
+ type: "number",
2366
+ min: field2.min,
2367
+ max: field2.max,
2368
+ step: field2.step,
2369
+ placeholder: String(value),
2370
+ value: draft,
2371
+ disabled: !writable,
2372
+ onChange: (event) => setDraft(event.target.value)
2373
+ }
2374
+ ), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable || draft === "", onClick: () => {
2375
+ void save();
2376
+ } }, "\u4FDD\u5B58")), notice && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-error", role: "alert" }, notice));
2377
+ }
2378
+ function PublicEndpointControl({ name, label, value, writable, onSave }) {
2379
+ const [draft, setDraft] = import_react2.default.useState(null);
2380
+ const [notice, setNotice] = import_react2.default.useState("");
2381
+ const current = publicEndpointDraft(name, value);
2382
+ const save = async () => {
2383
+ let next;
2384
+ try {
2385
+ next = parsePublicEndpoint(name, draft ?? current);
2386
+ } catch {
2387
+ setNotice("\u8BF7\u8F93\u5165\u4E0D\u542B\u8BA4\u8BC1\u3001\u67E5\u8BE2\u53C2\u6570\u6216\u7247\u6BB5\u7684\u516C\u5F00 HTTPS \u5730\u5740\uFF1B\u4EF7\u683C\u5FEB\u7167\u53EF\u7559\u7A7A\u3002");
2388
+ return;
2389
+ }
2390
+ setNotice("");
2391
+ try {
2392
+ if (await onSave(name, next)) setDraft((currentDraft) => currentDraft === draft ? null : currentDraft);
2393
+ } catch {
2394
+ setNotice("\u5730\u5740\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
2395
+ }
2396
+ };
2397
+ return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: `mr-${name}` }, label), /* @__PURE__ */ import_react2.default.createElement(
2398
+ "input",
2399
+ {
2400
+ className: "mr-input",
2401
+ id: `mr-${name}`,
2402
+ type: "url",
2403
+ maxLength: 2048,
2404
+ value: draft ?? current,
2405
+ disabled: !writable,
2406
+ placeholder: name === "liveBenchEndpoint" ? "https://livebench.ai" : "\u7559\u7A7A\uFF1A\u4E0D\u8BFB\u53D6\u8FDC\u7A0B\u4EF7\u683C\u5FEB\u7167",
2407
+ onChange: (event) => setDraft(event.target.value)
2408
+ }
2409
+ ), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !writable || draft === null, onClick: () => {
2410
+ void save();
2411
+ } }, "\u4FDD\u5B58\u5730\u5740"), notice && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-error", role: "alert" }, notice));
2412
+ }
2413
+ function PublicSourceStatus({ label, source, enabled }) {
2414
+ const view = dynamicSourceView(source, enabled);
2415
+ return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, label, "\uFF1A", view.label), view.status !== "disabled" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, view.version ? `\u7248\u672C ${view.version} \xB7 ` : "", view.count, " \u6761\u8BB0\u5F55", view.verifiedAt ? ` \xB7 \u6700\u8FD1\u6821\u9A8C ${new Date(view.verifiedAt).toLocaleString()}` : ""), view.source && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u516C\u5F00\u6765\u6E90\uFF1A", view.source), (view.refreshing || view.pending) && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption", role: "status" }, "\u516C\u5F00\u6E90\u6B63\u5728\u5237\u65B0\uFF1B\u4E0A\u9762\u7684\u7248\u672C\u4ECD\u662F\u6700\u8FD1\u4E00\u6B21\u5DF2\u4FDD\u5B58\u5FEB\u7167\uFF0C\u4E0D\u662F\u672C\u6B21\u8BF7\u6C42\u5DF2\u6210\u529F\u3002"), view.lastAttemptAt && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u6700\u8FD1\u5C1D\u8BD5 ", new Date(view.lastAttemptAt).toLocaleString(), view.lastSuccessAt ? ` \xB7 \u6700\u8FD1\u6210\u529F ${new Date(view.lastSuccessAt).toLocaleString()}` : ""), view.status === "error" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u66F4\u65B0\u5931\u8D25\uFF1B\u662F\u5426\u53EF\u7528\u7531 Host \u6821\u9A8C\uFF0C\u4E0D\u5C06\u7F3A\u5931\u6570\u636E\u5F53\u4F5C\u96F6\u6210\u672C\u6216\u6EE1\u5206\u3002"), view.status === "stale" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u4E0A\u6B21\u6709\u6548\u5FEB\u7167\u5DF2\u8FC7\u671F\u6216\u672C\u6B21\u66F4\u65B0\u5931\u8D25\uFF1BHost \u53EF\u80FD\u6CBF\u7528\u65E7\u6570\u636E\uFF0C\u4E0D\u80FD\u5F53\u4F5C\u6700\u65B0\u4EF7\u683C\u6216\u80FD\u529B\u3002"), view.errorCode === "storage-failed" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u672C\u6B21\u72B6\u6001\u4FDD\u5B58\u5931\u8D25\uFF1B\u53EA\u80FD\u4F7F\u7528\u6B64\u524D\u5DF2\u4FDD\u5B58\u7684\u5F53\u524D\u6E90\u5FEB\u7167\uFF0C\u8BF7\u91CD\u8BD5\u5237\u65B0\u3002"), view.errorCode === "refresh-interrupted" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u4E0A\u6B21\u516C\u5F00\u6570\u636E\u5237\u65B0\u4E2D\u65AD\uFF1B\u8BF7\u5237\u65B0\u6458\u8981\u67E5\u770B Host \u7684\u540E\u7EED\u91CD\u8BD5\u72B6\u6001\u3002"), view.lastGoodEndpoint && view.endpoint && view.lastGoodEndpoint !== view.endpoint && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u6B64\u524D\u6765\u6E90\u7684\u6709\u6548\u5FEB\u7167\u4ECD\u4FDD\u7559\u5728\u672C\u5730\uFF0C\u4F46\u4E0D\u5E94\u7528\u5230\u5F53\u524D\u516C\u5F00\u6E90\u3002"), view.status === "disabled" && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u5F53\u524D\u8BBE\u7F6E\u4E0D\u4F7F\u7528\u6B64\u516C\u5F00\u6E90\uFF1B\u4FDD\u7559\u7684\u5386\u53F2\u5FEB\u7167\u4E0D\u4EE3\u8868\u6B63\u5728\u4F7F\u7528\u3002"));
2416
+ }
2417
+ function LearningEvidence({ learning }) {
2418
+ const evidence = learningEvidenceRows(learning);
2419
+ const exclusions = learningExclusionRows(learning);
2420
+ return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, evidence.total > 0 && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u5206\u6A21\u578B / \u4EFB\u52A1\u7C7B\u578B\u7684\u5DF2\u89C2\u5BDF\u53CD\u9988\uFF08", evidence.total, " \u7EC4\uFF09"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u53EA\u63CF\u8FF0\u5DF2\u9009\u62E9\u5E76\u83B7\u5F97\u8BC4\u4EF7\u7684\u7ED3\u679C\uFF0C\u4E0D\u4EE3\u8868\u6240\u6709\u6A21\u578B\u7684\u771F\u5B9E\u8D28\u91CF\u6216\u6536\u76CA\uFF1B\u6709\u6548\u6837\u672C\u91CF\u53CD\u6620\u6743\u91CD\u96C6\u4E2D\u7A0B\u5EA6\uFF0C\u4E0D\u662F\u8D28\u91CF\u7F6E\u4FE1\u5EA6\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-table-wrap" }, /* @__PURE__ */ import_react2.default.createElement("table", { className: "mr-table" }, /* @__PURE__ */ import_react2.default.createElement("thead", null, /* @__PURE__ */ import_react2.default.createElement("tr", null, /* @__PURE__ */ import_react2.default.createElement("th", null, "\u8DEF\u7EBF / \u4EFB\u52A1\u7C7B\u578B"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u6709\u7528 / \u4E0D\u597D"), /* @__PURE__ */ import_react2.default.createElement("th", null, "\u504F\u597D\u6548\u7528\u8C03\u6574"))), /* @__PURE__ */ import_react2.default.createElement("tbody", null, evidence.rows.map((row) => /* @__PURE__ */ import_react2.default.createElement("tr", { key: row.id }, /* @__PURE__ */ import_react2.default.createElement("td", null, row.route, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-caption" }, row.domain)), /* @__PURE__ */ import_react2.default.createElement("td", null, row.positiveCount, " / ", row.negativeCount, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-caption" }, "\u5DF2\u89C2\u5BDF ", row.feedbackCount, " \xB7 \u6709\u6548\u6743\u91CD ", row.effectiveWeight.toFixed(2), " \xB7 \u6709\u6548\u6837\u672C\u91CF ", row.effectiveSampleSize.toFixed(2))), /* @__PURE__ */ import_react2.default.createElement("td", null, row.adjustment > 0 ? "+" : "", row.adjustment.toFixed(4), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-caption" }, "\u6536\u7F29\u7CFB\u6570 ", row.shrinkage.toFixed(2)))))))), evidence.total > evidence.rows.length && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u4EC5\u5C55\u793A\u524D ", evidence.rows.length, " \u7EC4\uFF1B\u4E0D\u662F\u6240\u6709\u6A21\u578B\u7684\u8986\u76D6\u7387\u3002")), exclusions.length > 0 && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u672A\u53C2\u4E0E\u5B66\u4E60\u7684\u539F\u56E0"), /* @__PURE__ */ import_react2.default.createElement("ul", { className: "mr-checklist" }, exclusions.map((row) => /* @__PURE__ */ import_react2.default.createElement("li", { key: row.reason }, row.label, "\uFF1A", row.count)))));
2421
+ }
2422
+ function AdaptiveControls({ ledger, settings, onChanged, refreshing }) {
2423
+ const value = settings.value;
2424
+ const learning = learningView(ledger?.learning, value);
2425
+ const [resetBusy, setResetBusy] = import_react2.default.useState(false);
2426
+ const [refreshNotice, setRefreshNotice] = import_react2.default.useState("");
2427
+ const resetPending = import_react2.default.useRef(false);
2428
+ const save = async (key, next) => {
2429
+ const accepted = await settings.write(key, next);
2430
+ if (accepted) {
2431
+ setRefreshNotice("");
2432
+ try {
2433
+ await onChanged?.();
2434
+ } catch {
2435
+ setRefreshNotice("\u8BBE\u7F6E\u5DF2\u4FDD\u5B58\uFF0C\u4F46\u6458\u8981\u5237\u65B0\u5931\u8D25\uFF1B\u8BF7\u5237\u65B0\u6267\u884C\u8BB0\u5F55\u540E\u91CD\u65B0\u89C4\u5212\u3002");
2436
+ }
2437
+ }
2438
+ return accepted;
2439
+ };
2440
+ const reset = async () => {
2441
+ if (resetPending.current || !settings.writable) return;
2442
+ resetPending.current = true;
2443
+ try {
2444
+ if (!window.confirm("\u4ECE\u73B0\u5728\u91CD\u65B0\u5B66\u4E60\u4F60\u7684\u504F\u597D\uFF1F\u6B64\u524D\u53CD\u9988\u5C06\u4E0D\u518D\u53C2\u4E0E\u8C03\u6574\uFF0C\u4F46\u6267\u884C\u8BB0\u5F55\u3001\u8D39\u7528\u5386\u53F2\u548C\u539F\u8BC4\u4EF7\u4E0D\u4F1A\u5220\u9664\u3002")) return;
2445
+ setResetBusy(true);
2446
+ await save("feedbackResetAt", Date.now());
2447
+ } finally {
2448
+ resetPending.current = false;
2449
+ setResetBusy(false);
2450
+ }
2451
+ };
2452
+ return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("h3", { className: "mr-section-title" }, "\u6301\u7EED\u53CD\u9988\u4E0E\u672C\u5730\u504F\u597D"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u53EA\u4F7F\u7528\u4F60\u5BF9\u5DF2\u8BB0\u5F55\u7ED3\u679C\u7684\u70B9\u8D5E / \u4E0D\u597D / \u64A4\u56DE\uFF0C\u6309\u4EFB\u52A1\u7C7B\u578B\u548C\u65F6\u95F4\u8870\u51CF\u8C03\u6574\u504F\u597D\u6548\u7528\uFF1B\u4E0D\u62AC\u9AD8\u8D28\u91CF\u786C\u95E8\u69DB\u3002\u7CFB\u7EDF\u6267\u884C\u6210\u529F\u4E0D\u7B49\u4E8E\u4F60\u6EE1\u610F\uFF0C\u6A21\u578B\u590D\u6838\u4E5F\u4E0D\u4F5C\u4E3A\u4EBA\u5DE5\u8BC4\u5206\u3002\u4E0D\u4F1A\u4E3A\u4E86\u5B66\u4E60\u53D1\u8D77\u4ED8\u8D39\u63A2\u7D22\u3002"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u8303\u56F4\uFF1A\u672C\u5730 DSH_HOME \u5DE5\u4F5C\u5BA4\u5171\u4EAB\uFF0C\u4E0D\u662F\u6309\u8D26\u53F7\u9694\u79BB\uFF1B\u6700\u591A\u6700\u8FD1 200 \u6B21\u8FD0\u884C\u7684\u6ED1\u52A8\u7A97\u53E3\uFF0C\u4E0D\u4E0A\u4F20\u53CD\u9988\u6216\u4EFB\u52A1\u5185\u5BB9\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-check" }, /* @__PURE__ */ import_react2.default.createElement(
2453
+ "input",
2454
+ {
2455
+ type: "checkbox",
2456
+ checked: adaptiveSettingValue(value, "feedbackLearningEnabled") !== false,
2457
+ disabled: !settings.writable,
2458
+ onChange: (event) => {
2459
+ void save("feedbackLearningEnabled", event.target.checked);
2460
+ }
2461
+ }
2462
+ ), "\u542F\u7528\u7528\u6237\u53CD\u9988\u5B66\u4E60"), ADAPTIVE_NUMBER_FIELDS.filter((field2) => field2.key !== "dynamicDataTtlMinutes").map((field2) => /* @__PURE__ */ import_react2.default.createElement(
2463
+ AdaptiveNumberControl,
2464
+ {
2465
+ key: field2.key,
2466
+ field: field2,
2467
+ value: adaptiveSettingValue(value, field2.key),
2468
+ writable: settings.writable,
2469
+ onSave: save
2470
+ }
2471
+ ))), settings.busy && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, "\u6B63\u5728\u4FDD\u5B58\u8BBE\u7F6E\uFF0C\u8BF7\u7A0D\u5019\u2026"), refreshing && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, "\u6B63\u5728\u8BFB\u53D6\u53CD\u9988\u4E0E\u516C\u5F00\u6570\u636E\u6458\u8981\u2026"), refreshNotice && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, refreshNotice), learning.available ? /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, learning.enabled ? "\u5B66\u4E60\u5DF2\u542F\u7528" : "\u5B66\u4E60\u5DF2\u505C\u7528", " \xB7 \u6700\u8FD1\u4E00\u6B21\u8D26\u672C\u6458\u8981\uFF1A\u6709\u6548\u53CD\u9988 ", learning.feedbackCount, " \xB7 \u6709\u6548\u6743\u91CD ", learning.effectiveWeight.toFixed(2), " \xB7 \u5FFD\u7565 ", learning.ignoredCount, learning.policyVersion ? ` \xB7 \u7B56\u7565 ${learning.policyVersion}` : "") : /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", role: "status" }, "\u5B66\u4E60\u6458\u8981\u5C1A\u672A\u8BFB\u53D6\uFF1B\u4FDD\u5B58\u540E\u5237\u65B0\u6267\u884C\u8BB0\u5F55\u67E5\u770B\u3002"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u6458\u8981\u662F\u6700\u8FD1\u4E00\u6B21\u8BFB\u53D6\u7684\u5DF2\u89C2\u5BDF\u8BC1\u636E\u3002\u4FEE\u6539\u8BC4\u4EF7 / \u5B66\u4E60\u8BBE\u7F6E\u540E\u9700\u5237\u65B0\u6458\u8981\u5E76\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\uFF1B\u5237\u65B0\u6309\u68C0\u67E5\u95F4\u9694\u590D\u7528\u516C\u5F00\u5FEB\u7167\uFF0C\u4E0D\u4FDD\u8BC1\u6BCF\u6B21\u4E0B\u8F7D\u65B0\u6570\u636E\u3002"), /* @__PURE__ */ import_react2.default.createElement(LearningEvidence, { learning: ledger?.learning }), learning.resetAt > 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u5B66\u4E60\u8D77\u70B9\uFF1A", new Date(learning.resetAt).toLocaleString(), "\uFF0C\u6B64\u524D\u53CD\u9988\u4E0D\u518D\u53C2\u4E0E\u8C03\u6574\u3002"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !settings.writable || resetBusy, onClick: () => {
2472
+ void reset();
2473
+ } }, resetBusy ? "\u6B63\u5728\u8BBE\u7F6E\u2026" : "\u4ECE\u73B0\u5728\u91CD\u65B0\u5B66\u4E60"), /* @__PURE__ */ import_react2.default.createElement("h3", { className: "mr-section-title" }, "\u52A8\u6001\u516C\u5171\u6570\u636E"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u9ED8\u8BA4\u5173\u95ED\u3002\u660E\u786E\u542F\u7528\u540E\uFF0C\u5728\u6253\u5F00 / \u5237\u65B0\u8D26\u672C\u53CA\u6267\u884C\u524D\u6309\u68C0\u67E5\u95F4\u9694\u8BFB\u53D6\u516C\u5F00 LiveBench \u548C\u53EF\u9009\u4EF7\u683C\u5FEB\u7167\uFF1B\u4EF7\u683C\u4E0E\u80FD\u529B\u5148\u9A8C\u4FDD\u7559\u7248\u672C\uFF0C\u4E0D\u4EE3\u8868\u771F\u5B9E\u4EFB\u52A1\u6536\u76CA\u3002\u53EA\u6709\u6253\u5F00\u516C\u5171\u6E90\u5F00\u5173\u624D\u4F1A\u8BFB\u53D6\u8FDC\u7A0B\u6570\u636E\uFF0C\u4E0D\u53D1\u9001\u4EFB\u52A1\u6216\u53CD\u9988\u3002"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-check" }, /* @__PURE__ */ import_react2.default.createElement(
2474
+ "input",
2475
+ {
2476
+ type: "checkbox",
2477
+ checked: adaptiveSettingValue(value, "dynamicDataEnabled") === true,
2478
+ disabled: !settings.writable,
2479
+ onChange: (event) => {
2480
+ void save("dynamicDataEnabled", event.target.checked);
2481
+ }
2482
+ }
2483
+ ), "\u542F\u7528\u52A8\u6001\u516C\u5171\u6570\u636E"), /* @__PURE__ */ import_react2.default.createElement(
2484
+ AdaptiveNumberControl,
2485
+ {
2486
+ field: ADAPTIVE_NUMBER_FIELDS.find((field2) => field2.key === "dynamicDataTtlMinutes"),
2487
+ value: adaptiveSettingValue(value, "dynamicDataTtlMinutes"),
2488
+ writable: settings.writable,
2489
+ onSave: save
2490
+ }
2491
+ ), /* @__PURE__ */ import_react2.default.createElement(PublicEndpointControl, { name: "liveBenchEndpoint", label: "LiveBench \u516C\u5F00\u6E90", value: adaptiveSettingValue(value, "liveBenchEndpoint"), writable: settings.writable, onSave: save }), /* @__PURE__ */ import_react2.default.createElement(PublicEndpointControl, { name: "pricingSnapshotEndpoint", label: "\u4EF7\u683C\u5FEB\u7167\u516C\u5F00\u6E90\uFF08\u53EF\u9009\uFF09", value: adaptiveSettingValue(value, "pricingSnapshotEndpoint"), writable: settings.writable, onSave: save }), /* @__PURE__ */ import_react2.default.createElement(PublicSourceStatus, { label: "LiveBench", source: ledger?.dynamicData?.liveBench, enabled: adaptiveSettingValue(value, "dynamicDataEnabled") === true }), /* @__PURE__ */ import_react2.default.createElement(PublicSourceStatus, { label: "\u4EF7\u683C", source: ledger?.dynamicData?.pricing, enabled: adaptiveSettingValue(value, "dynamicDataEnabled") === true && Boolean(adaptiveSettingValue(value, "pricingSnapshotEndpoint")) })));
2492
+ }
2493
+ function CostControlCard({ ledger, settingsScope, onChanged, error, refreshing = false }) {
2032
2494
  const settings = useSettingsWriter(settingsScope);
2033
2495
  const [dailyDraft, setDailyDraft] = import_react2.default.useState("");
2034
2496
  const [monthlyDraft, setMonthlyDraft] = import_react2.default.useState("");
@@ -2066,7 +2528,7 @@ function CostControlCard({ ledger, settingsScope, onChanged, error }) {
2066
2528
  void set("allowManualReassign", event.target.checked);
2067
2529
  } }), "\u5141\u8BB8\u624B\u52A8\u6539\u6D3E\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-check" }, /* @__PURE__ */ import_react2.default.createElement("input", { type: "checkbox", checked: value.confirmUnsandboxedCli !== false, disabled: !settings.writable, onChange: (event) => {
2068
2530
  void set("confirmUnsandboxedCli", event.target.checked);
2069
- } }), "\u76F4\u63A5\u542F\u52A8\u65E0\u6C99\u7BB1 CLI \u524D\u5148\u786E\u8BA4")), settings.notice && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, settings.notice)));
2531
+ } }), "\u76F4\u63A5\u542F\u52A8\u65E0\u6C99\u7BB1 CLI \u524D\u5148\u786E\u8BA4")), /* @__PURE__ */ import_react2.default.createElement(AdaptiveControls, { ledger, settings, onChanged, refreshing }), settings.notice && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, settings.notice)));
2070
2532
  }
2071
2533
  function DagView({ packages, renderNode, label = "\u5B50\u4EFB\u52A1\u4F9D\u8D56\u56FE" }) {
2072
2534
  const layers = dagLayers(packages);
@@ -2082,13 +2544,13 @@ function ChannelText({ item }) {
2082
2544
  return /* @__PURE__ */ import_react2.default.createElement("span", { className: item.channel === "official-cli" ? "mr-pill mr-pill-channel-ok" : "mr-pill" }, item.channel === "official-cli" ? `\u5B98\u65B9 CLI \xB7 ${item.toolId ?? ""}` : "\u6A21\u578B\u76EE\u5F55 API");
2083
2545
  }
2084
2546
  function RunNode({ run, item, routes, allowReassign, busy, onRate, onRerun }) {
2085
- if (item.status === "paused") return /* @__PURE__ */ import_react2.default.createElement(PausedNode, { run, item, busy, onRerun });
2086
2547
  const [target, setTarget] = import_react2.default.useState("");
2548
+ if (item.status === "paused") return /* @__PURE__ */ import_react2.default.createElement(PausedNode, { run, item, busy, onRerun });
2087
2549
  const cost = packageCost(item);
2088
2550
  const rerun = rerunSupport(run);
2089
2551
  const canRerun = rerun.supported && !item.ok && !busy;
2090
2552
  const others = routes.filter((route) => `${route.provider}/${route.model}` !== `${item.provider}/${item.model}`);
2091
- return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-route" }, item.provider, "/", item.model, item.reassigned ? "\uFF08\u5DF2\u6539\u6D3E\uFF09" : ""), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelText, { item }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u9884\u4F30 ", money(item.estimatedCost), " \xB7 \u5B9E\u9645 ", cost.budget, item.difficulty ? ` \xB7 \u96BE\u5EA6 ${BAND[item.difficulty] ?? item.difficulty}` : "")), cost.reference && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, cost.reference), item.actualModel && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "CLI \u56DE\u62A5\u6A21\u578B\uFF1A", item.actualModel), (item.billing || item.billingMode) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u8BA1\u8D39\uFF1A", BILLING_CHANNEL_LABEL[item.billing] ?? "\u2014", item.subscriptionRoute ? ` \xB7 \u5957\u9910\u8DEF\u7EBF ${item.subscriptionRoute.provider}/${item.subscriptionRoute.model}` : "", item.billingMode && item.billingMode !== "subscription-first" ? ` \xB7 ${BILLING_MODE_LABEL[item.billingMode] ?? item.billingMode}` : ""), billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-warn-text" }, "\u8BA1\u8D39\u5207\u6362\uFF1A", billingSwitchText(item)), item.fallback?.reason && item.fallback.reason !== billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u56DE\u9000\u539F\u56E0\uFF1A", item.fallback.reason), item.fallback?.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, "CLI \u539F\u59CB\u9519\u8BEF\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, item.fallback.error)), !item.ok && item.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, item.error), item.review && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5F3A\u6A21\u578B\u62BD\u67E5\uFF08", item.review.provider, "/", item.review.model, "\uFF09\uFF1A", item.review.score ? `${item.review.score}/5` : "\u672A\u8BC4\u5206", " ", item.review.summary), item.answer && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u7ED3\u679C"), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.answer, item.answerTruncated ? "\n\u2026\uFF08\u5DF2\u622A\u65AD\uFF09" : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-node-actions" }, item.ok && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", "aria-pressed": item.rating === 1, disabled: busy, onClick: () => onRate(run.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: () => {
2553
+ return /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-route" }, item.provider, "/", item.model, item.reassigned ? "\uFF08\u5DF2\u6539\u6D3E\uFF09" : ""), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelText, { item }), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, "\u9884\u4F30 ", money(item.estimatedCost), " \xB7 \u5B9E\u9645 ", cost.budget, item.difficulty ? ` \xB7 \u96BE\u5EA6 ${BAND[item.difficulty] ?? item.difficulty}` : "")), cost.reference && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, cost.reference), item.actualModel && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "CLI \u56DE\u62A5\u6A21\u578B\uFF1A", item.actualModel), (item.billing || item.billingMode) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u8BA1\u8D39\uFF1A", BILLING_CHANNEL_LABEL[item.billing] ?? "\u2014", item.subscriptionRoute ? ` \xB7 \u5957\u9910\u8DEF\u7EBF ${item.subscriptionRoute.provider}/${item.subscriptionRoute.model}` : "", item.billingMode && item.billingMode !== "subscription-first" ? ` \xB7 ${BILLING_MODE_LABEL[item.billingMode] ?? item.billingMode}` : ""), billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-warn-text" }, "\u8BA1\u8D39\u5207\u6362\uFF1A", billingSwitchText(item)), item.fallback?.reason && item.fallback.reason !== billingSwitchText(item) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u56DE\u9000\u539F\u56E0\uFF1A", item.fallback.reason), item.fallback?.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, "CLI \u539F\u59CB\u9519\u8BEF\uFF1A", /* @__PURE__ */ import_react2.default.createElement("code", null, item.fallback.error)), !item.ok && item.error && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy mr-fallback-error" }, item.error), item.review && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5F3A\u6A21\u578B\u62BD\u67E5\uFF08", item.review.provider, "/", item.review.model, "\uFF09\uFF1A", item.review.score ? `${item.review.score}/5` : "\u672A\u8BC4\u5206", " ", item.review.summary), item.answer && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u67E5\u770B\u7ED3\u679C"), /* @__PURE__ */ import_react2.default.createElement("pre", null, item.answer, item.answerTruncated ? "\n\u2026\uFF08\u5DF2\u622A\u65AD\uFF09" : "")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-node-actions" }, item.ok && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", title: item.rating === 1 ? "\u518D\u6B21\u70B9\u51FB\u64A4\u56DE\u6709\u7528\u8BC4\u4EF7" : "\u8BC4\u4EF7\u6B64\u7ED3\u679C\u6709\u7528", "aria-pressed": item.rating === 1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "up")) }, "\u{1F44D} \u6709\u7528"), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", title: item.rating === -1 ? "\u518D\u6B21\u70B9\u51FB\u64A4\u56DE\u4E0D\u597D\u8BC4\u4EF7" : "\u8BC4\u4EF7\u6B64\u7ED3\u679C\u4E0D\u597D", "aria-pressed": item.rating === -1, disabled: busy, onClick: () => onRate(...feedbackRatingArguments(run, item, "down")) }, "\u{1F44E} \u4E0D\u597D")), canRerun && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-mini", type: "button", title: rerun.confirm || void 0, onClick: () => onRerun(run.id, item.id, null) }, run.kind === "tool" ? "\u91CD\u65B0\u6267\u884C\u6B64\u8C03\u7528" : rerun.writes ? "\u5728\u65B0\u5DE5\u4F5C\u533A\u7EED\u8DD1" : "\u91CD\u8DD1\u6B64\u6B65"), !rerun.supported && !item.ok && item.ran && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, rerun.reason), rerun.supported && rerun.reassign && !(rerun.writes && item.ok) && allowReassign && others.length > 0 && !busy && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-inline" }, /* @__PURE__ */ import_react2.default.createElement("select", { className: "mr-input mr-mini-select", "aria-label": "\u6539\u6D3E\u5230", value: target, onChange: (event) => setTarget(event.target.value) }, /* @__PURE__ */ import_react2.default.createElement("option", { value: "" }, "\u6539\u6D3E\u5230\u2026"), others.map((route) => /* @__PURE__ */ import_react2.default.createElement("option", { key: `${route.provider}/${route.model}`, value: `${route.provider}\0${route.model}` }, route.provider, "/", route.model))), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-mini", type: "button", disabled: !target, onClick: () => {
2092
2554
  const [provider, model] = target.split("\0");
2093
2555
  onRerun(run.id, item.id, { provider, model });
2094
2556
  } }, "\u6539\u6D3E\u5E76\u91CD\u8DD1"))));
@@ -2173,26 +2635,26 @@ function RunLauncher({ task, mode, directRoute, budgetUsd, defaultPreset, ledger
2173
2635
  if (blocked || busy) return;
2174
2636
  const current = ++pendingRequest.current;
2175
2637
  const snapshot = request();
2176
- const signature = inputSignature;
2638
+ const signature2 = inputSignature;
2177
2639
  setPhase({ kind: "previewing" });
2178
2640
  try {
2179
2641
  const value = unwrapRemote(await previewRun(snapshot), "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002");
2180
- if (mounted.current && current === pendingRequest.current && signature === latestSignature.current) setPhase({ kind: "preview", value, request: snapshot, signature });
2642
+ if (mounted.current && current === pendingRequest.current && signature2 === latestSignature.current) setPhase({ kind: "preview", value, request: snapshot, signature: signature2 });
2181
2643
  } catch (error) {
2182
- if (mounted.current && current === pendingRequest.current && signature === latestSignature.current) setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002" });
2644
+ if (mounted.current && current === pendingRequest.current && signature2 === latestSignature.current) setPhase({ kind: "error", message: text2(error?.message) || "\u6267\u884C\u9884\u89C8\u5931\u8D25\u3002" });
2183
2645
  }
2184
2646
  };
2185
2647
  const confirm2 = async () => {
2186
2648
  if (phase.kind !== "preview" || phase.signature !== latestSignature.current) return;
2187
2649
  const shown = phase.value;
2188
2650
  const snapshot = phase.request;
2189
- const signature = phase.signature;
2190
- setPhase({ kind: "running", value: shown, request: snapshot, signature });
2651
+ const signature2 = phase.signature;
2652
+ setPhase({ kind: "running", value: shown, request: snapshot, signature: signature2 });
2191
2653
  try {
2192
2654
  const value = unwrapRemote(await startRun({ ...snapshot, confirmedReasons: (shown.reasons ?? []).map((item) => item.code) }), "\u6267\u884C\u5931\u8D25\u3002");
2193
2655
  if (!mounted.current) return;
2194
2656
  if (value.status === "needs-confirmation") {
2195
- setPhase(signature === latestSignature.current ? { kind: "preview", value, request: snapshot, signature, changed: true } : { kind: "idle" });
2657
+ setPhase(signature2 === latestSignature.current ? { kind: "preview", value, request: snapshot, signature: signature2, changed: true } : { kind: "idle" });
2196
2658
  return;
2197
2659
  }
2198
2660
  setPhase({ kind: "done", value });
@@ -11653,7 +12115,7 @@ var xterm_default = `/**
11653
12115
  `;
11654
12116
 
11655
12117
  // .dsh-plugin/client/host-version.mjs
11656
- var ROUTER_CLIENT_VERSION = true ? "0.15.0" : "";
12118
+ var ROUTER_CLIENT_VERSION = true ? "0.16.1" : "";
11657
12119
  var STALE_HOST_MESSAGE = "\u63D2\u4EF6\u540E\u53F0\u7248\u672C\u8F83\u65E7\uFF0C\u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u542F Harness\uFF08\u5305\u62EC\u6258\u76D8\u56FE\u6807\uFF09\u540E\u518D\u4F7F\u7528\u3002";
11658
12120
  function isMissingRemoteMethod(message) {
11659
12121
  const value = String(message ?? "");
@@ -12431,6 +12893,11 @@ var router_main_default = `.mr-workspace {
12431
12893
  // .dsh-plugin/client/router-main.jsx
12432
12894
  var money2 = (value) => value === null || value === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : `$${Number(value).toFixed(4)}`;
12433
12895
  var text5 = (value) => typeof value === "string" ? value.trim() : "";
12896
+ var routingSettingsSignature = (value) => JSON.stringify([
12897
+ value.modelProfilesJson,
12898
+ value.routingPreset,
12899
+ ...Object.keys(ADAPTIVE_DEFAULTS).map((key) => value[key] ?? ADAPTIVE_DEFAULTS[key])
12900
+ ]);
12434
12901
  function RouterPanelIcon({ size = 20, active = false }) {
12435
12902
  return /* @__PURE__ */ import_react5.default.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ import_react5.default.createElement("path", { d: "M7 6.5h7M7 17.5h7M15 6.5v11", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }), /* @__PURE__ */ import_react5.default.createElement("circle", { cx: "5", cy: "6.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react5.default.createElement("circle", { cx: "5", cy: "17.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react5.default.createElement("circle", { cx: "17", cy: "12", r: "3", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.7" }));
12436
12903
  }
@@ -12607,12 +13074,18 @@ var WORKSPACE_VIEWS = [
12607
13074
  ];
12608
13075
  function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResult, rerunStep, loadBoundaries }) {
12609
13076
  const [health, setHealth] = import_react5.default.useState({ report: null, error: "", refreshing: false });
12610
- const [ledger, setLedger] = import_react5.default.useState({ value: null, error: "" });
13077
+ const [ledger, setLedger] = import_react5.default.useState({ value: null, error: "", refreshing: false });
12611
13078
  const [boundaries, setBoundaries] = import_react5.default.useState({ value: null, error: "" });
12612
13079
  const [busy, setBusy] = import_react5.default.useState(false);
12613
13080
  const mounted = import_react5.default.useRef(true);
12614
- import_react5.default.useEffect(() => () => {
12615
- mounted.current = false;
13081
+ const ledgerRequest = import_react5.default.useRef(0);
13082
+ const mutationBusy = import_react5.default.useRef(false);
13083
+ import_react5.default.useEffect(() => {
13084
+ mounted.current = true;
13085
+ return () => {
13086
+ mounted.current = false;
13087
+ ledgerRequest.current += 1;
13088
+ };
12616
13089
  }, []);
12617
13090
  const call = async (operation, fallback) => {
12618
13091
  if (typeof operation !== "function") throw new Error("\u5DE5\u4F5C\u53F0\u670D\u52A1\u5C1A\u672A\u52A0\u8F7D\uFF0C\u8BF7\u66F4\u65B0\u63D2\u4EF6\u540E\u91CD\u8BD5\u3002");
@@ -12628,11 +13101,20 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
12628
13101
  }
12629
13102
  };
12630
13103
  const refreshLedger = async () => {
13104
+ const request = ++ledgerRequest.current;
13105
+ if (mounted.current) setLedger((previous) => ({ ...previous, error: "", refreshing: true }));
12631
13106
  try {
12632
13107
  const value = await call(loadLedger, "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002");
12633
- if (mounted.current) setLedger({ value, error: "" });
13108
+ if (mounted.current && request === ledgerRequest.current) setLedger({ value, error: "", refreshing: false });
13109
+ if (!mounted.current || request !== ledgerRequest.current) return null;
13110
+ return value;
12634
13111
  } catch (error) {
12635
- if (mounted.current) setLedger((previous) => ({ ...previous, error: text5(error?.message) || "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002" }));
13112
+ if (mounted.current && request === ledgerRequest.current) setLedger((previous) => ({
13113
+ ...previous,
13114
+ refreshing: false,
13115
+ error: `${text5(error?.message) || "\u6267\u884C\u8BB0\u5F55\u8BFB\u53D6\u5931\u8D25\u3002"}${previous.value ? " \u4E0B\u65B9\u4FDD\u7559\u6700\u8FD1\u4E00\u6B21\u6458\u8981\uFF0C\u5C1A\u672A\u91CD\u65B0\u6838\u9A8C\u3002" : ""}`
13116
+ }));
13117
+ return null;
12636
13118
  }
12637
13119
  };
12638
13120
  const refreshBoundaries = async () => {
@@ -12651,18 +13133,22 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
12651
13133
  if (mounted.current) setHealth((previous) => ({ ...previous, error: text5(error?.message) || "\u65E0\u6CD5\u4FDD\u5B58\u4F53\u68C0\u72B6\u6001\u3002" }));
12652
13134
  }
12653
13135
  };
12654
- const rate = async (runId, packageId, rating) => {
13136
+ const rate = async (runId, packageId, rating, expectedFinishedAt) => {
13137
+ if (mutationBusy.current) return;
13138
+ mutationBusy.current = true;
12655
13139
  setBusy(true);
12656
13140
  try {
12657
- await call(() => rateResult({ runId, packageId, rating }), "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002");
13141
+ await call(() => rateResult({ runId, packageId, rating, ...expectedFinishedAt === void 0 ? {} : { expectedFinishedAt } }), "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002");
12658
13142
  await refreshLedger();
12659
13143
  } catch (error) {
12660
13144
  if (mounted.current) setLedger((previous) => ({ ...previous, error: text5(error?.message) || "\u8BC4\u4EF7\u4FDD\u5B58\u5931\u8D25\u3002" }));
12661
13145
  } finally {
13146
+ mutationBusy.current = false;
12662
13147
  if (mounted.current) setBusy(false);
12663
13148
  }
12664
13149
  };
12665
13150
  const rerun = async (runId, packageId, override, choice = null) => {
13151
+ if (mutationBusy.current) return;
12666
13152
  const run = ledger.value?.runs?.find((item2) => item2.id === runId);
12667
13153
  const item = run?.packages?.find((entry) => entry.id === packageId);
12668
13154
  const reasons = rerunConfirmations({ run, item, override, choice, ledger: ledger.value, health: health.report, toolFor: toolForProvider, headlessIds: HEADLESS_TOOLS });
@@ -12671,6 +13157,8 @@ function useWorkbenchData({ toolHealth, completeOnboarding, loadLedger, rateResu
12671
13157
  ${reasons.map((entry, index) => `${index + 1}. ${entry.text}`).join("\n")}
12672
13158
  \u5168\u90E8\u786E\u8BA4\u5E76\u7EE7\u7EED\u5417\uFF1F`)) return;
12673
13159
  const codes = new Set(reasons.map((entry) => entry.code));
13160
+ if (mutationBusy.current) return;
13161
+ mutationBusy.current = true;
12674
13162
  setBusy(true);
12675
13163
  try {
12676
13164
  const request = {
@@ -12691,6 +13179,7 @@ ${reasons.map((entry, index) => `${index + 1}. ${entry.text}`).join("\n")}
12691
13179
  } catch (error) {
12692
13180
  if (mounted.current) setLedger((previous) => ({ ...previous, error: `${text5(error?.message) || "\u91CD\u8DD1\u5931\u8D25\u3002"} \u53EF\u4EE5\u4FEE\u6B63\u540E\u518D\u70B9\u201C\u91CD\u8DD1\u6B64\u6B65\u201D\uFF0C\u6216\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u8C03\u7528 model_router_rerun_step\u3002` }));
12693
13181
  } finally {
13182
+ mutationBusy.current = false;
12694
13183
  if (mounted.current) setBusy(false);
12695
13184
  }
12696
13185
  };
@@ -12714,7 +13203,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12714
13203
  const [toolProbes, setToolProbes] = import_react5.default.useState(null);
12715
13204
  const [routingSettings, setRoutingSettings] = import_react5.default.useState(() => {
12716
13205
  const value = settingsScope.getSnapshot().value ?? {};
12717
- return JSON.stringify([value.modelProfilesJson, value.routingPreset]);
13206
+ return routingSettingsSignature(value);
12718
13207
  });
12719
13208
  const [view, setView] = import_react5.default.useState("plan");
12720
13209
  const tabRefs = import_react5.default.useRef({});
@@ -12723,6 +13212,9 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12723
13212
  const budgetValue = import_react5.default.useRef(budget);
12724
13213
  const mounted = import_react5.default.useRef(false);
12725
13214
  const catalogRequest = import_react5.default.useRef(0);
13215
+ const generationRequest = import_react5.default.useRef(0);
13216
+ const generationInputs = import_react5.default.useRef("");
13217
+ generationInputs.current = JSON.stringify([task, budget, mode, directKey, routingSettings, catalogState.catalog, toolProbes, workbench.health.report?.tools]);
12726
13218
  import_react5.default.useEffect(() => {
12727
13219
  if (plan) resultHeading.current?.focus({ preventScroll: true });
12728
13220
  }, [plan]);
@@ -12739,10 +13231,10 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12739
13231
  import_react5.default.useEffect(() => {
12740
13232
  const syncBudget = () => {
12741
13233
  const value = settingsScope.getSnapshot().value ?? {};
12742
- const signature = JSON.stringify([value.modelProfilesJson, value.routingPreset]);
13234
+ const signature2 = routingSettingsSignature(value);
12743
13235
  setRoutingSettings((previous) => {
12744
- if (previous === signature) return previous;
12745
- return signature;
13236
+ if (previous === signature2) return previous;
13237
+ return signature2;
12746
13238
  });
12747
13239
  if (budgetEdited.current) return;
12748
13240
  const next = String(value.budgetUsd ?? 0);
@@ -12760,6 +13252,14 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12760
13252
  setPlan(null);
12761
13253
  setPlanError("");
12762
13254
  }, [routingSettings]);
13255
+ const dataRevision = workbench.ledger.value?.dynamicData?.revision ?? "";
13256
+ const feedbackRevision = workbench.ledger.value?.learning?.revision ?? "";
13257
+ import_react5.default.useEffect(() => {
13258
+ if (plan && plan.workbenchRevision !== `${dataRevision}/${feedbackRevision}`) {
13259
+ setPlan(null);
13260
+ setPlanError("");
13261
+ }
13262
+ }, [dataRevision, feedbackRevision, plan]);
12763
13263
  import_react5.default.useEffect(() => {
12764
13264
  mounted.current = true;
12765
13265
  const request = ++catalogRequest.current;
@@ -12791,15 +13291,19 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12791
13291
  }
12792
13292
  };
12793
13293
  const invalidatePlan = () => {
13294
+ generationRequest.current += 1;
12794
13295
  setPlan(null);
12795
13296
  setPlanError("");
12796
13297
  };
12797
13298
  const handleToolProbes = import_react5.default.useCallback((snapshot) => {
13299
+ generationRequest.current += 1;
12798
13300
  setToolProbes(snapshot);
12799
13301
  setPlan(null);
12800
13302
  setPlanError("");
12801
13303
  }, []);
12802
- const generate = () => {
13304
+ const generate = async () => {
13305
+ const request = ++generationRequest.current;
13306
+ const inputs = generationInputs.current;
12803
13307
  setPlanError("");
12804
13308
  try {
12805
13309
  if (!text5(task)) throw new Error("\u8BF7\u5148\u63CF\u8FF0\u4EFB\u52A1\u3002");
@@ -12808,7 +13312,16 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12808
13312
  if (!Number.isFinite(parsedBudget) || parsedBudget < 0) throw new Error("\u9884\u7B97\u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E 0 \u7684\u6570\u5B57\u3002");
12809
13313
  const direct = mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] : null;
12810
13314
  if (mode === "direct" && !direct) throw new Error("\u8BF7\u9009\u62E9\u8981\u76F4\u63A5\u4F7F\u7528\u7684\u6A21\u578B\u3002");
12811
- setPlan(createWorkspacePlan(task, catalogState.catalog, {
13315
+ const signature2 = routingSettingsSignature(settingsScope.getSnapshot().value ?? {});
13316
+ const ledger = await workbench.refreshLedger();
13317
+ if (request !== generationRequest.current || inputs !== generationInputs.current || !mounted.current) return;
13318
+ if (!ledger) throw new Error("\u8BF7\u5148\u6062\u590D\u6267\u884C\u8BB0\u5F55\u8FDE\u63A5\uFF1B\u65E0\u6CD5\u6838\u5BF9\u6700\u65B0\u6570\u636E\u4E0E\u5B66\u4E60\u72B6\u6001\u3002");
13319
+ if (ledger.storageAvailable === false) throw new Error("\u6267\u884C\u8BB0\u5F55\u5B58\u50A8\u6682\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u6838\u5BF9\u8D39\u7528\u4E0E\u5B66\u4E60\u72B6\u6001\uFF1B\u8BF7\u6062\u590D\u8FDE\u63A5\u540E\u518D\u89C4\u5212\u3002");
13320
+ if (ledger.budget?.historyVerified === false) throw new Error("\u8D39\u7528\u5386\u53F2\u4E0D\u5B8C\u6574\uFF0C\u8BF7\u4ECE\u5907\u4EFD\u6062\u590D\u5E76\u6838\u9A8C\u540E\u518D\u89C4\u5212\u3002");
13321
+ if (signature2 !== routingSettingsSignature(settingsScope.getSnapshot().value ?? {})) throw new Error("\u89C4\u5212\u8BBE\u7F6E\u5DF2\u66F4\u65B0\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u5EFA\u8BAE\u3002");
13322
+ const next = createWorkspacePlan(task, catalogState.catalog, {
13323
+ ...ledger.routingData,
13324
+ learning: ledger.learning,
12812
13325
  mode,
12813
13326
  ...direct ? { directProvider: direct.provider, directModel: direct.model } : {},
12814
13327
  budgetUsd: parsedBudget,
@@ -12816,10 +13329,11 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12816
13329
  installedToolIds: (toolProbes?.probes ?? []).filter((probe) => probe.installed).map((probe) => probe.id),
12817
13330
  runnableToolIds: (toolProbes?.readiness ?? []).filter((item) => item.ready).map((item) => item.id),
12818
13331
  preset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
12819
- qualityBiases: workbench.ledger.value?.biases ?? null,
12820
13332
  loggedOutToolIds: (workbench.health.report?.tools ?? []).filter((item) => item.installed && item.login?.state === "logged-out").map((item) => item.id)
12821
- }));
13333
+ });
13334
+ setPlan({ ...next, workbenchRevision: `${ledger.dynamicData?.revision ?? ""}/${ledger.learning?.revision ?? ""}` });
12822
13335
  } catch (error) {
13336
+ if (request !== generationRequest.current || inputs !== generationInputs.current || !mounted.current) return;
12823
13337
  setPlan(null);
12824
13338
  setPlanError(text5(error?.message) || "\u65E0\u6CD5\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE\u3002");
12825
13339
  }
@@ -12856,7 +13370,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12856
13370
  ledger: workbench.ledger.value,
12857
13371
  previewRun,
12858
13372
  startRun,
12859
- planningRevision: routingSettings,
13373
+ planningRevision: `${routingSettings}/${dataRevision}/${feedbackRevision}`,
12860
13374
  directRoute: mode === "direct" ? routes.find((route) => `${route.provider}/${route.model}` === directKey) ?? routes[0] ?? null : null,
12861
13375
  defaultPreset: settingsScope.getSnapshot().value?.routingPreset ?? "balanced",
12862
13376
  disabledReason: catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55\u2026" : catalogState.status === "error" ? "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u201C\u6A21\u578B\u914D\u7F6E\u201D\u4E2D\u5237\u65B0\u3002" : routes.length === 0 ? "\u8BF7\u5148\u5728\u5B98\u65B9\u201C\u6A21\u578B\u201D\u9875\u914D\u7F6E\u81F3\u5C11\u4E00\u6761\u6A21\u578B\u8DEF\u7EBF\u3002" : "",
@@ -12874,8 +13388,8 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12874
13388
  onRefresh: () => {
12875
13389
  void workbench.refreshLedger();
12876
13390
  },
12877
- onRate: (runId, packageId, rating) => {
12878
- void workbench.rate(runId, packageId, rating);
13391
+ onRate: (runId, packageId, rating, expectedFinishedAt) => {
13392
+ void workbench.rate(runId, packageId, rating, expectedFinishedAt);
12879
13393
  },
12880
13394
  onRerun: (runId, packageId, override, choice) => {
12881
13395
  void workbench.rerun(runId, packageId, override, choice);
@@ -12911,11 +13425,12 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
12911
13425
  CostControlCard,
12912
13426
  {
12913
13427
  ledger: workbench.ledger.value,
12914
- error: workbench.ledger.error && !workbench.ledger.value ? workbench.ledger.error : "",
13428
+ error: workbench.ledger.error || workbench.ledger.value?.storageNotice || "",
13429
+ refreshing: workbench.ledger.refreshing,
12915
13430
  settingsScope,
12916
13431
  onChanged: () => {
12917
13432
  invalidatePlan();
12918
- void workbench.refreshLedger();
13433
+ return workbench.refreshLedger();
12919
13434
  }
12920
13435
  }
12921
13436
  ), /* @__PURE__ */ import_react5.default.createElement(
@@ -12987,7 +13502,13 @@ var optionalRouteText = (value, subject) => {
12987
13502
  var rateRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RateRequest`, (value) => {
12988
13503
  const request = plainObject2(value, "rate request");
12989
13504
  if (!["up", "down", "clear"].includes(request.rating)) throw new TypeError("rating must be up, down or clear");
12990
- return { runId: idText(request.runId, "runId"), packageId: idText(request.packageId, "packageId"), rating: request.rating };
13505
+ if (request.expectedFinishedAt !== void 0 && (!Number.isFinite(request.expectedFinishedAt) || request.expectedFinishedAt < 0)) throw new TypeError("expectedFinishedAt must be a non-negative timestamp");
13506
+ return {
13507
+ runId: idText(request.runId, "runId"),
13508
+ packageId: idText(request.packageId, "packageId"),
13509
+ rating: request.rating,
13510
+ ...request.expectedFinishedAt === void 0 ? {} : { expectedFinishedAt: request.expectedFinishedAt }
13511
+ };
12991
13512
  });
12992
13513
  var rerunRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#RerunRequest`, (value) => {
12993
13514
  const request = plainObject2(value, "rerun request");