@ljwei-stak/model-router-galgame 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.dsh-plugin/client.js +1943 -4
- package/.dsh-plugin/official-tools-remote-service.mjs +1 -78
- package/.dsh-plugin/shared/official-tools-remote.mjs +0 -50
- package/INSTALLATION_GUIDE.zh.md +84 -69
- package/MIGRATION.md +36 -16
- package/README.i18n.yaml +3 -6
- package/README.md +68 -116
- package/README.zh.md +65 -99
- package/docs/assets/router-only-0.12.0.png +0 -0
- package/package.json +5 -24
- package/.dsh-plugin/shared/gal-spring-act1.mjs +0 -164
- package/.dsh-plugin/shared/gal-spring-act2.mjs +0 -482
- package/.dsh-plugin/shared/gal-spring-act3.mjs +0 -124
- package/.dsh-plugin/shared/gal-spring-sides.mjs +0 -392
- package/.dsh-plugin/shared/gal-story-echo.mjs +0 -415
- package/.dsh-plugin/shared/gal-story-echocity.mjs +0 -1283
- package/.dsh-plugin/shared/gal-story-spring.mjs +0 -333
- package/aipicture/story-backgrounds/README.md +0 -16
- package/aipicture/story-backgrounds/model-city-title.webp +0 -0
- package/docs/ECHO_CITY_STORY.zh.md +0 -117
- package/docs/GAL_ART_PROMPTS.zh.md +0 -53
- package/docs/GAL_ART_SOURCES.zh.md +0 -84
- package/docs/GAL_SETTINGS.zh.md +0 -211
- package/docs/assets/gal-claude-0.10.1-preview.png +0 -0
- package/docs/assets/gal-fullbody-0.11.0-preview.jpg +0 -0
- package/docs/assets/gal-official-fullbody-0.11.0.jpg +0 -0
- package/docs/assets/gal-official-rc2-0.11.0.jpg +0 -0
- package/docs/assets/gal-portrait-sources.json +0 -355
- package/docs/assets/gal-settings-0.11.0-preview.jpg +0 -0
- package/docs/assets/gal-spring-0.10.1-preview.png +0 -0
- package/docs/assets/gal-title-0.11.0-preview.jpg +0 -0
package/.dsh-plugin/client.js
CHANGED
|
@@ -1,4 +1,1943 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@ljwei-stak/model-router-galgame",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
var __create = Object.create;
|
|
7
|
+
var __defProp = Object.defineProperty;
|
|
8
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
9
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
10
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
11
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
12
|
+
var __export = (target, all) => {
|
|
13
|
+
for (var name in all)
|
|
14
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
15
|
+
};
|
|
16
|
+
var __copyProps = (to, from, except, desc) => {
|
|
17
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
18
|
+
for (let key of __getOwnPropNames(from))
|
|
19
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
20
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
21
|
+
}
|
|
22
|
+
return to;
|
|
23
|
+
};
|
|
24
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
25
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
26
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
27
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
28
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
29
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
30
|
+
mod
|
|
31
|
+
));
|
|
32
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
33
|
+
|
|
34
|
+
// .dsh-plugin/client/official-harness.jsx
|
|
35
|
+
var official_harness_exports = {};
|
|
36
|
+
__export(official_harness_exports, {
|
|
37
|
+
ROUTER_NAMESPACE: () => ROUTER_NAMESPACE,
|
|
38
|
+
ROUTER_PACKAGE: () => ROUTER_PACKAGE,
|
|
39
|
+
ROUTER_PANEL: () => ROUTER_PANEL,
|
|
40
|
+
RouterSettingsCard: () => RouterSettingsCard,
|
|
41
|
+
RouterSettingsCardController: () => RouterSettingsCardController,
|
|
42
|
+
apply: () => apply,
|
|
43
|
+
inject: () => inject
|
|
44
|
+
});
|
|
45
|
+
module.exports = __toCommonJS(official_harness_exports);
|
|
46
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
47
|
+
|
|
48
|
+
// .dsh-plugin/shared/official-tool-registry.mjs
|
|
49
|
+
var OFFICIAL_TOOLS = Object.freeze([
|
|
50
|
+
Object.freeze({
|
|
51
|
+
id: "kimi-code",
|
|
52
|
+
label: "Kimi Code \xB7 Node",
|
|
53
|
+
vendor: "Moonshot AI",
|
|
54
|
+
purpose: "Kimi \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B kimi \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
|
|
55
|
+
package: "@moonshot-ai/kimi-code",
|
|
56
|
+
version: "2.1.1",
|
|
57
|
+
manager: "npm",
|
|
58
|
+
installArgs: ["install", "-g", "@moonshot-ai/kimi-code@2.1.1", "--registry=https://registry.npmjs.org/"],
|
|
59
|
+
probeExecutables: ["kimi"],
|
|
60
|
+
probeNote: "kimi \u4E0E\u65E7 Python \u7248 kimi-cli \u540C\u540D\uFF1B\u8BF7\u6838\u5BF9\u53EF\u6267\u884C\u6587\u4EF6\u6765\u6E90\u548C\u7248\u672C\u3002",
|
|
61
|
+
providerHints: ["moonshot", "kimi"]
|
|
62
|
+
}),
|
|
63
|
+
Object.freeze({
|
|
64
|
+
id: "claude-code",
|
|
65
|
+
label: "Claude Code",
|
|
66
|
+
vendor: "Anthropic",
|
|
67
|
+
purpose: "Anthropic \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B claude \u547D\u4EE4\u3002",
|
|
68
|
+
package: "@anthropic-ai/claude-code",
|
|
69
|
+
version: "2.1.283",
|
|
70
|
+
manager: "npm",
|
|
71
|
+
installArgs: ["install", "-g", "@anthropic-ai/claude-code@2.1.283", "--registry=https://registry.npmjs.org/"],
|
|
72
|
+
probeExecutables: ["claude"],
|
|
73
|
+
providerHints: ["anthropic", "claude"]
|
|
74
|
+
}),
|
|
75
|
+
Object.freeze({
|
|
76
|
+
id: "codex",
|
|
77
|
+
label: "Codex CLI",
|
|
78
|
+
vendor: "OpenAI",
|
|
79
|
+
purpose: "OpenAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B codex \u547D\u4EE4\u3002",
|
|
80
|
+
package: "@openai/codex",
|
|
81
|
+
version: "0.157.1",
|
|
82
|
+
installArgs: ["install", "-g", "@openai/codex@0.157.1", "--registry=https://registry.npmjs.org/"],
|
|
83
|
+
manager: "npm",
|
|
84
|
+
probeExecutables: ["codex"],
|
|
85
|
+
probeNote: "Codex \u7248\u672C\u6A2A\u5E45\u7531\u9002\u914D\u5C42\u5BBD\u5339\u914D\uFF1B\u5B89\u88C5\u65F6\u56FA\u5B9A\u7248\u672C\u3002",
|
|
86
|
+
providerHints: ["openai", "gpt", "codex"]
|
|
87
|
+
}),
|
|
88
|
+
Object.freeze({
|
|
89
|
+
id: "minimax-code",
|
|
90
|
+
label: "MiniMax Code",
|
|
91
|
+
vendor: "MiniMax",
|
|
92
|
+
purpose: "MiniMax \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mcode \u547D\u4EE4\u3002",
|
|
93
|
+
package: "@minimax-ai/code",
|
|
94
|
+
version: "0.5.5",
|
|
95
|
+
manager: "npm",
|
|
96
|
+
installArgs: ["install", "-g", "@minimax-ai/code@0.5.5", "--registry=https://registry.npmjs.org/", "--ignore-scripts=false", "--include=optional", "--allow-scripts=@minimax-ai/code,better-sqlite3"],
|
|
97
|
+
probeExecutables: ["mcode"],
|
|
98
|
+
providerHints: ["minimax"]
|
|
99
|
+
}),
|
|
100
|
+
Object.freeze({
|
|
101
|
+
id: "mimo-code",
|
|
102
|
+
label: "MiMo Code",
|
|
103
|
+
vendor: "XiaoMi",
|
|
104
|
+
purpose: "\u5C0F\u7C73 MiMo \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mimo \u547D\u4EE4\u3002",
|
|
105
|
+
package: "@mimo-ai/cli",
|
|
106
|
+
version: "0.1.15",
|
|
107
|
+
manager: "npm",
|
|
108
|
+
installArgs: ["install", "-g", "@mimo-ai/cli@0.1.15", "--registry=https://registry.npmjs.org/"],
|
|
109
|
+
probeExecutables: ["mimo"],
|
|
110
|
+
providerHints: ["mimo", "xiaomi"]
|
|
111
|
+
}),
|
|
112
|
+
Object.freeze({
|
|
113
|
+
id: "grok-build",
|
|
114
|
+
label: "Grok Build",
|
|
115
|
+
vendor: "xAI",
|
|
116
|
+
purpose: "xAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B grok \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
|
|
117
|
+
package: "@xai-official/grok",
|
|
118
|
+
version: "1.0.41",
|
|
119
|
+
manager: "npm",
|
|
120
|
+
installArgs: ["install", "-g", "@xai-official/grok@1.0.41", "--registry=https://registry.npmjs.org/"],
|
|
121
|
+
probeExecutables: ["grok"],
|
|
122
|
+
providerHints: ["xai", "grok"]
|
|
123
|
+
}),
|
|
124
|
+
Object.freeze({
|
|
125
|
+
id: "zcode",
|
|
126
|
+
label: "ZCode",
|
|
127
|
+
vendor: "Z.ai",
|
|
128
|
+
purpose: "\u667A\u8C31\u5B98\u65B9 ZCode \u684C\u9762\u7248\uFF0C\u5185\u542B GLM \u7F16\u7A0B\u4EE3\u7406\u3002Windows \u5B89\u88C5\u5668\u53EF\u9009\u62E9 D \u76D8\u76EE\u5F55\u3002",
|
|
129
|
+
version: "3.14.3",
|
|
130
|
+
manager: "signed-windows-installer",
|
|
131
|
+
installArgs: [],
|
|
132
|
+
probeExecutables: [],
|
|
133
|
+
probeNote: "\u68C0\u6D4B\u7ECF\u8FC7\u6709\u6548\u7B7E\u540D\u7684 ZCode.exe \u548C\u540C\u76EE\u5F55 GLM \u8D44\u6E90\uFF1B\u684C\u9762\u5B89\u88C5\u5668\u9700\u4EBA\u5DE5\u9009\u62E9\u5B89\u88C5\u4F4D\u7F6E\u3002",
|
|
134
|
+
providerHints: ["zai", "z.ai", "zcode", "glm", "zhipu"]
|
|
135
|
+
})
|
|
136
|
+
]);
|
|
137
|
+
var TOOL_BY_ID = new Map(OFFICIAL_TOOLS.map((tool) => [tool.id, tool]));
|
|
138
|
+
function getOfficialTool(id2) {
|
|
139
|
+
return TOOL_BY_ID.get(String(id2 ?? "").trim()) ?? null;
|
|
140
|
+
}
|
|
141
|
+
function toolForProvider(provider) {
|
|
142
|
+
const value = String(provider ?? "").trim().toLowerCase();
|
|
143
|
+
if (!value) return null;
|
|
144
|
+
for (const tool of OFFICIAL_TOOLS) {
|
|
145
|
+
if (tool.providerHints.some((hint) => value.includes(hint))) return tool;
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
function installCommandLine(tool) {
|
|
150
|
+
if (!tool || tool.unsupported) return null;
|
|
151
|
+
if (tool.manager === "signed-windows-installer") return "\u6253\u5F00\u5B98\u65B9\u7B7E\u540D\u5B89\u88C5\u5668\uFF08\u9009\u62E9\u5B89\u88C5\u76EE\u5F55\uFF09";
|
|
152
|
+
return tool.manager === "npm" ? `npm ${tool.installArgs.join(" ")}` : `${tool.manager} ${tool.installArgs.join(" ")}`;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// .dsh-plugin/shared/model-profiles.mjs
|
|
156
|
+
var MAX_TEXT = 32e3;
|
|
157
|
+
var MAX_PROFILES = 200;
|
|
158
|
+
var id = (value) => typeof value === "string" ? value.trim() : "";
|
|
159
|
+
function nonnegative(value, label) {
|
|
160
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1e6) {
|
|
161
|
+
throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 1000000 \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
|
|
162
|
+
}
|
|
163
|
+
return value;
|
|
164
|
+
}
|
|
165
|
+
function normalizeProfile(entry, index) {
|
|
166
|
+
const label = `\u7B2C ${index + 1} \u4E2A\u6A21\u578B`;
|
|
167
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
168
|
+
const allowed = /* @__PURE__ */ new Set(["provider", "model", "quality", "pricing", "specialties", "cliModel"]);
|
|
169
|
+
const unknown = Object.keys(entry).find((key) => !allowed.has(key));
|
|
170
|
+
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`);
|
|
171
|
+
const provider = id(entry.provider);
|
|
172
|
+
const model = id(entry.model);
|
|
173
|
+
if (!provider || !model || provider.length > 160 || model.length > 240) {
|
|
174
|
+
throw new Error(`${label} \u9700\u8981\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E provider \u548C model`);
|
|
175
|
+
}
|
|
176
|
+
const profile = { provider, model };
|
|
177
|
+
if (entry.quality !== void 0) {
|
|
178
|
+
const value = nonnegative(entry.quality, `${label} \u7684 quality`);
|
|
179
|
+
if (value > 100) throw new Error(`${label} \u7684 quality \u5E94\u5728 0 \u5230 100 \u4E4B\u95F4`);
|
|
180
|
+
profile.quality = value / 100;
|
|
181
|
+
}
|
|
182
|
+
if (entry.pricing !== void 0) {
|
|
183
|
+
const prices = entry.pricing;
|
|
184
|
+
if (!prices || typeof prices !== "object" || Array.isArray(prices)) throw new Error(`${label} \u7684 pricing \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
185
|
+
const priceFields = /* @__PURE__ */ new Set(["input", "output", "cacheRead", "cacheWrite", "currency"]);
|
|
186
|
+
const unknownPrice = Object.keys(prices).find((key) => !priceFields.has(key));
|
|
187
|
+
if (unknownPrice) throw new Error(`${label} \u7684 pricing \u542B\u4E0D\u652F\u6301\u7684\u5B57\u6BB5 ${unknownPrice}`);
|
|
188
|
+
if (prices.currency !== void 0 && prices.currency !== "USD") throw new Error(`${label} \u7684\u4EF7\u683C\u5E01\u79CD\u76EE\u524D\u4EC5\u652F\u6301 USD`);
|
|
189
|
+
profile.pricing = {
|
|
190
|
+
input: nonnegative(prices.input, `${label} \u7684\u8F93\u5165\u5355\u4EF7`),
|
|
191
|
+
output: nonnegative(prices.output, `${label} \u7684\u8F93\u51FA\u5355\u4EF7`),
|
|
192
|
+
currency: "USD"
|
|
193
|
+
};
|
|
194
|
+
if (prices.cacheRead !== void 0) profile.pricing.cacheRead = nonnegative(prices.cacheRead, `${label} \u7684\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7`);
|
|
195
|
+
if (prices.cacheWrite !== void 0) profile.pricing.cacheWrite = nonnegative(prices.cacheWrite, `${label} \u7684\u7F13\u5B58\u5199\u5165\u5355\u4EF7`);
|
|
196
|
+
}
|
|
197
|
+
if (entry.specialties !== void 0) {
|
|
198
|
+
if (!Array.isArray(entry.specialties) || entry.specialties.length > 16 || entry.specialties.some((value) => !/^[a-z][a-z0-9-]{0,39}$/.test(value))) {
|
|
199
|
+
throw new Error(`${label} \u7684 specialties \u5FC5\u987B\u662F\u81F3\u591A 16 \u4E2A\u82F1\u6587\u4EFB\u52A1\u6807\u7B7E`);
|
|
200
|
+
}
|
|
201
|
+
profile.specialties = [...new Set(entry.specialties)];
|
|
202
|
+
}
|
|
203
|
+
if (entry.cliModel !== void 0) {
|
|
204
|
+
const cliModel = id(entry.cliModel);
|
|
205
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$/.test(cliModel)) throw new Error(`${label} \u7684 cliModel \u65E0\u6548`);
|
|
206
|
+
if (toolForProvider(provider)?.id === "zcode") throw new Error(`${label} \u5BF9\u5E94\u7684 ZCode CLI \u6682\u4E0D\u652F\u6301\u9010\u6B21\u5207\u6362\u6A21\u578B`);
|
|
207
|
+
profile.cliModel = cliModel;
|
|
208
|
+
}
|
|
209
|
+
if (Object.keys(profile).length === 2) throw new Error(`${label} \u81F3\u5C11\u63D0\u4F9B quality\u3001pricing\u3001specialties \u6216 cliModel \u4E4B\u4E00`);
|
|
210
|
+
return profile;
|
|
211
|
+
}
|
|
212
|
+
function parseModelProfilesJson(value) {
|
|
213
|
+
const source = value == null || value === "" ? "[]" : value;
|
|
214
|
+
if (typeof source !== "string" || source.length > MAX_TEXT) throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u8D85\u8FC7 32000 \u5B57\u7B26\u4E0A\u9650");
|
|
215
|
+
let input;
|
|
216
|
+
try {
|
|
217
|
+
input = JSON.parse(source);
|
|
218
|
+
} catch {
|
|
219
|
+
throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u4E0D\u662F\u6709\u6548\u7684 JSON");
|
|
220
|
+
}
|
|
221
|
+
if (!Array.isArray(input) || input.length > MAX_PROFILES) throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u5FC5\u987B\u662F\u6700\u591A 200 \u9879\u7684 JSON \u6570\u7EC4");
|
|
222
|
+
const profiles = input.map(normalizeProfile);
|
|
223
|
+
const seen = /* @__PURE__ */ new Set();
|
|
224
|
+
for (const profile of profiles) {
|
|
225
|
+
const key = `${profile.provider}\0${profile.model}`;
|
|
226
|
+
if (seen.has(key)) throw new Error(`\u6A21\u578B ${profile.provider}/${profile.model} \u91CD\u590D\u914D\u7F6E`);
|
|
227
|
+
seen.add(key);
|
|
228
|
+
}
|
|
229
|
+
return profiles;
|
|
230
|
+
}
|
|
231
|
+
function applyModelProfiles(routes, profiles) {
|
|
232
|
+
const input = Array.isArray(routes) ? routes : [];
|
|
233
|
+
const values = typeof profiles === "string" ? parseModelProfilesJson(profiles) : profiles;
|
|
234
|
+
if (!Array.isArray(values)) throw new Error("\u6A21\u578B\u914D\u7F6E\u5FC5\u987B\u662F\u6570\u7EC4");
|
|
235
|
+
const byKey = new Map(values.map((profile) => [`${profile.provider}\0${profile.model}`, profile]));
|
|
236
|
+
return input.map((route) => {
|
|
237
|
+
const profile = byKey.get(`${route.provider}\0${route.model}`);
|
|
238
|
+
if (!profile) return route;
|
|
239
|
+
return {
|
|
240
|
+
...route,
|
|
241
|
+
...profile.quality === void 0 ? {} : { quality: profile.quality, qualitySource: "user" },
|
|
242
|
+
...profile.pricing === void 0 ? {} : { pricing: { ...profile.pricing }, pricingSource: "user" },
|
|
243
|
+
...profile.specialties === void 0 ? {} : { specialties: [...profile.specialties] },
|
|
244
|
+
...profile.cliModel === void 0 ? {} : { cliModel: profile.cliModel }
|
|
245
|
+
};
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// .dsh-plugin/client/router-main.jsx
|
|
250
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
251
|
+
|
|
252
|
+
// .dsh-plugin/shared/livebench.mjs
|
|
253
|
+
var TASK_ALIASES = Object.freeze({
|
|
254
|
+
reasoning: ["reasoning", "reasoning_score", "hard_reasoning"],
|
|
255
|
+
code: ["code", "coding", "coding_score"],
|
|
256
|
+
math: ["math", "mathematics", "math_score"],
|
|
257
|
+
research: ["research", "retrieval", "knowledge", "data_analysis"],
|
|
258
|
+
writing: ["writing", "creative_writing", "language"],
|
|
259
|
+
vision: ["vision", "multimodal", "visual"],
|
|
260
|
+
summarization: ["summarization", "summary", "if"],
|
|
261
|
+
classification: ["classification", "instruction_following"]
|
|
262
|
+
});
|
|
263
|
+
var CATEGORY_TO_TASK = Object.freeze({
|
|
264
|
+
reasoning: "reasoning",
|
|
265
|
+
coding: "code",
|
|
266
|
+
"agentic coding": "code",
|
|
267
|
+
mathematics: "math",
|
|
268
|
+
"data analysis": "research",
|
|
269
|
+
language: "writing",
|
|
270
|
+
if: "summarization",
|
|
271
|
+
vision: "vision",
|
|
272
|
+
multimodal: "vision"
|
|
273
|
+
});
|
|
274
|
+
function normalized(value) {
|
|
275
|
+
return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
276
|
+
}
|
|
277
|
+
function liveBenchRow(snapshot, model) {
|
|
278
|
+
return snapshot?.models?.[normalized(model)] ?? null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
// .dsh-plugin/shared/router.mjs
|
|
282
|
+
var OBJECTIVE_WEIGHTS = Object.freeze({
|
|
283
|
+
simple: Object.freeze({ quality: 0.28, cost: 0.45, latency: 0.14, specialty: 0.04, reasoning: 0.07, risk: 0.02 }),
|
|
284
|
+
balanced: Object.freeze({ quality: 0.4, cost: 0.26, latency: 0.1, specialty: 0.09, reasoning: 0.1, risk: 0.05 }),
|
|
285
|
+
complex: Object.freeze({ quality: 0.48, cost: 0.14, latency: 0.06, specialty: 0.14, reasoning: 0.11, risk: 0.07 })
|
|
286
|
+
});
|
|
287
|
+
var REASONING_EFFORT_ORDER = Object.freeze(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
|
|
288
|
+
var REASONING_EFFORT_MULTIPLIERS = Object.freeze({
|
|
289
|
+
off: Object.freeze({ output: 0.78, latency: 0.75 }),
|
|
290
|
+
minimal: Object.freeze({ output: 0.86, latency: 0.82 }),
|
|
291
|
+
low: Object.freeze({ output: 0.93, latency: 0.9 }),
|
|
292
|
+
medium: Object.freeze({ output: 1, latency: 1 }),
|
|
293
|
+
high: Object.freeze({ output: 1.16, latency: 1.15 }),
|
|
294
|
+
xhigh: Object.freeze({ output: 1.34, latency: 1.3 }),
|
|
295
|
+
max: Object.freeze({ output: 1.58, latency: 1.5 })
|
|
296
|
+
});
|
|
297
|
+
var QUALITY_FLOORS = Object.freeze({ simple: 0.75, balanced: 0.78, complex: 0.82 });
|
|
298
|
+
var DEFAULT_ROUTER_SETTINGS = Object.freeze({
|
|
299
|
+
pricing: Object.freeze({}),
|
|
300
|
+
// The official site publishes versioned table/categories assets and the
|
|
301
|
+
// adapter discovers the newest release from this root URL.
|
|
302
|
+
liveBenchEndpoint: "https://livebench.ai",
|
|
303
|
+
liveBenchTtlMs: 9e5,
|
|
304
|
+
budgetUsd: 0,
|
|
305
|
+
cacheReadRatio: 0,
|
|
306
|
+
cacheWriteRatio: 0
|
|
307
|
+
});
|
|
308
|
+
var MODEL_CATALOG = Object.freeze([
|
|
309
|
+
{ id: "claude-fable-5", aliases: ["claude-fable-5", "claude fable 5"], quality: 0.99, latency: 0.34, costIn: 10, costOut: 50, specialties: ["reasoning", "writing", "research"], risk: 0.06 },
|
|
310
|
+
{ id: "claude-opus-4-8", aliases: ["claude-opus-4-8", "claude opus 4.8"], quality: 0.97, latency: 0.39, costIn: 5, costOut: 25, specialties: ["reasoning", "writing", "code"], risk: 0.07 },
|
|
311
|
+
{ id: "gpt-5.6-sol", aliases: ["gpt-5.6-sol", "gpt 5.6 sol"], quality: 0.98, latency: 0.4, costIn: 5, costOut: 30, specialties: ["reasoning", "code", "math", "vision"], risk: 0.06 },
|
|
312
|
+
{ id: "gpt-5.5", aliases: ["gpt-5.5", "gpt 5.5"], quality: 0.95, latency: 0.44, costIn: 5, costOut: 30, specialties: ["reasoning", "code", "math"], risk: 0.08 },
|
|
313
|
+
{ id: "deepseek-v4-pro", aliases: ["deepseek-v4-pro", "deepseek v4 pro"], quality: 0.93, latency: 0.52, costIn: 1.74, costOut: 3.48, specialties: ["code", "math", "reasoning"], risk: 0.1 },
|
|
314
|
+
{ id: "deepseek-v4-flash", aliases: ["deepseek-v4-flash", "deepseek v4 flash"], quality: 0.82, latency: 0.82, costIn: 0.14, costOut: 0.28, specialties: ["code", "summarization", "classification"], risk: 0.14 },
|
|
315
|
+
{ id: "kimi-k3", aliases: ["kimi-k3", "kimi k3"], quality: 0.91, latency: 0.56, costIn: 3, costOut: 15, specialties: ["reasoning", "long-context", "code"], risk: 0.1 },
|
|
316
|
+
{ id: "qwen3.7-max", aliases: ["qwen3.7-max", "qwen 3.7 max"], quality: 0.94, latency: 0.5, costIn: 2.5, costOut: 7.5, specialties: ["reasoning", "math", "code"], risk: 0.08 },
|
|
317
|
+
{ id: "qwen3.7-plus", aliases: ["qwen3.7-plus", "qwen 3.7 plus"], quality: 0.87, latency: 0.72, costIn: 0.4, costOut: 1.6, specialties: ["code", "math", "writing"], risk: 0.12 },
|
|
318
|
+
{ id: "glm-5.2", aliases: ["glm-5.2", "glm 5.2"], quality: 0.89, latency: 0.64, costIn: 1.4, costOut: 4.4, specialties: ["reasoning", "writing", "math"], risk: 0.11 },
|
|
319
|
+
{ id: "gpt-5.6-luna", aliases: ["gpt-5.6-luna", "gpt 5.6 luna"], quality: 0.84, latency: 0.86, costIn: 0.2, costOut: 1.2, specialties: ["classification", "summarization", "code"], risk: 0.14 },
|
|
320
|
+
{ id: "gpt-5.6-terra", aliases: ["gpt-5.6-terra", "gpt 5.6 terra"], quality: 0.91, latency: 0.66, costIn: 2, costOut: 12, specialties: ["code", "writing", "reasoning"], risk: 0.1 },
|
|
321
|
+
{ id: "minimax-m3", aliases: ["minimax-m3", "minimax m3"], quality: 0.86, latency: 0.69, costIn: 0.3, costOut: 1.2, specialties: ["writing", "code", "summarization"], risk: 0.13 },
|
|
322
|
+
{ id: "gemini-3-flash", aliases: ["gemini 3 flash", "gemini-3-flash"], quality: 0.88, latency: 0.73, costIn: 0.5, costOut: 3, specialties: ["vision", "research", "summarization"], risk: 0.12 },
|
|
323
|
+
{ id: "big-pickle", aliases: ["big pickle"], quality: 0.7, latency: 0.88, costIn: 0, costOut: 0, specialties: ["classification", "summarization"], risk: 0.24 }
|
|
324
|
+
]);
|
|
325
|
+
var clamp = (value, min = 0, max = 1) => Math.max(min, Math.min(max, value));
|
|
326
|
+
var normalize = (value) => String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
327
|
+
var routeKey = (provider, model) => `${String(provider ?? "")}/${String(model ?? "")}`;
|
|
328
|
+
var OPENCODE_CATALOG_PROVIDERS = Object.freeze([
|
|
329
|
+
"opencode",
|
|
330
|
+
"opencode-go",
|
|
331
|
+
// Some OpenCode-compatible configuration examples use the product name as
|
|
332
|
+
// the route id. Treat those aliases as catalog routes too; the pi-ai catalog
|
|
333
|
+
// still owns the actual model endpoints.
|
|
334
|
+
"opencode-zen",
|
|
335
|
+
"opencode-go-zen"
|
|
336
|
+
]);
|
|
337
|
+
function classifyTask(text2) {
|
|
338
|
+
const value = String(text2 ?? "");
|
|
339
|
+
if (value.length < 80 && /翻译|解释|translate|explain/i.test(value)) return "general";
|
|
340
|
+
return detectTaskTypes(value)[0] ?? "general";
|
|
341
|
+
}
|
|
342
|
+
var TASK_TYPE_RULES = Object.freeze([
|
|
343
|
+
["vision", /图片|图像|照片|视觉|image|vision|截图|识图/i],
|
|
344
|
+
["math", /数学|证明|定理|公式|方程|math|proof|theorem/i],
|
|
345
|
+
["code", /代码|编程|工程|项目|架构|接口|api|debug|实现|部署|测试|code/i],
|
|
346
|
+
["research", /研究|论文|文献|联网|检索|research|source|引用/i],
|
|
347
|
+
["summarization", /总结|摘要|提炼|提取|关键词|分类|翻译|summar|classif|extract/i],
|
|
348
|
+
["writing", /写作|润色|小说|文案|报告|writing|draft/i]
|
|
349
|
+
]);
|
|
350
|
+
var TASK_TYPE_LABELS = Object.freeze({
|
|
351
|
+
vision: "\u89C6\u89C9\u5904\u7406",
|
|
352
|
+
math: "\u6570\u5B66\u63A8\u5BFC",
|
|
353
|
+
code: "\u5DE5\u7A0B\u4E0E\u4EE3\u7801",
|
|
354
|
+
research: "\u7814\u7A76\u4E0E\u68C0\u7D22",
|
|
355
|
+
summarization: "\u6458\u8981\u4E0E\u6574\u7406",
|
|
356
|
+
writing: "\u5199\u4F5C\u4E0E\u8868\u8FBE"
|
|
357
|
+
});
|
|
358
|
+
function detectTaskTypes(text2) {
|
|
359
|
+
const value = String(text2 ?? "");
|
|
360
|
+
const ranked = TASK_TYPE_RULES.map(([type, pattern]) => ({
|
|
361
|
+
type,
|
|
362
|
+
signals: value.match(new RegExp(pattern.source, pattern.flags.includes("g") ? pattern.flags : `${pattern.flags}g`))?.length ?? 0
|
|
363
|
+
})).filter((item) => item.signals > 0);
|
|
364
|
+
ranked.sort((left, right) => right.signals - left.signals || left.type.localeCompare(right.type));
|
|
365
|
+
return ranked.map((item) => item.type);
|
|
366
|
+
}
|
|
367
|
+
function assessComplexity(text2) {
|
|
368
|
+
const value = String(text2 ?? "");
|
|
369
|
+
const lengthScore = clamp(value.length / 2200);
|
|
370
|
+
const requirementScore = clamp((value.match(/(?:^|\n)\s*(?:[-*]|\d+[.)]|[一二三四五六七八九十]+[、.])/g) ?? []).length / 8);
|
|
371
|
+
const codeScore = /(代码|工程|架构|接口|实现|部署|测试|code|api|debug)/i.test(value) ? 0.22 : 0;
|
|
372
|
+
const highReasoningScore = /(数学|证明|定理|研究|论文|复杂|多步骤|约束|比较|评估|架构|模块|部署|math|proof|research)/i.test(value) ? 0.2 : 0;
|
|
373
|
+
const visionScore = /(图片|图像|照片|截图|视觉|image|vision)/i.test(value) ? 0.12 : 0;
|
|
374
|
+
const domainMarkers = (value.match(/代码|工程|架构|接口|实现|部署|测试|模块|拆分|约束|评估|证明|定理|研究|论文|图片|图像|照片|视觉|code|api|debug|proof|research|vision/gi) ?? []).length;
|
|
375
|
+
const domainComplexity = clamp(domainMarkers / 5) * 0.28;
|
|
376
|
+
const raw = clamp(0.1 + lengthScore * 0.3 + requirementScore * 0.18 + domainComplexity + codeScore + highReasoningScore + visionScore);
|
|
377
|
+
const band = isHardRequirement(value) ? "complex" : isSimpleRequirement(value) && value.length <= 180 ? "simple" : raw < 0.34 ? "simple" : raw < 0.66 ? "balanced" : "complex";
|
|
378
|
+
return { value: raw, band };
|
|
379
|
+
}
|
|
380
|
+
function specialtyMatch(model, taskType, liveScores = {}) {
|
|
381
|
+
const benchmark = asScore(liveScores?.[taskType]);
|
|
382
|
+
if (benchmark !== void 0) return benchmark;
|
|
383
|
+
if (model.specialties.includes(taskType)) return 1;
|
|
384
|
+
if (taskType === "general") return 0.58;
|
|
385
|
+
if (taskType === "research" && model.specialties.includes("writing")) return 0.68;
|
|
386
|
+
if (taskType === "writing" && model.specialties.includes("reasoning")) return 0.62;
|
|
387
|
+
return 0.38;
|
|
388
|
+
}
|
|
389
|
+
function qualityForTask(row, taskType) {
|
|
390
|
+
return asScore(row?.liveScores?.[taskType]) ?? asScore(row?.liveOverall) ?? row?.metadata?.quality ?? row?.quality ?? 0;
|
|
391
|
+
}
|
|
392
|
+
function specialtyForTask(row, taskType) {
|
|
393
|
+
return specialtyMatch(row.metadata, taskType, row.liveScores);
|
|
394
|
+
}
|
|
395
|
+
function asScore(value) {
|
|
396
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
397
|
+
const number = Number(value);
|
|
398
|
+
if (!Number.isFinite(number)) return void 0;
|
|
399
|
+
return clamp(number > 1 ? number / 100 : number);
|
|
400
|
+
}
|
|
401
|
+
function normalizePricing(pricing) {
|
|
402
|
+
if (pricing === null || typeof pricing !== "object" || Array.isArray(pricing)) return {};
|
|
403
|
+
const normalized2 = {};
|
|
404
|
+
for (const [id2, raw] of Object.entries(pricing)) {
|
|
405
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
406
|
+
if ((raw.input ?? raw.costIn) === null || (raw.input ?? raw.costIn) === void 0) continue;
|
|
407
|
+
if ((raw.output ?? raw.costOut) === null || (raw.output ?? raw.costOut) === void 0) continue;
|
|
408
|
+
const input = Number(raw.input ?? raw.costIn);
|
|
409
|
+
const output = Number(raw.output ?? raw.costOut);
|
|
410
|
+
const cacheRead = Number(raw.cacheRead ?? input);
|
|
411
|
+
const cacheWrite = Number(raw.cacheWrite ?? input);
|
|
412
|
+
if (![input, output, cacheRead, cacheWrite].every((value) => Number.isFinite(value) && value >= 0)) continue;
|
|
413
|
+
if (String(raw.currency ?? "USD").toUpperCase() !== "USD") continue;
|
|
414
|
+
normalized2[normalize(id2)] = {
|
|
415
|
+
input,
|
|
416
|
+
output,
|
|
417
|
+
cacheRead,
|
|
418
|
+
cacheWrite,
|
|
419
|
+
currency: String(raw.currency ?? "USD").toUpperCase()
|
|
420
|
+
};
|
|
421
|
+
}
|
|
422
|
+
return normalized2;
|
|
423
|
+
}
|
|
424
|
+
function pricingFor(model, pricing, provider = "") {
|
|
425
|
+
const normalizedPricing = normalizePricing(pricing);
|
|
426
|
+
const providerOverride = provider === "" ? void 0 : normalizedPricing[normalize(`${provider}/${model.id}`)];
|
|
427
|
+
const override = providerOverride ?? normalizedPricing[normalize(model.id)];
|
|
428
|
+
if (override) return override;
|
|
429
|
+
if (!Number.isFinite(Number(model.costIn)) || !Number.isFinite(Number(model.costOut)) || model.costIn === null || model.costIn === void 0 || model.costOut === null || model.costOut === void 0) return null;
|
|
430
|
+
return {
|
|
431
|
+
input: Number(model.costIn),
|
|
432
|
+
output: Number(model.costOut),
|
|
433
|
+
cacheRead: Number(model.cacheRead ?? model.costIn),
|
|
434
|
+
cacheWrite: Number(model.cacheWrite ?? model.costIn),
|
|
435
|
+
currency: "USD"
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
function normalizedCacheRatios(cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
439
|
+
const read = Number.isFinite(Number(cacheReadRatio)) ? clamp(Number(cacheReadRatio)) : 0;
|
|
440
|
+
const write = Number.isFinite(Number(cacheWriteRatio)) ? Math.min(clamp(Number(cacheWriteRatio)), 1 - read) : 0;
|
|
441
|
+
return { read, write };
|
|
442
|
+
}
|
|
443
|
+
function effectivePricing(pricing, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
444
|
+
const { read, write } = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
|
|
445
|
+
return {
|
|
446
|
+
input: (1 - read - write) * Number(pricing.input) + read * Number(pricing.cacheRead) + write * Number(pricing.cacheWrite),
|
|
447
|
+
output: Number(pricing.output)
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
function costScore(pricing, maxCost, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
451
|
+
if (pricing === null) return 0;
|
|
452
|
+
const effective = effectivePricing(pricing, cacheReadRatio, cacheWriteRatio);
|
|
453
|
+
const mean = (effective.input + effective.output) / 2;
|
|
454
|
+
if (maxCost <= 0) return mean === 0 ? 1 : 0;
|
|
455
|
+
return clamp(1 - mean / maxCost);
|
|
456
|
+
}
|
|
457
|
+
function estimateCost(model, text2, outputTokens = 900, pricingOverrides = {}, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
458
|
+
const pricing = pricingFor(model, pricingOverrides);
|
|
459
|
+
if (pricing === null) return null;
|
|
460
|
+
const inputTokens = Math.max(80, Math.ceil(String(text2 ?? "").length / 3.7));
|
|
461
|
+
const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
|
|
462
|
+
const cacheReadTokens = Math.min(inputTokens, Math.max(0, Math.round(inputTokens * ratios.read)));
|
|
463
|
+
const cacheWriteTokens = Math.min(inputTokens - cacheReadTokens, Math.max(0, Math.round(inputTokens * ratios.write)));
|
|
464
|
+
const billableInputTokens = inputTokens - cacheReadTokens - cacheWriteTokens;
|
|
465
|
+
return (billableInputTokens * pricing.input + cacheReadTokens * pricing.cacheRead + cacheWriteTokens * pricing.cacheWrite + outputTokens * pricing.output) / 1e6;
|
|
466
|
+
}
|
|
467
|
+
function modelMetadata(name) {
|
|
468
|
+
const key = normalize(name);
|
|
469
|
+
if (!key) return null;
|
|
470
|
+
return MODEL_CATALOG.find((model) => model.aliases.some((alias) => key === normalize(alias))) ?? null;
|
|
471
|
+
}
|
|
472
|
+
function taskTokenBudget(text2, task, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
473
|
+
const inputTokens = Math.max(80, Math.ceil(String(text2 ?? "").length / 3.7));
|
|
474
|
+
const multipliers = {
|
|
475
|
+
analysis: { input: 0.9, output: 0.55 },
|
|
476
|
+
execution: { input: 1.2, output: (task.difficulty ?? complexity) === "complex" ? 1.45 : 1 },
|
|
477
|
+
verification: { input: 1.15, output: 0.7 },
|
|
478
|
+
synthesis: { input: 1.65, output: 1.3 }
|
|
479
|
+
};
|
|
480
|
+
const multiplier = multipliers[task.purpose] ?? { input: 1, output: 1 };
|
|
481
|
+
const effortMultiplier = reasoningEffortMultiplier(task.reasoningEffort);
|
|
482
|
+
const totalInputTokens = Math.max(80, Math.round(inputTokens * multiplier.input));
|
|
483
|
+
const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
|
|
484
|
+
const cacheReadTokens = Math.min(totalInputTokens, Math.max(0, Math.round(totalInputTokens * ratios.read)));
|
|
485
|
+
const cacheWriteTokens = Math.min(totalInputTokens - cacheReadTokens, Math.max(0, Math.round(totalInputTokens * ratios.write)));
|
|
486
|
+
return {
|
|
487
|
+
inputTokens: totalInputTokens,
|
|
488
|
+
cacheReadTokens,
|
|
489
|
+
cacheWriteTokens,
|
|
490
|
+
outputTokens: Math.max(220, Math.round(900 * multiplier.output * effortMultiplier.output))
|
|
491
|
+
};
|
|
492
|
+
}
|
|
493
|
+
function taskQualityFloor(band, task) {
|
|
494
|
+
const difficulty = task.difficulty ?? band;
|
|
495
|
+
const base = QUALITY_FLOORS[difficulty] ?? QUALITY_FLOORS.balanced;
|
|
496
|
+
if (task.purpose === "synthesis") return Math.max(base, band === "complex" ? 0.84 : base);
|
|
497
|
+
if (difficulty !== "complex") return base;
|
|
498
|
+
return clamp(base + Math.max(0, Number(task.criticality ?? 0.75) - 0.65) * 0.12);
|
|
499
|
+
}
|
|
500
|
+
var MAX_EXPLICIT_EXECUTION_PACKAGES = 6;
|
|
501
|
+
var REQUIREMENT_ACTION = /(?:实现|新增|添加|修复|更新|构建|设计|完成|编写|优化|接入|支持|配置|部署|测试|验证|检查|整理|创建|移除|替换|迁移|适配|开发|调研|安装|下载|上传|发布|生成|集成|改造|封装|显示|提供|允许|确保|处理|解决|分析|探测|检测|选择|分配|拆分|对接|加入|保留|记录|输出|审查|核对|对比|提取|摘要|总结|分类|翻译|格式化|润色|可以|能够|需要|进行|implement|add|fix|update|build|design|write|improve|support|configure|deploy|test|verify|check|create|remove|replace|migrate|install|publish|generate|integrate|review|extract|summarize|classify|translate|format|should|must|need)/i;
|
|
502
|
+
var HIGH_STAKES_WORK = /架构|安全|隐私|权限|并发|事务|迁移|生产|部署|发布|证明|定理|科研|论文|复杂|多步骤|跨系统|系统设计|architecture|security|migration|production|proof|theorem|research/i;
|
|
503
|
+
var SIMPLE_WORK = /翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread/i;
|
|
504
|
+
var HIGH_STAKES_ACTION = /设计|实现|修复|审计|测试|验证|证明|推导|部署|迁移|规划|研究|解决|推理|design|implement|fix|audit|verify|prove|derive|deploy|migrate|research|solve/i;
|
|
505
|
+
var SIMPLE_REQUEST_START = /^(?:请|帮我)?(?:翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread)/i;
|
|
506
|
+
function isSimpleRequirement(value) {
|
|
507
|
+
const item = String(value ?? "").trim();
|
|
508
|
+
return SIMPLE_REQUEST_START.test(item) && !/(?:并|然后|最后|同时|以及|and then).{0,20}(?:设计|实现|验证|部署|证明|审计|测试|design|implement|verify|deploy|prove|audit|test)/i.test(item);
|
|
509
|
+
}
|
|
510
|
+
function isHardRequirement(value) {
|
|
511
|
+
const item = String(value ?? "").trim();
|
|
512
|
+
if (isSimpleRequirement(item)) return false;
|
|
513
|
+
return HIGH_STAKES_WORK.test(item) && HIGH_STAKES_ACTION.test(item);
|
|
514
|
+
}
|
|
515
|
+
function requirementDifficulty(objective, type, fallback = "balanced") {
|
|
516
|
+
const value = String(objective ?? "").trim();
|
|
517
|
+
if (isHardRequirement(value)) return "complex";
|
|
518
|
+
if (value.length <= 180 && SIMPLE_WORK.test(value)) return "simple";
|
|
519
|
+
const assessed = assessComplexity(value);
|
|
520
|
+
if (type === "math" && /证明|推导|proof|derive/i.test(value)) return "complex";
|
|
521
|
+
if (assessed.band === "simple" && (type === "code" || type === "research")) return "balanced";
|
|
522
|
+
return assessed.band === "simple" ? "simple" : assessed.band === "complex" ? "complex" : fallback;
|
|
523
|
+
}
|
|
524
|
+
function shouldSplitRequirements(requirements) {
|
|
525
|
+
if (requirements.length >= 3) return true;
|
|
526
|
+
if (requirements.length !== 2) return false;
|
|
527
|
+
const types = requirements.map((item) => detectTaskTypes(item)[0] ?? "general");
|
|
528
|
+
return types[0] !== types[1] || /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(requirements[1]);
|
|
529
|
+
}
|
|
530
|
+
function requirementText(value) {
|
|
531
|
+
return String(value ?? "").trim().replace(/[。;;\s]+$/u, "").trim();
|
|
532
|
+
}
|
|
533
|
+
function looksLikeRequirement(value) {
|
|
534
|
+
const item = requirementText(value);
|
|
535
|
+
return item.length >= 3 && !/[::]$/u.test(item) && !/(?:以下|下列|如下)(?:的)?(?:任务|需求|工作|事项|要求)/u.test(item) && REQUIREMENT_ACTION.test(item.slice(0, 32));
|
|
536
|
+
}
|
|
537
|
+
function explicitRequirements(text2) {
|
|
538
|
+
const visibleLines = [];
|
|
539
|
+
let fence = null;
|
|
540
|
+
for (const line of String(text2 ?? "").split(/\r?\n/u)) {
|
|
541
|
+
const marker2 = /^\s*(`{3,}|~{3,})/u.exec(line);
|
|
542
|
+
if (fence) {
|
|
543
|
+
if (marker2 && marker2[1][0] === fence.char && marker2[1].length >= fence.length && !line.slice(marker2[0].length).trim()) fence = null;
|
|
544
|
+
continue;
|
|
545
|
+
}
|
|
546
|
+
if (marker2) {
|
|
547
|
+
fence = { char: marker2[1][0], length: marker2[1].length };
|
|
548
|
+
continue;
|
|
549
|
+
}
|
|
550
|
+
visibleLines.push(line);
|
|
551
|
+
}
|
|
552
|
+
const value = visibleLines.join("\n");
|
|
553
|
+
const lines = visibleLines.map((line) => line.trim()).filter(Boolean);
|
|
554
|
+
if (/^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(lines[0] ?? "") && !/(?:执行|完成|实施|分配)/u.test(lines[0])) return [];
|
|
555
|
+
const marker = /^(?:[-*•]\s+|\d{1,2}[.)、](?!\d)\s*|[一二三四五六七八九十]{1,3}[、.)]\s*)(.+)$/u;
|
|
556
|
+
const marked = lines.map((line) => marker.exec(line)?.[1]).filter(Boolean).map(requirementText).filter(looksLikeRequirement);
|
|
557
|
+
if (marked.length >= 2) return marked;
|
|
558
|
+
const plain = lines.filter((line) => !/^#{1,6}\s|^```|[::]$/u.test(line));
|
|
559
|
+
if (plain.length >= 2 && plain.every(looksLikeRequirement)) {
|
|
560
|
+
return plain.map(requirementText).filter((item) => item.length >= 3);
|
|
561
|
+
}
|
|
562
|
+
const clauses = value.split(/[;;,,]/u).map(requirementText).filter(Boolean);
|
|
563
|
+
if (clauses.length >= 2 && clauses.every(looksLikeRequirement)) {
|
|
564
|
+
return clauses;
|
|
565
|
+
}
|
|
566
|
+
const sentences = value.split(/[。!?]/u).map(requirementText).filter(Boolean);
|
|
567
|
+
if (sentences.length >= 2 && sentences.every(looksLikeRequirement)) {
|
|
568
|
+
return sentences;
|
|
569
|
+
}
|
|
570
|
+
return [];
|
|
571
|
+
}
|
|
572
|
+
function taskPackages(taskType, text2, band) {
|
|
573
|
+
if (band !== "complex") {
|
|
574
|
+
const task = { id: "execution", name: "\u76F4\u63A5\u56DE\u7B54\u4E0E\u5FC5\u8981\u6821\u9A8C", type: taskType, purpose: "execution", difficulty: band, criticality: 0.65, dependsOn: [], preferredReasoningEffort: band === "simple" ? "low" : "medium" };
|
|
575
|
+
return [{ ...task, qualityFloor: taskQualityFloor(band, task) }];
|
|
576
|
+
}
|
|
577
|
+
const value = String(text2 ?? "");
|
|
578
|
+
const packages = [
|
|
579
|
+
{ id: "analysis", name: "\u95EE\u9898\u5EFA\u6A21\u4E0E\u7EA6\u675F\u63D0\u53D6", type: "reasoning", purpose: "analysis", difficulty: "balanced", criticality: 0.8, dependsOn: [], preferredReasoningEffort: "medium" }
|
|
580
|
+
];
|
|
581
|
+
const requirements = explicitRequirements(text2);
|
|
582
|
+
if (requirements.length >= 2) {
|
|
583
|
+
const groups = requirements.length > MAX_EXPLICIT_EXECUTION_PACKAGES ? [
|
|
584
|
+
...requirements.slice(0, MAX_EXPLICIT_EXECUTION_PACKAGES - 1).map((item) => [item]),
|
|
585
|
+
requirements.slice(MAX_EXPLICIT_EXECUTION_PACKAGES - 1)
|
|
586
|
+
] : requirements.map((item) => [item]);
|
|
587
|
+
groups.forEach((group, index) => {
|
|
588
|
+
const objective = group.length === 1 ? group[0] : group.map((item, offset) => `${MAX_EXPLICIT_EXECUTION_PACKAGES + offset}. ${item}`).join("\n");
|
|
589
|
+
const type = detectTaskTypes(objective)[0] ?? "general";
|
|
590
|
+
const difficulty = group.length > 1 ? "complex" : requirementDifficulty(objective, type);
|
|
591
|
+
const previous = packages.at(-1);
|
|
592
|
+
const sequential = /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(objective);
|
|
593
|
+
packages.push({
|
|
594
|
+
id: `execution-${index + 1}`,
|
|
595
|
+
name: group.length === 1 ? `\u9700\u6C42 ${index + 1}\uFF1A${group[0].slice(0, 28)}` : `\u9700\u6C42 ${index + 1}\uFF1A\u5176\u4F59 ${group.length} \u9879`,
|
|
596
|
+
objective,
|
|
597
|
+
type,
|
|
598
|
+
purpose: "execution",
|
|
599
|
+
difficulty,
|
|
600
|
+
criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
|
|
601
|
+
dependsOn: sequential && previous?.purpose === "execution" ? ["analysis", previous.id] : ["analysis"],
|
|
602
|
+
preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
|
|
603
|
+
});
|
|
604
|
+
});
|
|
605
|
+
} else {
|
|
606
|
+
const domains = [...new Set([...detectTaskTypes(text2), taskType].filter((type) => type !== "general"))];
|
|
607
|
+
for (const type of domains.length > 0 ? domains : [taskType]) {
|
|
608
|
+
const objective = value.split(/[,,。;;]|最后|然后|接着|并且/u).map((item) => item.trim()).filter((item) => detectTaskTypes(item).includes(type)).join("\uFF1B") || value;
|
|
609
|
+
const difficulty = requirementDifficulty(objective, type);
|
|
610
|
+
packages.push({
|
|
611
|
+
id: `execution-${type}`,
|
|
612
|
+
name: `${TASK_TYPE_LABELS[type] ?? type}\u65B9\u5411\u5904\u7406`,
|
|
613
|
+
objective,
|
|
614
|
+
type,
|
|
615
|
+
purpose: "execution",
|
|
616
|
+
difficulty,
|
|
617
|
+
criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
|
|
618
|
+
dependsOn: ["analysis"],
|
|
619
|
+
preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
|
|
620
|
+
});
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
if (/(测试|验证|评估|对比|benchmark|test|verify|audit)/i.test(value)) {
|
|
624
|
+
packages.push({
|
|
625
|
+
id: "verification",
|
|
626
|
+
name: "\u9A8C\u8BC1\u3001\u53CD\u4F8B\u4E0E\u98CE\u9669\u5BA1\u67E5",
|
|
627
|
+
type: "reasoning",
|
|
628
|
+
purpose: "verification",
|
|
629
|
+
difficulty: "complex",
|
|
630
|
+
criticality: 0.88,
|
|
631
|
+
dependsOn: packages.filter((task) => task.purpose === "execution").map((task) => task.id),
|
|
632
|
+
preferredReasoningEffort: "high"
|
|
633
|
+
});
|
|
634
|
+
}
|
|
635
|
+
packages.push({
|
|
636
|
+
id: "synthesis",
|
|
637
|
+
name: "\u7ED3\u679C\u6821\u9A8C\u4E0E\u6574\u5408",
|
|
638
|
+
type: "reasoning",
|
|
639
|
+
purpose: "synthesis",
|
|
640
|
+
difficulty: "complex",
|
|
641
|
+
criticality: 1,
|
|
642
|
+
dependsOn: packages.filter((task) => task.purpose !== "analysis").map((task) => task.id),
|
|
643
|
+
preferredReasoningEffort: "xhigh"
|
|
644
|
+
});
|
|
645
|
+
return packages.map((task) => ({ ...task, qualityFloor: taskQualityFloor(band, task) }));
|
|
646
|
+
}
|
|
647
|
+
var SYNTHESIS_WEIGHTS = Object.freeze({ quality: 0.58, cost: 0.08, latency: 0.04, specialty: 0.08, reasoning: 0.16, risk: 0.06 });
|
|
648
|
+
var ROUTING_BEAM_WIDTH = 256;
|
|
649
|
+
var ROUTING_CANDIDATE_LIMIT = 12;
|
|
650
|
+
function weightsForTask(weights, task) {
|
|
651
|
+
return task.purpose === "synthesis" ? SYNTHESIS_WEIGHTS : OBJECTIVE_WEIGHTS[task.difficulty] ?? weights;
|
|
652
|
+
}
|
|
653
|
+
function compareText(left, right) {
|
|
654
|
+
const a = String(left);
|
|
655
|
+
const b = String(right);
|
|
656
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
657
|
+
}
|
|
658
|
+
function compareRowsStable(left, right) {
|
|
659
|
+
return compareText(routeKey(left.provider, left.model), routeKey(right.provider, right.model));
|
|
660
|
+
}
|
|
661
|
+
function reasoningEffortRank(effort) {
|
|
662
|
+
const normalized2 = String(effort ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
663
|
+
const aliases = { none: "off", disabled: "off", extra: "xhigh", extrahigh: "xhigh", maximum: "max" };
|
|
664
|
+
const canonical = aliases[normalized2] ?? normalized2;
|
|
665
|
+
const index = REASONING_EFFORT_ORDER.indexOf(canonical);
|
|
666
|
+
return index < 0 ? REASONING_EFFORT_ORDER.indexOf("medium") : index;
|
|
667
|
+
}
|
|
668
|
+
function reasoningEffortMultiplier(effort) {
|
|
669
|
+
const normalized2 = String(effort ?? "").trim().toLowerCase().replace(/[\s_-]+/g, "");
|
|
670
|
+
const aliases = { none: "off", disabled: "off", extra: "xhigh", extrahigh: "xhigh", maximum: "max" };
|
|
671
|
+
return REASONING_EFFORT_MULTIPLIERS[aliases[normalized2] ?? normalized2] ?? REASONING_EFFORT_MULTIPLIERS.medium;
|
|
672
|
+
}
|
|
673
|
+
function selectReasoningEffort(efforts, preferred = "medium") {
|
|
674
|
+
const exact = Array.isArray(efforts) ? [...new Set(efforts.map((effort) => String(effort?.id ?? effort ?? "")).filter(Boolean))] : [];
|
|
675
|
+
if (exact.length === 0) return void 0;
|
|
676
|
+
const preferredRank = reasoningEffortRank(preferred);
|
|
677
|
+
return exact.slice().sort((left, right) => {
|
|
678
|
+
const distance = Math.abs(reasoningEffortRank(left) - preferredRank) - Math.abs(reasoningEffortRank(right) - preferredRank);
|
|
679
|
+
if (distance !== 0) return distance;
|
|
680
|
+
return reasoningEffortRank(left) - reasoningEffortRank(right) || compareText(left, right);
|
|
681
|
+
})[0];
|
|
682
|
+
}
|
|
683
|
+
function reasoningDecision(row, task) {
|
|
684
|
+
const preferred = String(task.preferredReasoningEffort ?? "medium");
|
|
685
|
+
const efforts = Array.isArray(row.reasoningEfforts) ? row.reasoningEfforts : [];
|
|
686
|
+
if (efforts.length === 0) {
|
|
687
|
+
const knownUnsupported = row.reasoningKnown === true;
|
|
688
|
+
const preferredRank2 = reasoningEffortRank(preferred);
|
|
689
|
+
return {
|
|
690
|
+
reasoningEffort: void 0,
|
|
691
|
+
reasoningFit: knownUnsupported ? clamp(0.78 - preferredRank2 * 0.08) : 0.55,
|
|
692
|
+
preferredReasoningEffort: preferred,
|
|
693
|
+
multiplier: REASONING_EFFORT_MULTIPLIERS.medium
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
const preferredRank = reasoningEffortRank(preferred);
|
|
697
|
+
const chosen = selectReasoningEffort(efforts, preferred);
|
|
698
|
+
const distance = Math.abs(reasoningEffortRank(chosen) - preferredRank);
|
|
699
|
+
return {
|
|
700
|
+
reasoningEffort: chosen,
|
|
701
|
+
reasoningFit: clamp(1 - distance / (REASONING_EFFORT_ORDER.length - 1)),
|
|
702
|
+
preferredReasoningEffort: preferred,
|
|
703
|
+
multiplier: reasoningEffortMultiplier(chosen)
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
function candidateUtility(row, task, weights, maxCost, usedRoutes, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
707
|
+
const quality = qualityForTask(row, task.type);
|
|
708
|
+
const floor = Number(task.qualityFloor ?? taskQualityFloor("complex", task));
|
|
709
|
+
const qualityGap = Math.max(0, floor - quality);
|
|
710
|
+
const duplicatePenalty = 0;
|
|
711
|
+
const synthesisPreference = task.purpose === "synthesis" && /deepseek[- ]?v4[- ]?pro/i.test(row.model) ? 0.025 : 0;
|
|
712
|
+
const reasoning = reasoningDecision(row, task);
|
|
713
|
+
const cost = clamp(costScore(row.pricing, maxCost, cacheReadRatio, cacheWriteRatio) / Math.sqrt(reasoning.multiplier.output));
|
|
714
|
+
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;
|
|
715
|
+
return { score, floor, qualityGap, ...reasoning };
|
|
716
|
+
}
|
|
717
|
+
function taskCost(row, task, text2, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
718
|
+
if (row?.pricing === null) return 0;
|
|
719
|
+
const decision = row === null ? null : reasoningDecision(row, task);
|
|
720
|
+
const tokens = taskTokenBudget(text2, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity, cacheReadRatio, cacheWriteRatio);
|
|
721
|
+
return row === null ? 0 : ((tokens.inputTokens - tokens.cacheReadTokens - tokens.cacheWriteTokens) * row.pricing.input + tokens.cacheReadTokens * row.pricing.cacheRead + tokens.cacheWriteTokens * row.pricing.cacheWrite + tokens.outputTokens * row.pricing.output) / 1e6;
|
|
722
|
+
}
|
|
723
|
+
function dominates(left, right, task, text2, complexity, cacheReadRatio, cacheWriteRatio) {
|
|
724
|
+
if (left.pricing === null || right.pricing === null) return false;
|
|
725
|
+
const leftReasoning = reasoningDecision(left, task);
|
|
726
|
+
const rightReasoning = reasoningDecision(right, task);
|
|
727
|
+
const leftValues = {
|
|
728
|
+
quality: qualityForTask(left, task.type),
|
|
729
|
+
cost: taskCost(left, task, text2, complexity, cacheReadRatio, cacheWriteRatio),
|
|
730
|
+
latency: clamp(left.latency * leftReasoning.multiplier.latency),
|
|
731
|
+
specialty: specialtyForTask(left, task.type),
|
|
732
|
+
reasoning: leftReasoning.reasoningFit,
|
|
733
|
+
risk: clamp(left.risk)
|
|
734
|
+
};
|
|
735
|
+
const rightValues = {
|
|
736
|
+
quality: qualityForTask(right, task.type),
|
|
737
|
+
cost: taskCost(right, task, text2, complexity, cacheReadRatio, cacheWriteRatio),
|
|
738
|
+
latency: clamp(right.latency * rightReasoning.multiplier.latency),
|
|
739
|
+
specialty: specialtyForTask(right, task.type),
|
|
740
|
+
reasoning: rightReasoning.reasoningFit,
|
|
741
|
+
risk: clamp(right.risk)
|
|
742
|
+
};
|
|
743
|
+
const noWorse = leftValues.quality >= rightValues.quality && leftValues.cost <= rightValues.cost && leftValues.latency <= rightValues.latency && leftValues.specialty >= rightValues.specialty && leftValues.reasoning >= rightValues.reasoning && leftValues.risk <= rightValues.risk;
|
|
744
|
+
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;
|
|
745
|
+
return noWorse && strictlyBetter;
|
|
746
|
+
}
|
|
747
|
+
function candidatePool(rows, task, weights, maxCost, text2, complexity, cacheReadRatio, cacheWriteRatio) {
|
|
748
|
+
const eligibleRows = task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
|
|
749
|
+
const floor = Number(task.qualityFloor ?? 0);
|
|
750
|
+
const feasible = eligibleRows.filter((row) => qualityForTask(row, task.type) >= floor);
|
|
751
|
+
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);
|
|
752
|
+
const taskWeights = weightsForTask(weights, task);
|
|
753
|
+
const scored = source.map((row) => ({
|
|
754
|
+
row,
|
|
755
|
+
decision: candidateUtility(row, task, taskWeights, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio),
|
|
756
|
+
cost: taskCost(row, task, text2, complexity, cacheReadRatio, cacheWriteRatio)
|
|
757
|
+
}));
|
|
758
|
+
const frontier = scored.filter((item) => !source.some((other) => other !== item.row && dominates(other, item.row, task, text2, complexity, cacheReadRatio, cacheWriteRatio)));
|
|
759
|
+
const essential = [
|
|
760
|
+
scored.slice().sort((left, right) => left.cost - right.cost || compareRowsStable(left.row, right.row))[0],
|
|
761
|
+
scored.slice().sort((left, right) => right.decision.score - left.decision.score || compareRowsStable(left.row, right.row))[0],
|
|
762
|
+
scored.slice().sort((left, right) => qualityForTask(right.row, task.type) - qualityForTask(left.row, task.type) || compareRowsStable(left.row, right.row))[0]
|
|
763
|
+
].filter(Boolean);
|
|
764
|
+
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);
|
|
765
|
+
return {
|
|
766
|
+
options: ordered,
|
|
767
|
+
relaxed: feasible.length === 0,
|
|
768
|
+
pruned: Math.max(0, eligibleRows.length - ordered.length)
|
|
769
|
+
};
|
|
770
|
+
}
|
|
771
|
+
function stateSignature(state) {
|
|
772
|
+
return state.assignments.map((assignment) => `${routeKey(assignment.row?.provider, assignment.row?.model)}@${assignment.decision?.reasoningEffort ?? "provider-default"}`).join("|");
|
|
773
|
+
}
|
|
774
|
+
function compareUtilityStates(left, right) {
|
|
775
|
+
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));
|
|
776
|
+
}
|
|
777
|
+
function compareCostStates(left, right) {
|
|
778
|
+
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));
|
|
779
|
+
}
|
|
780
|
+
function solveAssignments({ rows, tasks, weights, maxCost, text: text2, complexity, budget, cacheReadRatio, cacheWriteRatio, minimizeCost = false }) {
|
|
781
|
+
const pools = tasks.map((task) => candidatePool(rows, task, weights, maxCost, text2, complexity, cacheReadRatio, cacheWriteRatio));
|
|
782
|
+
if (pools.some((pool) => pool.options.length === 0)) return null;
|
|
783
|
+
const suffixMinimum = Array(tasks.length + 1).fill(0);
|
|
784
|
+
for (let index = tasks.length - 1; index >= 0; index -= 1) {
|
|
785
|
+
const costOptions = Number.isFinite(budget) ? pools[index].options.filter((option) => option.row.pricing !== null) : pools[index].options;
|
|
786
|
+
if (costOptions.length === 0) return null;
|
|
787
|
+
suffixMinimum[index] = suffixMinimum[index + 1] + Math.min(...costOptions.map((option) => option.cost));
|
|
788
|
+
}
|
|
789
|
+
if (Number.isFinite(budget) && suffixMinimum[0] > budget + 1e-12) return null;
|
|
790
|
+
let states = [{ assignments: [], routesByTask: /* @__PURE__ */ new Map(), usedRoutes: /* @__PURE__ */ new Set(), score: 0, cost: 0, switches: 0, relaxedCount: 0, qualityShortfall: 0 }];
|
|
791
|
+
for (let index = 0; index < tasks.length; index += 1) {
|
|
792
|
+
const task = tasks[index];
|
|
793
|
+
const pool = pools[index];
|
|
794
|
+
const expanded = [];
|
|
795
|
+
for (const state of states) {
|
|
796
|
+
for (const option of pool.options) {
|
|
797
|
+
if (Number.isFinite(budget) && option.row.pricing === null) continue;
|
|
798
|
+
const nextCost = state.cost + option.cost;
|
|
799
|
+
if (Number.isFinite(budget) && nextCost + suffixMinimum[index + 1] > budget + 1e-12) continue;
|
|
800
|
+
const taskWeights = weightsForTask(weights, task);
|
|
801
|
+
const decision = candidateUtility(option.row, task, taskWeights, maxCost, state.usedRoutes, cacheReadRatio, cacheWriteRatio);
|
|
802
|
+
const route = routeKey(option.row.provider, option.row.model);
|
|
803
|
+
const dependencySwitches = (task.dependsOn ?? []).reduce((count, dependency) => {
|
|
804
|
+
const dependencyRoute = state.routesByTask.get(dependency);
|
|
805
|
+
return count + (dependencyRoute !== void 0 && dependencyRoute !== route ? 1 : 0);
|
|
806
|
+
}, 0);
|
|
807
|
+
const handoffPenalty = dependencySwitches * 0.015;
|
|
808
|
+
const qualityShortfall = Math.max(0, decision.floor - qualityForTask(option.row, task.type));
|
|
809
|
+
const usedRoutes = new Set(state.usedRoutes);
|
|
810
|
+
usedRoutes.add(route);
|
|
811
|
+
const routesByTask = new Map(state.routesByTask);
|
|
812
|
+
routesByTask.set(task.id, route);
|
|
813
|
+
expanded.push({
|
|
814
|
+
assignments: [...state.assignments, { task, row: option.row, decision: { ...decision, relaxed: qualityShortfall > 0 }, estimatedCost: option.cost, handoffPenalty }],
|
|
815
|
+
routesByTask,
|
|
816
|
+
usedRoutes,
|
|
817
|
+
score: state.score + decision.score - handoffPenalty,
|
|
818
|
+
cost: nextCost,
|
|
819
|
+
switches: state.switches + dependencySwitches,
|
|
820
|
+
relaxedCount: state.relaxedCount + (qualityShortfall > 0 ? 1 : 0),
|
|
821
|
+
qualityShortfall: state.qualityShortfall + qualityShortfall
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
if (expanded.length === 0) return null;
|
|
826
|
+
expanded.sort(minimizeCost ? compareCostStates : compareUtilityStates);
|
|
827
|
+
states = expanded.slice(0, ROUTING_BEAM_WIDTH);
|
|
828
|
+
}
|
|
829
|
+
states.sort(minimizeCost ? compareCostStates : compareUtilityStates);
|
|
830
|
+
return {
|
|
831
|
+
...states[0],
|
|
832
|
+
candidatePools: pools,
|
|
833
|
+
minimumFeasibleCost: suffixMinimum[0]
|
|
834
|
+
};
|
|
835
|
+
}
|
|
836
|
+
function buildPlan({ text: text2 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0 } = {}) {
|
|
837
|
+
const firstLine = String(text2 ?? "").split(/\r?\n/u)[0].trim();
|
|
838
|
+
const transformOnly = /^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(firstLine) && !/(?:执行|完成|实施|分配)/u.test(firstLine);
|
|
839
|
+
const assessed = assessComplexity(transformOnly ? firstLine : text2);
|
|
840
|
+
const requirements = explicitRequirements(text2);
|
|
841
|
+
const compound = shouldSplitRequirements(requirements);
|
|
842
|
+
const complexity = compound && assessed.band !== "complex" ? { value: Math.max(0.66, assessed.value), band: "complex" } : assessed;
|
|
843
|
+
const taskType = classifyTask(transformOnly ? firstLine : text2);
|
|
844
|
+
const weights = OBJECTIVE_WEIGHTS[complexity.band];
|
|
845
|
+
const discovered = Array.isArray(available) ? available.map((entry) => {
|
|
846
|
+
const rawEfforts = Array.isArray(entry.reasoningEfforts) ? entry.reasoningEfforts : [];
|
|
847
|
+
const reasoningEfforts = rawEfforts.map((effort) => String(effort?.id ?? effort ?? "")).filter(Boolean);
|
|
848
|
+
return {
|
|
849
|
+
provider: String(entry.provider ?? ""),
|
|
850
|
+
model: String(entry.model ?? ""),
|
|
851
|
+
reasoningEfforts: [...new Set(reasoningEfforts)],
|
|
852
|
+
defaultReasoningEffort: entry.defaultReasoningEffort === void 0 ? void 0 : String(entry.defaultReasoningEffort),
|
|
853
|
+
reasoningKnown: entry.reasoningKnown === true || entry.reasoningKnown === void 0 && Array.isArray(entry.reasoningEfforts),
|
|
854
|
+
quality: asScore(entry.quality),
|
|
855
|
+
qualitySource: entry.qualitySource === "user" ? "user" : "route",
|
|
856
|
+
latency: asScore(entry.latency),
|
|
857
|
+
risk: asScore(entry.risk),
|
|
858
|
+
specialties: Array.isArray(entry.specialties) ? entry.specialties.filter((item) => typeof item === "string") : null,
|
|
859
|
+
pricing: normalizePricing({ route: entry.pricing ?? entry.price }).route ?? null,
|
|
860
|
+
pricingSource: entry.pricingSource === "user" ? "user" : "route",
|
|
861
|
+
inputModalities: Array.isArray(entry.inputModalities) ? entry.inputModalities.map((item) => String(item).toLowerCase()) : []
|
|
862
|
+
};
|
|
863
|
+
}) : [];
|
|
864
|
+
const rows = [];
|
|
865
|
+
const normalizedPrices = normalizePricing(pricing);
|
|
866
|
+
for (const route of discovered) {
|
|
867
|
+
if (!route.provider || !route.model) continue;
|
|
868
|
+
const catalog = modelMetadata(route.model);
|
|
869
|
+
const metadata = {
|
|
870
|
+
...catalog ?? { id: route.model, aliases: [route.model], specialties: [] },
|
|
871
|
+
...route.quality === void 0 ? {} : { quality: route.quality },
|
|
872
|
+
...route.latency === void 0 ? {} : { latency: route.latency },
|
|
873
|
+
...route.risk === void 0 ? {} : { risk: route.risk },
|
|
874
|
+
...route.specialties === null ? {} : { specialties: route.specialties }
|
|
875
|
+
};
|
|
876
|
+
const live = liveBenchRow(liveBench, route.model);
|
|
877
|
+
const liveScores = live?.scores ?? {};
|
|
878
|
+
const liveOverall = asScore(live?.overall);
|
|
879
|
+
const quality = asScore(liveScores?.[taskType]) ?? liveOverall ?? asScore(metadata.quality) ?? 0;
|
|
880
|
+
const qualitySource = liveOverall !== void 0 || asScore(liveScores?.[taskType]) !== void 0 ? "livebench" : route.quality !== void 0 ? route.qualitySource : catalog ? "catalog-heuristic" : "unknown";
|
|
881
|
+
const userPrice = normalizedPrices[normalize(`${route.provider}/${route.model}`)] ?? normalizedPrices[normalize(route.model)];
|
|
882
|
+
const pricingRow = userPrice ?? route.pricing ?? null;
|
|
883
|
+
const pricingSource = userPrice ? "user" : route.pricing ? route.pricingSource : "unknown";
|
|
884
|
+
const specialty = specialtyMatch(metadata, taskType, liveScores);
|
|
885
|
+
rows.push({
|
|
886
|
+
provider: route.provider,
|
|
887
|
+
model: route.model,
|
|
888
|
+
metadata,
|
|
889
|
+
quality,
|
|
890
|
+
qualitySource,
|
|
891
|
+
pricingSource,
|
|
892
|
+
liveScores,
|
|
893
|
+
liveOverall,
|
|
894
|
+
latency: metadata.latency ?? 0.5,
|
|
895
|
+
risk: metadata.risk ?? 0.2,
|
|
896
|
+
specialty,
|
|
897
|
+
reasoningEfforts: route.reasoningEfforts,
|
|
898
|
+
defaultReasoningEffort: route.defaultReasoningEffort,
|
|
899
|
+
reasoningKnown: route.reasoningKnown,
|
|
900
|
+
inputModalities: route.inputModalities,
|
|
901
|
+
pricing: pricingRow,
|
|
902
|
+
score: 0,
|
|
903
|
+
estimatedCost: pricingRow === null ? null : estimateCost({
|
|
904
|
+
id: route.model,
|
|
905
|
+
costIn: pricingRow.input,
|
|
906
|
+
costOut: pricingRow.output,
|
|
907
|
+
cacheRead: pricingRow.cacheRead,
|
|
908
|
+
cacheWrite: pricingRow.cacheWrite
|
|
909
|
+
}, text2, 900, {}, cacheReadRatio, cacheWriteRatio)
|
|
910
|
+
});
|
|
911
|
+
}
|
|
912
|
+
const maxCost = Math.max(1, ...rows.filter((row) => row.pricing !== null).map((row) => {
|
|
913
|
+
const effective = effectivePricing(row.pricing, cacheReadRatio, cacheWriteRatio);
|
|
914
|
+
return effective.input + effective.output;
|
|
915
|
+
}));
|
|
916
|
+
const taskNodes = taskPackages(taskType, text2, complexity.band);
|
|
917
|
+
const unassignableTasks = taskNodes.filter((task) => task.type === "vision" && !rows.some((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image"))).map((task) => task.id);
|
|
918
|
+
const budget = Number(budgetUsd);
|
|
919
|
+
const utilityPlan = solveAssignments({
|
|
920
|
+
rows,
|
|
921
|
+
tasks: taskNodes,
|
|
922
|
+
weights,
|
|
923
|
+
maxCost,
|
|
924
|
+
text: text2,
|
|
925
|
+
complexity: complexity.band,
|
|
926
|
+
budget: Number.POSITIVE_INFINITY,
|
|
927
|
+
cacheReadRatio,
|
|
928
|
+
cacheWriteRatio
|
|
929
|
+
});
|
|
930
|
+
const budgetPlan = budget > 0 ? solveAssignments({
|
|
931
|
+
rows,
|
|
932
|
+
tasks: taskNodes,
|
|
933
|
+
weights,
|
|
934
|
+
maxCost,
|
|
935
|
+
text: text2,
|
|
936
|
+
complexity: complexity.band,
|
|
937
|
+
budget,
|
|
938
|
+
cacheReadRatio,
|
|
939
|
+
cacheWriteRatio
|
|
940
|
+
}) : null;
|
|
941
|
+
const minimumCostPlan = budget > 0 && budgetPlan === null ? solveAssignments({
|
|
942
|
+
rows,
|
|
943
|
+
tasks: taskNodes,
|
|
944
|
+
weights,
|
|
945
|
+
maxCost,
|
|
946
|
+
text: text2,
|
|
947
|
+
complexity: complexity.band,
|
|
948
|
+
budget: Number.POSITIVE_INFINITY,
|
|
949
|
+
cacheReadRatio,
|
|
950
|
+
cacheWriteRatio,
|
|
951
|
+
minimizeCost: true
|
|
952
|
+
}) : null;
|
|
953
|
+
const optimized = budget > 0 ? budgetPlan ?? minimumCostPlan ?? utilityPlan : utilityPlan;
|
|
954
|
+
const assignments = optimized?.assignments ?? [];
|
|
955
|
+
const usedRoutes = optimized?.usedRoutes ?? /* @__PURE__ */ new Set();
|
|
956
|
+
const constraintRelaxed = (optimized?.relaxedCount ?? 0) > 0;
|
|
957
|
+
for (const row of rows) {
|
|
958
|
+
row.score = candidateUtility(row, taskNodes[0] ?? { type: taskType, qualityFloor: QUALITY_FLOORS[complexity.band] }, weights, maxCost, /* @__PURE__ */ new Set(), cacheReadRatio, cacheWriteRatio).score;
|
|
959
|
+
}
|
|
960
|
+
rows.sort((left, right) => right.score - left.score || compareRowsStable(left, right));
|
|
961
|
+
const selectedAssignment = assignments[0];
|
|
962
|
+
const selected = selectedAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows[0] ?? null);
|
|
963
|
+
const synthesizerAssignment = assignments.at(-1);
|
|
964
|
+
const synthesizer = synthesizerAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows.find((row) => /deepseek/i.test(row.model)) ?? rows[0]);
|
|
965
|
+
const subtasks = assignments.map(({ task, row, decision }) => ({
|
|
966
|
+
id: task.id,
|
|
967
|
+
name: task.name,
|
|
968
|
+
...task.objective ? { objective: task.objective } : {},
|
|
969
|
+
type: task.type,
|
|
970
|
+
difficulty: task.difficulty,
|
|
971
|
+
recommended: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
|
|
972
|
+
recommendedProvider: row?.provider ?? "",
|
|
973
|
+
qualitySource: row?.qualitySource ?? "unknown",
|
|
974
|
+
pricingSource: row?.pricingSource ?? "unknown",
|
|
975
|
+
recommendedReasoningEffort: decision?.reasoningEffort,
|
|
976
|
+
preferredReasoningEffort: decision?.preferredReasoningEffort ?? task.preferredReasoningEffort,
|
|
977
|
+
reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
|
|
978
|
+
purpose: task.purpose,
|
|
979
|
+
criticality: task.criticality,
|
|
980
|
+
qualityFloor: Number(task.qualityFloor.toFixed(3)),
|
|
981
|
+
dependsOn: [...task.dependsOn ?? []]
|
|
982
|
+
}));
|
|
983
|
+
const costBreakdown = assignments.map(({ task, row, decision, estimatedCost, handoffPenalty }, index) => {
|
|
984
|
+
const tokens = taskTokenBudget(text2, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity.band, cacheReadRatio, cacheWriteRatio);
|
|
985
|
+
const taskEstimate = estimatedCost ?? taskCost(row, task, text2, complexity.band, cacheReadRatio, cacheWriteRatio);
|
|
986
|
+
return {
|
|
987
|
+
stage: index + 1,
|
|
988
|
+
purpose: task.purpose,
|
|
989
|
+
difficulty: task.difficulty,
|
|
990
|
+
model: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
|
|
991
|
+
provider: row?.provider ?? "",
|
|
992
|
+
reasoningEffort: decision?.reasoningEffort,
|
|
993
|
+
preferredReasoningEffort: decision?.preferredReasoningEffort,
|
|
994
|
+
reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
|
|
995
|
+
reasoningOutputMultiplier: Number(Number(decision?.multiplier?.output ?? 1).toFixed(2)),
|
|
996
|
+
inputTokens: tokens.inputTokens,
|
|
997
|
+
cacheReadTokens: tokens.cacheReadTokens,
|
|
998
|
+
cacheWriteTokens: tokens.cacheWriteTokens,
|
|
999
|
+
outputTokens: tokens.outputTokens,
|
|
1000
|
+
estimatedCost: row?.pricing === null || row === null ? null : Number(taskEstimate.toFixed(6)),
|
|
1001
|
+
quality: row?.qualitySource === "unknown" || row === null ? null : Number(qualityForTask(row, task.type).toFixed(3)),
|
|
1002
|
+
qualitySource: row?.qualitySource ?? "unknown",
|
|
1003
|
+
pricingSource: row?.pricingSource ?? "unknown",
|
|
1004
|
+
handoffPenalty: Number(Number(handoffPenalty ?? 0).toFixed(3))
|
|
1005
|
+
};
|
|
1006
|
+
});
|
|
1007
|
+
const pricingComplete = assignments.length > 0 && assignments.every(({ row }) => row?.pricing !== null);
|
|
1008
|
+
const totalEstimate = pricingComplete ? costBreakdown.reduce((sum, row) => sum + row.estimatedCost, 0) : null;
|
|
1009
|
+
const baselineRows = assignments.map(({ task }) => {
|
|
1010
|
+
const strongest = rows.reduce((best, row) => qualityForTask(row, task.type) > (best === null ? -1 : qualityForTask(best, task.type)) ? row : best, null);
|
|
1011
|
+
return { task, strongest };
|
|
1012
|
+
});
|
|
1013
|
+
const baselineCost = baselineRows.every((item) => item.strongest?.pricing !== null && item.strongest !== null) ? baselineRows.reduce((sum, { task, strongest }) => sum + taskCost(strongest, task, text2, complexity.band, cacheReadRatio, cacheWriteRatio), 0) : null;
|
|
1014
|
+
const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row }) => ["livebench", "route", "user"].includes(row?.qualitySource)) && baselineRows.every(({ strongest }) => ["livebench", "route", "user"].includes(strongest?.qualitySource));
|
|
1015
|
+
const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate !== null ? totalEstimate > Number(budgetUsd) : null;
|
|
1016
|
+
const savings = baselineCost === null || totalEstimate === null || !qualityEvidenceComplete ? null : baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
|
|
1017
|
+
const paretoPruned = (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.pruned, 0);
|
|
1018
|
+
const minimumFeasibleCost = rows.every((row) => row.pricing !== null) ? optimized?.minimumFeasibleCost ?? minimumCostPlan?.cost ?? 0 : null;
|
|
1019
|
+
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`;
|
|
1020
|
+
return {
|
|
1021
|
+
mode,
|
|
1022
|
+
complexity: { value: Number(complexity.value.toFixed(3)), band: complexity.band },
|
|
1023
|
+
compound,
|
|
1024
|
+
unassignableTasks,
|
|
1025
|
+
taskType,
|
|
1026
|
+
taskTypes: [...new Set(taskNodes.map((task) => task.type).filter((type) => type !== "reasoning"))],
|
|
1027
|
+
objectiveWeights: weights,
|
|
1028
|
+
candidates: rows.slice(0, 8).map((row) => {
|
|
1029
|
+
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);
|
|
1030
|
+
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 };
|
|
1031
|
+
}),
|
|
1032
|
+
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 },
|
|
1033
|
+
subtasks,
|
|
1034
|
+
synthesizer: synthesizer == null ? null : { provider: synthesizer.provider, model: synthesizer.model, reasoningEffort: synthesizerAssignment?.decision?.reasoningEffort },
|
|
1035
|
+
estimatedCost: totalEstimate === null ? null : Number(totalEstimate.toFixed(6)),
|
|
1036
|
+
costBreakdown,
|
|
1037
|
+
optimization: {
|
|
1038
|
+
solver: "pareto-pruned quality-constrained beam assignment",
|
|
1039
|
+
qualityFloor: QUALITY_FLOORS[complexity.band],
|
|
1040
|
+
budgetUsd: Number(Number(budgetUsd) > 0 ? Number(budgetUsd) : 0),
|
|
1041
|
+
cacheReadRatio: normalizedCacheRatios(cacheReadRatio, cacheWriteRatio).read,
|
|
1042
|
+
cacheWriteRatio: normalizedCacheRatios(cacheReadRatio, cacheWriteRatio).write,
|
|
1043
|
+
budgetExceeded,
|
|
1044
|
+
constraintRelaxed,
|
|
1045
|
+
pricingComplete,
|
|
1046
|
+
qualityEvidenceComplete,
|
|
1047
|
+
baselineAllStrongCost: baselineCost === null ? null : Number(baselineCost.toFixed(6)),
|
|
1048
|
+
estimatedSavings: savings === null ? null : Number(savings.toFixed(4)),
|
|
1049
|
+
distinctRoutes: usedRoutes.size,
|
|
1050
|
+
handoffCount: optimized?.switches ?? 0,
|
|
1051
|
+
paretoPruned,
|
|
1052
|
+
beamWidth: ROUTING_BEAM_WIDTH,
|
|
1053
|
+
budgetFeasible: budget <= 0 ? pricingComplete ? true : null : budgetPlan !== null,
|
|
1054
|
+
minimumFeasibleCost: minimumFeasibleCost === null ? null : Number(Number(minimumFeasibleCost).toFixed(6)),
|
|
1055
|
+
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 || "") }
|
|
1056
|
+
},
|
|
1057
|
+
reason,
|
|
1058
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1059
|
+
};
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
// .dsh-plugin/shared/harness-plan.mjs
|
|
1063
|
+
var clean = (value) => typeof value === "string" ? value.trim() : "";
|
|
1064
|
+
function channelForProvider(provider, installedToolIds = [], runnableToolIds = []) {
|
|
1065
|
+
const tool = toolForProvider(provider);
|
|
1066
|
+
if (!tool || tool.unsupported) {
|
|
1067
|
+
return { kind: "harness-llm", detail: "\u901A\u8FC7\u5B98\u65B9\u6A21\u578B\u76EE\u5F55 API \u8C03\u7528\u3002" };
|
|
1068
|
+
}
|
|
1069
|
+
const installed = Array.isArray(installedToolIds) && installedToolIds.includes(tool.id);
|
|
1070
|
+
const runnable = installed && Array.isArray(runnableToolIds) && runnableToolIds.includes(tool.id);
|
|
1071
|
+
return runnable ? { kind: "official-cli", tool: tool.id, label: tool.label, detail: tool.id === "zcode" ? "ZCode \u5DF2\u5B89\u88C5\uFF0C\u63D2\u4EF6\u53EF\u8C03\u7528\u5176\u5B98\u65B9\u7F16\u7A0B\u4EE3\u7406\uFF1B3.14.3 \u7684 CLI \u4F7F\u7528\u81EA\u8EAB\u914D\u7F6E\u7684\u9ED8\u8BA4\u6A21\u578B\uFF0C\u4E0D\u80FD\u4FDD\u8BC1\u4E0E Harness \u5EFA\u8BAE\u6A21\u578B\u4E00\u81F4\u3002" : `${tool.label} \u5DF2\u5B89\u88C5\uFF0C\u63D2\u4EF6\u53EF\u6258\u7BA1\u8C03\u7528\u5176\u5B98\u65B9 CLI\uFF1BHarness \u6A21\u578B\u76EE\u5F55\u4E0E\u5382\u5546 CLI \u540D\u79F0\u53EF\u80FD\u4E0D\u540C\uFF0C\u56E2\u961F\u65E0\u6CD5\u786E\u8BA4\u6620\u5C04\u65F6\u4F7F\u7528 CLI \u9ED8\u8BA4\u6A21\u578B\uFF0C\u5B9E\u9645\u6A21\u578B\u4ECD\u987B\u6838\u5BF9\u8FD0\u884C\u8BB0\u5F55\u3002` } : {
|
|
1072
|
+
kind: "harness-llm",
|
|
1073
|
+
tool: tool.id,
|
|
1074
|
+
label: tool.label,
|
|
1075
|
+
detail: installed ? `${tool.label} \u5DF2\u5B89\u88C5\uFF0C\u4F46\u5F53\u524D\u5E73\u53F0\u7F3A\u5C11\u7ECF\u6838\u9A8C\u7684\u6258\u7BA1\u6267\u884C\u9002\u914D\u5668\uFF1B\u5B9E\u9645\u8C03\u7528\u4F7F\u7528\u5B98\u65B9\u6A21\u578B\u76EE\u5F55 API\u3002` : `${tool.label} \u672A\u5B89\u88C5\uFF1B\u5B9E\u9645\u8C03\u7528\u4F7F\u7528\u5B98\u65B9\u6A21\u578B\u76EE\u5F55 API\u3002\u53EF\u5728\u5DE5\u4F5C\u53F0\u4E00\u952E\u5B89\u88C5\uFF0C\u6216\u8FD0\u884C /tools install ${tool.id}\u3002`
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
function annotate(channel) {
|
|
1079
|
+
return {
|
|
1080
|
+
executionChannel: channel.kind,
|
|
1081
|
+
...channel.tool ? { channelTool: channel.tool } : {},
|
|
1082
|
+
...channel.label ? { channelLabel: channel.label } : {},
|
|
1083
|
+
channelDetail: channel.detail
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
function createPlanFromRoutes(task, availableRoutes, {
|
|
1087
|
+
mode = "single",
|
|
1088
|
+
budgetUsd = 0,
|
|
1089
|
+
installedToolIds = [],
|
|
1090
|
+
runnableToolIds = [],
|
|
1091
|
+
pricing = {},
|
|
1092
|
+
liveBench = null,
|
|
1093
|
+
cacheReadRatio = 0,
|
|
1094
|
+
cacheWriteRatio = 0
|
|
1095
|
+
} = {}) {
|
|
1096
|
+
const taskText = clean(task);
|
|
1097
|
+
if (!taskText) throw new Error("task must contain text");
|
|
1098
|
+
const selectedMode = mode === "team" ? "team" : "single";
|
|
1099
|
+
const routes = Array.isArray(availableRoutes) ? availableRoutes : [];
|
|
1100
|
+
const installedIds = Array.isArray(installedToolIds) ? installedToolIds.filter(Boolean) : [];
|
|
1101
|
+
const channelCache = /* @__PURE__ */ new Map();
|
|
1102
|
+
const channelOf = (provider) => {
|
|
1103
|
+
const key = String(provider ?? "");
|
|
1104
|
+
if (!channelCache.has(key)) channelCache.set(key, channelForProvider(key, installedIds, runnableToolIds));
|
|
1105
|
+
return channelCache.get(key);
|
|
1106
|
+
};
|
|
1107
|
+
const needsImage = detectTaskTypes(taskText).includes("vision");
|
|
1108
|
+
const plan = buildPlan({
|
|
1109
|
+
text: taskText,
|
|
1110
|
+
available: routes,
|
|
1111
|
+
mode: selectedMode,
|
|
1112
|
+
budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0),
|
|
1113
|
+
pricing,
|
|
1114
|
+
liveBench,
|
|
1115
|
+
cacheReadRatio,
|
|
1116
|
+
cacheWriteRatio
|
|
1117
|
+
});
|
|
1118
|
+
const selectedChannel = plan.selected ? channelOf(plan.selected.provider) : null;
|
|
1119
|
+
return {
|
|
1120
|
+
...plan,
|
|
1121
|
+
contractVersion: 2,
|
|
1122
|
+
availableRoutes: routes,
|
|
1123
|
+
...selectedChannel ? annotate(selectedChannel) : {},
|
|
1124
|
+
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",
|
|
1125
|
+
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",
|
|
1126
|
+
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",
|
|
1127
|
+
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,
|
|
1128
|
+
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",
|
|
1129
|
+
team: {
|
|
1130
|
+
requested: selectedMode === "team",
|
|
1131
|
+
recommended: selectedMode === "team" && plan.complexity.band === "complex" && plan.subtasks.length > 1,
|
|
1132
|
+
handoff: "\u53EF\u5728\u5B98\u65B9\u4F1A\u8BDD\u8C03\u7528 model_router_team_execute \u6258\u7BA1\u6267\u884C\u5F53\u524D\u5E73\u53F0\u652F\u6301\u7684\u5B98\u65B9 CLI \u5DE5\u4F5C\u5305\uFF1B\u5B98\u65B9 Agent Teams \u53EF\u534F\u4F5C\u7BA1\u7406\u4EFB\u52A1\uFF0C\u4F46\u6210\u5458\u6A21\u578B\u7531\u5BBF\u4E3B\u914D\u7F6E\uFF0C\u4E0D\u80FD\u76F4\u63A5\u6309\u672C\u8BA1\u5212\u9010\u4E2A\u5207\u6362\u3002",
|
|
1133
|
+
workPackages: plan.subtasks.map((item, index) => ({
|
|
1134
|
+
id: item.id,
|
|
1135
|
+
name: item.name,
|
|
1136
|
+
...item.objective ? { objective: item.objective } : {},
|
|
1137
|
+
type: item.type,
|
|
1138
|
+
purpose: item.purpose,
|
|
1139
|
+
difficulty: item.difficulty,
|
|
1140
|
+
qualitySource: item.qualitySource,
|
|
1141
|
+
pricingSource: item.pricingSource,
|
|
1142
|
+
dependsOn: item.dependsOn,
|
|
1143
|
+
recommendedProvider: item.recommendedProvider,
|
|
1144
|
+
recommendedModel: item.recommended,
|
|
1145
|
+
estimatedCost: plan.costBreakdown[index]?.estimatedCost ?? null,
|
|
1146
|
+
...item.recommendedReasoningEffort ? { recommendedReasoningEffort: item.recommendedReasoningEffort } : {},
|
|
1147
|
+
...annotate(channelOf(item.recommendedProvider)),
|
|
1148
|
+
verificationChecklist: item.purpose === "synthesis" ? ["\u6838\u5BF9\u5404\u5DE5\u4F5C\u5305\u4EA4\u4ED8\u7269\u4E0E\u4F9D\u8D56", "\u8BB0\u5F55\u51B2\u7A81\u3001\u672A\u89E3\u51B3\u4E8B\u9879\u548C\u6700\u7EC8\u9A8C\u6536\u7ED3\u679C"] : item.type === "code" ? ["\u8BF4\u660E\u6539\u52A8\u6587\u4EF6\u4E0E\u63A5\u53E3", "\u8FD0\u884C\u4E0E\u6539\u52A8\u76F8\u5173\u7684\u9A8C\u8BC1\u5E76\u8BB0\u5F55\u7ED3\u679C", "\u5217\u51FA\u5C1A\u672A\u5B8C\u6210\u7684\u8FB9\u754C\u60C5\u51B5"] : item.type === "research" ? ["\u5217\u51FA\u6765\u6E90\u3001\u65E5\u671F\u548C\u53EF\u6838\u5BF9\u7684\u7ED3\u8BBA", "\u6807\u51FA\u63A8\u65AD\u4E0E\u4E0D\u786E\u5B9A\u4E8B\u9879"] : ["\u5217\u51FA\u4EA4\u4ED8\u5185\u5BB9\u548C\u9A8C\u6536\u4F9D\u636E", "\u6807\u51FA\u672A\u5B8C\u6210\u4E8B\u9879"]
|
|
1149
|
+
}))
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
// .dsh-plugin/client/catalog.mjs
|
|
1155
|
+
var clean2 = (value) => typeof value === "string" ? value.trim() : "";
|
|
1156
|
+
function routesFromModelCatalog(catalog) {
|
|
1157
|
+
const groups = Array.isArray(catalog?.groups) ? catalog.groups : [];
|
|
1158
|
+
const routable = new Set(Array.isArray(catalog?.routableProviders) ? catalog.routableProviders : []);
|
|
1159
|
+
const routes = [];
|
|
1160
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1161
|
+
for (const group of groups) {
|
|
1162
|
+
const provider = clean2(group?.id);
|
|
1163
|
+
if (!provider || !routable.has(provider)) continue;
|
|
1164
|
+
for (const entry of Array.isArray(group.models) ? group.models : []) {
|
|
1165
|
+
const model = clean2(entry?.id);
|
|
1166
|
+
if (!model) continue;
|
|
1167
|
+
const key = `${provider}\0${model}`;
|
|
1168
|
+
if (seen.has(key)) continue;
|
|
1169
|
+
seen.add(key);
|
|
1170
|
+
const reasoning = entry?.reasoning;
|
|
1171
|
+
const efforts = Array.isArray(reasoning?.efforts) ? [...new Set(reasoning.efforts.map((item) => clean2(item?.id ?? item)).filter(Boolean))] : [];
|
|
1172
|
+
routes.push({
|
|
1173
|
+
provider,
|
|
1174
|
+
providerName: clean2(group.name) || provider,
|
|
1175
|
+
model,
|
|
1176
|
+
name: clean2(entry.name) || model,
|
|
1177
|
+
reasoningKnown: reasoning !== void 0 && reasoning !== null,
|
|
1178
|
+
reasoningEfforts: efforts,
|
|
1179
|
+
...clean2(reasoning?.defaultEffort) ? { defaultReasoningEffort: clean2(reasoning.defaultEffort) } : {},
|
|
1180
|
+
// modelCatalog deliberately omits modalities; unknown means the Host
|
|
1181
|
+
// must still validate image capability before a real request.
|
|
1182
|
+
inputModalities: []
|
|
1183
|
+
});
|
|
1184
|
+
}
|
|
1185
|
+
}
|
|
1186
|
+
routes.sort((left, right) => `${left.provider}/${left.model}`.localeCompare(`${right.provider}/${right.model}`));
|
|
1187
|
+
return routes;
|
|
1188
|
+
}
|
|
1189
|
+
function createWorkspacePlan(task, catalog, options = {}) {
|
|
1190
|
+
const routes = applyModelProfiles(
|
|
1191
|
+
routesFromModelCatalog(catalog),
|
|
1192
|
+
parseModelProfilesJson(options.modelProfilesJson ?? "[]")
|
|
1193
|
+
);
|
|
1194
|
+
return createPlanFromRoutes(task, routes, options);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
// .dsh-plugin/client/model-profile-editor.jsx
|
|
1198
|
+
var import_react = __toESM(require("react"), 1);
|
|
1199
|
+
|
|
1200
|
+
// .dsh-plugin/client/model-profile-editor-state.mjs
|
|
1201
|
+
var PROFILE_SPECIALTY_HINT = "code, math, research, summarization, writing, vision, reasoning";
|
|
1202
|
+
var profileRouteKey = (route) => `${String(route?.provider ?? "")}\0${String(route?.model ?? "")}`;
|
|
1203
|
+
var field = (value) => String(value ?? "").trim();
|
|
1204
|
+
function nonnegativeField(value, label, max = 1e6) {
|
|
1205
|
+
const raw = field(value);
|
|
1206
|
+
if (!raw) return null;
|
|
1207
|
+
const parsed = Number(raw);
|
|
1208
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > max) throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 ${max} \u7684\u6570\u5B57\u3002`);
|
|
1209
|
+
return parsed;
|
|
1210
|
+
}
|
|
1211
|
+
function profileDraft(profile) {
|
|
1212
|
+
return {
|
|
1213
|
+
quality: profile?.quality === void 0 ? "" : String(Number((profile.quality * 100).toFixed(4))),
|
|
1214
|
+
input: profile?.pricing?.input === void 0 ? "" : String(profile.pricing.input),
|
|
1215
|
+
output: profile?.pricing?.output === void 0 ? "" : String(profile.pricing.output),
|
|
1216
|
+
cacheRead: profile?.pricing?.cacheRead === void 0 ? "" : String(profile.pricing.cacheRead),
|
|
1217
|
+
cacheWrite: profile?.pricing?.cacheWrite === void 0 ? "" : String(profile.pricing.cacheWrite),
|
|
1218
|
+
specialties: Array.isArray(profile?.specialties) ? profile.specialties.join(", ") : "",
|
|
1219
|
+
cliModel: profile?.cliModel ?? ""
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
function profileFromDraft(route, draft) {
|
|
1223
|
+
const provider = field(route?.provider);
|
|
1224
|
+
const model = field(route?.model);
|
|
1225
|
+
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");
|
|
1226
|
+
const profile = { provider, model };
|
|
1227
|
+
const quality = nonnegativeField(draft?.quality, "\u8D28\u91CF\u8BC4\u5206", 100);
|
|
1228
|
+
if (quality !== null) profile.quality = quality;
|
|
1229
|
+
const input = nonnegativeField(draft?.input, "\u8F93\u5165\u5355\u4EF7");
|
|
1230
|
+
const output = nonnegativeField(draft?.output, "\u8F93\u51FA\u5355\u4EF7");
|
|
1231
|
+
const cacheRead = nonnegativeField(draft?.cacheRead, "\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7");
|
|
1232
|
+
const cacheWrite = nonnegativeField(draft?.cacheWrite, "\u7F13\u5B58\u5199\u5165\u5355\u4EF7");
|
|
1233
|
+
if (input === null !== (output === null)) throw new Error("\u8F93\u5165\u548C\u8F93\u51FA\u5355\u4EF7\u9700\u8981\u540C\u65F6\u586B\u5199\uFF1B\u7559\u7A7A\u8868\u793A\u4EF7\u683C\u672A\u77E5\u3002");
|
|
1234
|
+
if (input === null && (cacheRead !== null || cacheWrite !== null)) throw new Error("\u586B\u5199\u7F13\u5B58\u5355\u4EF7\u524D\uFF0C\u8BF7\u5148\u586B\u5199\u8F93\u5165\u548C\u8F93\u51FA\u5355\u4EF7\u3002");
|
|
1235
|
+
if (input !== null) {
|
|
1236
|
+
profile.pricing = { input, output, currency: "USD" };
|
|
1237
|
+
if (cacheRead !== null) profile.pricing.cacheRead = cacheRead;
|
|
1238
|
+
if (cacheWrite !== null) profile.pricing.cacheWrite = cacheWrite;
|
|
1239
|
+
}
|
|
1240
|
+
const rawSpecialties = field(draft?.specialties);
|
|
1241
|
+
if (rawSpecialties) {
|
|
1242
|
+
const specialties = [...new Set(rawSpecialties.split(/[,,\s]+/u).filter(Boolean))];
|
|
1243
|
+
if (specialties.length > 16 || specialties.some((item) => !/^[a-z][a-z0-9-]{0,39}$/.test(item))) {
|
|
1244
|
+
throw new Error("\u64C5\u957F\u65B9\u5411\u6700\u591A 16 \u9879\uFF0C\u7528\u82F1\u6587\u5C0F\u5199\u6807\u7B7E\u5E76\u4EE5\u9017\u53F7\u5206\u9694\uFF0C\u4F8B\u5982 code, research\u3002");
|
|
1245
|
+
}
|
|
1246
|
+
profile.specialties = specialties;
|
|
1247
|
+
}
|
|
1248
|
+
const cliModel = field(draft?.cliModel);
|
|
1249
|
+
if (cliModel) {
|
|
1250
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$/.test(cliModel)) throw new Error("CLI \u6A21\u578B\u540D\u9700\u7531\u5B57\u6BCD\u6570\u5B57\u5F00\u5934\uFF0C\u4E14\u53EA\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u3001\u70B9\u3001\u4E0B\u5212\u7EBF\u3001\u5192\u53F7\u3001\u659C\u6760\u6216\u8FDE\u5B57\u7B26\u3002");
|
|
1251
|
+
profile.cliModel = cliModel;
|
|
1252
|
+
}
|
|
1253
|
+
return profile;
|
|
1254
|
+
}
|
|
1255
|
+
function updateProfileJson(rawJson, route, draft) {
|
|
1256
|
+
const profiles = parseModelProfilesJson(rawJson ?? "[]");
|
|
1257
|
+
const candidate = draft === null ? null : profileFromDraft(route, draft);
|
|
1258
|
+
const selected = profileRouteKey(route);
|
|
1259
|
+
const others = profiles.filter((profile) => profileRouteKey(profile) !== selected).map((profile) => ({
|
|
1260
|
+
...profile,
|
|
1261
|
+
...profile.quality === void 0 ? {} : { quality: Number((profile.quality * 100).toFixed(6)) }
|
|
1262
|
+
}));
|
|
1263
|
+
const next = candidate && Object.keys(candidate).length > 2 ? [...others, candidate] : others;
|
|
1264
|
+
const json = JSON.stringify(next, null, 2);
|
|
1265
|
+
parseModelProfilesJson(json);
|
|
1266
|
+
return json;
|
|
1267
|
+
}
|
|
1268
|
+
|
|
1269
|
+
// .dsh-plugin/client/model-profile-editor.jsx
|
|
1270
|
+
var messageOf = (error) => typeof error?.message === "string" ? error.message : "\u6A21\u578B\u914D\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002";
|
|
1271
|
+
function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
1272
|
+
const [snapshot, setSnapshot] = import_react.default.useState(() => settingsScope.getSnapshot());
|
|
1273
|
+
const [selected, setSelected] = import_react.default.useState("");
|
|
1274
|
+
const [drafts, setDrafts] = import_react.default.useState({});
|
|
1275
|
+
const [saving, setSaving] = import_react.default.useState(false);
|
|
1276
|
+
const [notice, setNotice] = import_react.default.useState(null);
|
|
1277
|
+
import_react.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
|
|
1278
|
+
const route = routes.find((item) => profileRouteKey(item) === selected) ?? routes[0];
|
|
1279
|
+
const routeKey2 = route ? profileRouteKey(route) : "";
|
|
1280
|
+
const rawJson = snapshot.value?.modelProfilesJson ?? "[]";
|
|
1281
|
+
let profiles = [];
|
|
1282
|
+
let loadError = "";
|
|
1283
|
+
try {
|
|
1284
|
+
profiles = parseModelProfilesJson(rawJson);
|
|
1285
|
+
} catch (error) {
|
|
1286
|
+
loadError = messageOf(error);
|
|
1287
|
+
}
|
|
1288
|
+
const saved = profiles.find((item) => profileRouteKey(item) === routeKey2);
|
|
1289
|
+
const pending = drafts[routeKey2];
|
|
1290
|
+
const draft = pending?.fields ?? profileDraft(saved);
|
|
1291
|
+
const writable = snapshot.status === "ready" && snapshot.writable === true && !saving && !loadError;
|
|
1292
|
+
const unmatched = profiles.filter((item) => !routes.some((route2) => profileRouteKey(route2) === profileRouteKey(item))).length;
|
|
1293
|
+
const edit = (name, value) => {
|
|
1294
|
+
if (!route || !writable) return;
|
|
1295
|
+
setDrafts((previous) => {
|
|
1296
|
+
const previousEntry = previous[routeKey2] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
|
|
1297
|
+
return { ...previous, [routeKey2]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
|
|
1298
|
+
});
|
|
1299
|
+
setNotice(null);
|
|
1300
|
+
};
|
|
1301
|
+
const write = async (remove) => {
|
|
1302
|
+
if (!route || !writable) return;
|
|
1303
|
+
const revision = pending?.baseRevision ?? snapshot.revision;
|
|
1304
|
+
if (revision !== snapshot.revision) {
|
|
1305
|
+
setNotice({ tone: "error", text: "\u8BBE\u7F6E\u5DF2\u5728\u5176\u4ED6\u9875\u9762\u66F4\u65B0\u3002\u5F53\u524D\u8F93\u5165\u4ECD\u5728\uFF1B\u8BF7\u6838\u5BF9\u6700\u65B0\u5185\u5BB9\uFF0C\u518D\u9009\u62E9\u201C\u91CD\u65B0\u52A0\u8F7D\u6B64\u8DEF\u7EBF\u201D\u540E\u7F16\u8F91\u3002" });
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
let nextJson;
|
|
1309
|
+
try {
|
|
1310
|
+
nextJson = updateProfileJson(rawJson, route, remove ? null : draft);
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
setNotice({ tone: "error", text: messageOf(error) });
|
|
1313
|
+
return;
|
|
1314
|
+
}
|
|
1315
|
+
setSaving(true);
|
|
1316
|
+
setNotice(null);
|
|
1317
|
+
try {
|
|
1318
|
+
const accepted = await settingsScope.mutate([{ op: "set", path: ["modelProfilesJson"], value: nextJson }], revision);
|
|
1319
|
+
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");
|
|
1320
|
+
setDrafts((previous) => {
|
|
1321
|
+
const next = { ...previous };
|
|
1322
|
+
delete next[routeKey2];
|
|
1323
|
+
return next;
|
|
1324
|
+
});
|
|
1325
|
+
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" });
|
|
1326
|
+
onSaved?.();
|
|
1327
|
+
} catch (error) {
|
|
1328
|
+
setNotice({ tone: "error", text: messageOf(error) });
|
|
1329
|
+
} finally {
|
|
1330
|
+
setSaving(false);
|
|
1331
|
+
}
|
|
1332
|
+
};
|
|
1333
|
+
const reload = () => {
|
|
1334
|
+
setDrafts((previous) => {
|
|
1335
|
+
const next = { ...previous };
|
|
1336
|
+
delete next[routeKey2];
|
|
1337
|
+
return next;
|
|
1338
|
+
});
|
|
1339
|
+
setNotice(null);
|
|
1340
|
+
};
|
|
1341
|
+
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: routeKey2, onChange: (event) => {
|
|
1342
|
+
setSelected(event.target.value);
|
|
1343
|
+
setNotice(null);
|
|
1344
|
+
} }, 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("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: () => {
|
|
1345
|
+
void write(false);
|
|
1346
|
+
} }, 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: () => {
|
|
1347
|
+
void write(true);
|
|
1348
|
+
} }, "\u5220\u9664\u6B64\u6A21\u578B\u914D\u7F6E")), notice && /* @__PURE__ */ import_react.default.createElement("p", { className: notice.tone === "error" ? "mr-error" : "mr-profile-success", role: notice.tone === "error" ? "alert" : "status" }, notice.text), /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption" }, "\u8D28\u91CF\u8BC4\u5206\u548C\u4EF7\u683C\u90FD\u662F\u7528\u6237\u63D0\u4F9B\u7684\u4F30\u503C\u3002\u672A\u586B\u5199\u5355\u4EF7\u65F6\u663E\u793A\u201C\u4EF7\u683C\u5F85\u914D\u7F6E\u201D\uFF1B\u9884\u7B97\u53EA\u5F71\u54CD\u672C\u5730\u89C4\u5212\uFF0C\u4E0D\u9650\u5236\u5B9E\u9645\u8D26\u5355\u3002CLI \u6A21\u578B\u540D\u987B\u4E0E\u5382\u5546\u5DE5\u5177\u6838\u5BF9\u3002"))));
|
|
1349
|
+
}
|
|
1350
|
+
|
|
1351
|
+
// .dsh-plugin/client/tool-install-state.mjs
|
|
1352
|
+
function stableVersionOrder(left, right) {
|
|
1353
|
+
const parse = (value) => /^([0-9]+)\.([0-9]+)\.([0-9]+)$/.exec(String(value ?? ""));
|
|
1354
|
+
const current = parse(left);
|
|
1355
|
+
const target = parse(right);
|
|
1356
|
+
if (!current || !target) return null;
|
|
1357
|
+
for (let index = 1; index <= 3; index += 1) {
|
|
1358
|
+
const delta = Number(current[index]) - Number(target[index]);
|
|
1359
|
+
if (delta) return Math.sign(delta);
|
|
1360
|
+
}
|
|
1361
|
+
return 0;
|
|
1362
|
+
}
|
|
1363
|
+
function toolInstallAction({ tool, probe, readiness, job, probeStatus }) {
|
|
1364
|
+
const running = job?.status === "running";
|
|
1365
|
+
const versionOrder = probe?.installed ? stableVersionOrder(probe.version, tool.version) : null;
|
|
1366
|
+
const currentAndReady = versionOrder === 0 && readiness?.ready === true;
|
|
1367
|
+
const repairable = versionOrder === 0 && readiness?.ready === false;
|
|
1368
|
+
const newerOrUncertain = probe?.installed && (versionOrder === null || versionOrder > 0);
|
|
1369
|
+
const label = running ? "\u5B89\u88C5\u4E2D\u2026" : currentAndReady ? "\u5DF2\u662F\u76EE\u6807\u7248\u672C" : repairable ? "\u4FEE\u590D\u5B98\u65B9\u6267\u884C\u5165\u53E3" : newerOrUncertain ? "\u8BF7\u4EBA\u5DE5\u6838\u5BF9\u7248\u672C" : probe?.installed ? "\u66F4\u65B0\u5230\u76EE\u6807\u7248\u672C" : job?.status === "failed" ? "\u91CD\u8BD5\u5B89\u88C5" : tool.manager === "signed-windows-installer" ? "\u4E0B\u8F7D\u5B89\u88C5\u5668" : "\u4E0B\u8F7D\u5B89\u88C5";
|
|
1370
|
+
return {
|
|
1371
|
+
label,
|
|
1372
|
+
disabled: probeStatus !== "ready" || running || currentAndReady || newerOrUncertain
|
|
1373
|
+
};
|
|
1374
|
+
}
|
|
1375
|
+
|
|
1376
|
+
// .dsh-plugin/client/router-main.css
|
|
1377
|
+
var router_main_default = ".mr-workspace {\r\n --mr-card: rgba(255, 255, 255, .94);\r\n --mr-ink: #182232;\r\n --mr-muted: #526174;\r\n --mr-line: rgba(38, 55, 75, .13);\r\n --mr-accent: #315cc8;\r\n --mr-soft: #edf3ff;\r\n box-sizing: border-box;\r\n width: 100%;\r\n height: 100%;\r\n min-height: 0;\r\n overflow-y: auto;\r\n background: rgba(246, 249, 253, .9);\r\n backdrop-filter: blur(18px);\r\n color: var(--mr-ink);\r\n font-family: inherit;\r\n}\r\n.mr-workspace *, .mr-workspace *::before, .mr-workspace *::after { box-sizing: border-box; }\r\n.mr-shell { width: min(1160px, 100%); margin: 0 auto; padding: 36px 32px 64px; }\r\n.mr-header { display: flex; flex-wrap: wrap; gap: 20px; align-items: end; justify-content: space-between; margin-bottom: 24px; }\r\n.mr-eyebrow { margin: 0 0 8px; color: var(--mr-accent); font-size: 11px; font-weight: 750; letter-spacing: .14em; text-transform: uppercase; }\r\n.mr-title { margin: 0; font-size: clamp(25px, 3vw, 34px); letter-spacing: -.035em; line-height: 1.16; }\r\n.mr-subtitle { margin: 10px 0 0; color: var(--mr-muted); font-size: 14px; line-height: 1.65; }\r\n.mr-status { display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; border: 1px solid var(--mr-line); border-radius: 999px; background: var(--mr-card); color: var(--mr-muted); font-size: 12px; white-space: nowrap; }\r\n.mr-status-dot { width: 7px; height: 7px; border-radius: 50%; background: #2cba83; }\r\n.mr-status-dot.loading { background: #e9a640; }\r\n.mr-status-dot.error { background: #d95360; }\r\n.mr-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(280px, .85fr); gap: 18px; align-items: start; }\r\n.mr-card { min-width: 0; border: 1px solid var(--mr-line); border-radius: 18px; background: var(--mr-card); box-shadow: 0 12px 42px rgba(25, 45, 76, .08); }\r\n.mr-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 20px 22px 0; }\r\n.mr-card-title { margin: 0; font-size: 16px; font-weight: 700; }\r\n.mr-card-copy { margin: 5px 0 0; color: var(--mr-muted); font-size: 12px; line-height: 1.55; }\r\n.mr-card-body { padding: 18px 22px 22px; }\r\n.mr-label { display: block; margin: 0 0 8px; font-size: 12px; font-weight: 700; }\r\n.mr-textarea, .mr-input { width: 100%; border: 1px solid var(--mr-line); border-radius: 11px; background: #fff; color: var(--mr-ink); font: inherit; outline: none; }\r\n.mr-textarea { min-height: 154px; padding: 13px 14px; resize: vertical; line-height: 1.6; font-size: 14px; }\r\n.mr-input { min-height: 38px; padding: 8px 11px; font-size: 13px; }\r\n.mr-textarea:focus, .mr-input:focus { border-color: var(--mr-accent); box-shadow: 0 0 0 3px rgba(49, 92, 200, .13); }\r\n.mr-controls { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 14px; margin-top: 17px; }\r\n.mr-control-group { display: flex; flex-direction: column; gap: 7px; }\r\n.mr-control-label { color: var(--mr-muted); font-size: 11px; font-weight: 650; }\r\n.mr-segment { display: inline-flex; padding: 3px; border-radius: 10px; background: #edf1f7; }\r\n.mr-segment button { border: 0; border-radius: 8px; padding: 8px 12px; background: transparent; color: var(--mr-muted); font: inherit; font-size: 12px; cursor: pointer; }\r\n.mr-segment button[aria-pressed='true'] { background: #fff; color: var(--mr-ink); box-shadow: 0 2px 8px rgba(20, 35, 56, .1); font-weight: 700; }\r\n.mr-budget { width: 140px; }\r\n.mr-actions { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-top: 18px; }\r\n.mr-button { border: 1px solid transparent; border-radius: 10px; min-height: 38px; padding: 8px 14px; background: var(--mr-accent); color: #fff; font: inherit; font-size: 13px; font-weight: 700; cursor: pointer; }\r\n.mr-button:hover:not(:disabled) { filter: brightness(.94); }\r\n.mr-button:disabled { cursor: not-allowed; opacity: .5; }\r\n.mr-button-secondary { border-color: var(--mr-line); background: #fff; color: var(--mr-ink); }\r\n.mr-caption { color: var(--mr-muted); font-size: 11px; line-height: 1.5; }\r\n.mr-search { margin-top: 14px; }\r\n.mr-list { display: grid; gap: 8px; margin-top: 13px; max-height: 395px; overflow-y: auto; }\r\n.mr-route { display: flex; justify-content: space-between; gap: 10px; padding: 10px 11px; border: 1px solid var(--mr-line); border-radius: 10px; background: #f9fbfe; }\r\n.mr-route-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 700; }\r\n.mr-route-provider { margin-top: 2px; color: var(--mr-muted); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\r\n.mr-pill { align-self: start; flex: none; padding: 3px 7px; border-radius: 7px; background: var(--mr-soft); color: var(--mr-accent); font-size: 10px; font-weight: 700; }\r\n.mr-empty, .mr-error { padding: 14px; border-radius: 10px; background: #f4f6fa; color: var(--mr-muted); font-size: 12px; line-height: 1.6; }\r\n.mr-error { background: #fff0f1; color: #a52d3c; }\r\n.mr-results { margin-top: 18px; }\r\n.mr-result-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 16px; }\r\n.mr-metric { padding: 13px; border-radius: 12px; background: #f5f8fd; }\r\n.mr-metric-label { color: var(--mr-muted); font-size: 10px; }\r\n.mr-metric-value { margin-top: 5px; font-size: 15px; font-weight: 750; word-break: break-word; }\r\n.mr-section-title { margin: 20px 0 10px; font-size: 13px; font-weight: 750; }\r\n.mr-package { padding: 13px 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: #fafcff; }\r\n.mr-package + .mr-package { margin-top: 8px; }\r\n.mr-package-top { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; align-items: center; }\r\n.mr-package-name { font-size: 12px; font-weight: 750; }\r\n.mr-package-route { color: var(--mr-accent); font-size: 11px; font-weight: 700; }\r\n.mr-package-copy { margin: 7px 0 0; color: var(--mr-muted); font-size: 11px; line-height: 1.6; }\r\n.mr-notice { margin-top: 18px; padding: 14px 16px; border: 1px solid rgba(49, 92, 200, .16); border-radius: 12px; background: #eff4ff; color: #354d7a; font-size: 11px; line-height: 1.65; }\r\n.mr-notice code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; }\r\n@media (max-width: 850px) { .mr-grid { grid-template-columns: 1fr; } .mr-shell { padding: 24px 18px 48px; } }\r\n@media (prefers-color-scheme: dark) {\r\n .mr-workspace { --mr-card: rgba(30, 38, 51, .94); --mr-ink: #ecf2ff; --mr-muted: #aebbd0; --mr-line: rgba(210, 224, 245, .15); --mr-accent: #91adff; --mr-soft: rgba(97, 132, 222, .18); background: rgba(18, 23, 32, .91); }\r\n .mr-textarea, .mr-input, .mr-segment button[aria-pressed='true'], .mr-button-secondary { background: #273245; color: var(--mr-ink); }\r\n .mr-segment, .mr-empty, .mr-metric { background: #222d3e; }\r\n .mr-route, .mr-package { background: #222d3e; }\r\n .mr-notice { background: #243454; color: #c9d6fa; }\r\n .mr-error { background: #4a2730; color: #ffd4da; }\r\n}\r\n\r\n/* \u5B98\u65B9\u5DE5\u5177\u5361\u7247 */\r\n.mr-tools { display: grid; gap: 9px; }\r\n.mr-tool { display: flex; align-items: center; justify-content: space-between; gap: 16px; padding: 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: var(--mr-card); }\r\n.mr-tool-info { min-width: 0; flex: 1; }\r\n.mr-tool-status { display: flex; align-items: center; gap: 6px; margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\r\n.mr-tool-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: #8d9aaa; }\r\n.mr-tool-dot.installed { background: #2cba83; }\r\n.mr-tool-dot.running { background: #e9a640; }\r\n.mr-tool-command { display: block; margin-top: 6px; padding: 4px 8px; font-size: 12px; background: var(--dsw-alias-markdown-code-block); border-radius: 6px; overflow-wrap: anywhere; }\r\n.mr-tool-button { flex-shrink: 0; }\r\n.mr-tool-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }\r\n.mr-tool-detail { margin: 7px 0 0; }\r\n.mr-tool-error { margin: 8px 0 0; padding: 9px 11px; }\r\n.mr-tool-log { margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\r\n.mr-tool-log summary { cursor: pointer; }\r\n.mr-tool-log pre { max-height: 140px; overflow: auto; padding: 8px; border-radius: 7px; background: var(--dsw-alias-markdown-code-block); font-size: 10px; white-space: pre-wrap; overflow-wrap: anywhere; }\r\n.mr-channel-line { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }\r\n.mr-pill-channel-ok { border-color: var(--dsw-alias-success, #2f9e63); color: var(--dsw-alias-success, #2f9e63); }\n.mr-profile-card { margin-top: 18px; }\n.mr-profile-state { display: block; margin: 8px 0 0; }\n.mr-profile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); gap: 12px; margin-top: 15px; }\n.mr-profile-field { display: grid; align-content: start; gap: 6px; color: var(--mr-muted); font-size: 11px; font-weight: 650; }\n.mr-profile-field .mr-input { color: var(--mr-ink); }\n.mr-profile-advanced { margin-top: 14px; color: var(--mr-muted); font-size: 11px; }\n.mr-profile-advanced summary { cursor: pointer; }\n.mr-profile-success { padding: 10px 12px; border-radius: 10px; background: #e5f5eb; color: #175f3a; font-size: 12px; }\n@media (prefers-color-scheme: dark) { .mr-profile-success { background: #193b2b; color: #b4f1cc; } }\n@media (max-width: 560px) { .mr-tool { align-items: stretch; flex-direction: column; gap: 10px; } .mr-tool-button { align-self: flex-start; } }\n";
|
|
1378
|
+
|
|
1379
|
+
// .dsh-plugin/client/router-main.jsx
|
|
1380
|
+
var money = (value) => value === null || value === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : `$${Number(value).toFixed(4)}`;
|
|
1381
|
+
var text = (value) => typeof value === "string" ? value.trim() : "";
|
|
1382
|
+
function RouterPanelIcon({ size = 20, active = false }) {
|
|
1383
|
+
return /* @__PURE__ */ import_react2.default.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ import_react2.default.createElement("path", { d: "M7 6.5h7M7 17.5h7M15 6.5v11", stroke: "currentColor", strokeWidth: "1.8", strokeLinecap: "round" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "5", cy: "6.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "5", cy: "17.5", r: "2", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.6" }), /* @__PURE__ */ import_react2.default.createElement("circle", { cx: "17", cy: "12", r: "3", fill: active ? "currentColor" : "none", stroke: "currentColor", strokeWidth: "1.7" }));
|
|
1384
|
+
}
|
|
1385
|
+
function RouteList({ routes, query }) {
|
|
1386
|
+
const filtered = routes.filter((route) => {
|
|
1387
|
+
const haystack = `${route.providerName} ${route.provider} ${route.name} ${route.model}`.toLowerCase();
|
|
1388
|
+
return haystack.includes(query.toLowerCase());
|
|
1389
|
+
});
|
|
1390
|
+
if (filtered.length === 0) return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-empty" }, routes.length === 0 ? "\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u5C1A\u65E0\u53EF\u89C4\u5212\u7684\u8DEF\u7EBF\u3002\u8BF7\u5148\u5728\u201C\u6A21\u578B\u201D\u9875\u5B8C\u6210\u914D\u7F6E\u3002" : "\u6CA1\u6709\u5339\u914D\u7684\u6A21\u578B\u3002");
|
|
1391
|
+
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-list", role: "list", "aria-label": "\u5B98\u65B9\u5DF2\u767B\u8BB0\u6A21\u578B\u8DEF\u7EBF" }, filtered.map((route) => /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route", role: "listitem", key: `${route.provider}/${route.model}` }, /* @__PURE__ */ import_react2.default.createElement("div", { style: { minWidth: 0 } }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route-name", title: route.name }, route.name), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route-provider", title: `${route.provider}/${route.model}` }, route.provider, "/", route.model)), route.reasoningKnown && /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-pill" }, "\u63A8\u7406\u7B49\u7EA7"))));
|
|
1392
|
+
}
|
|
1393
|
+
function ChannelBadge({ item }) {
|
|
1394
|
+
if (!item?.executionChannel) return null;
|
|
1395
|
+
const official = item.executionChannel === "official-cli";
|
|
1396
|
+
return /* @__PURE__ */ import_react2.default.createElement("span", { className: official ? "mr-pill mr-pill-channel-ok" : "mr-pill", title: item.channelDetail ?? "" }, official ? `\u5B98\u65B9 CLI \xB7 ${item.channelLabel ?? item.channelTool}` : "\u6A21\u578B\u76EE\u5F55 API");
|
|
1397
|
+
}
|
|
1398
|
+
function PlanResults({ plan }) {
|
|
1399
|
+
const selected = plan.selected;
|
|
1400
|
+
return /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card mr-results", "aria-label": "\u8DEF\u7531\u5EFA\u8BAE" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u672C\u5730\u8BA1\u7B97\u5B8C\u6210\uFF0C\u672A\u5411\u6A21\u578B\u53D1\u9001\u4EFB\u52A1\u5185\u5BB9\u3002"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-result-grid" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u63A8\u8350\u8DEF\u7EBF"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, selected ? `${selected.provider}/${selected.model}` : "\u6682\u65E0\u8DEF\u7EBF")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u4EFB\u52A1\u590D\u6742\u5EA6"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u590D\u6742" }[plan.complexity.band] || plan.complexity.band)), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-label" }, "\u4F30\u7B97\u603B\u6210\u672C"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-metric-value" }, money(plan.estimatedCost)))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u6267\u884C\u6E20\u9053"), /* @__PURE__ */ import_react2.default.createElement(ChannelBadge, { item: plan })), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, plan.reason), plan.optimization.budgetExceeded && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error" }, "\u6309\u5DF2\u63D0\u4F9B\u5355\u4EF7\u4F30\u7B97\uFF0C\u4EFB\u52A1\u53EF\u80FD\u8D85\u8FC7\u672C\u6B21\u9884\u7B97\u3002\u9884\u7B97\u53EA\u5F71\u54CD\u5EFA\u8BAE\uFF0C\u4E0D\u4F1A\u963B\u6B62\u5B9E\u9645\u6263\u8D39\u3002"), plan.mode === "team" && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("h3", { className: "mr-section-title" }, "\u56E2\u961F\u5DE5\u4F5C\u5305"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, "\u4E0B\u65B9\u6A21\u578B\u662F\u89C4\u5212\u5EFA\u8BAE\uFF1B\u6258\u7BA1\u6267\u884C\u4F1A\u6309\u5382\u5546 CLI \u7684\u6A21\u578B\u540D\u89C4\u5219\u9009\u7528\uFF0C\u672A\u6838\u9A8C\u6620\u5C04\u65F6\u4F7F\u7528\u8BE5 CLI \u7684\u9ED8\u8BA4\u6A21\u578B\u3002"), plan.team.workPackages.length === 0 ? /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-empty" }, "\u5F53\u524D\u76EE\u5F55\u6CA1\u6709\u53EF\u5206\u914D\u7684\u6A21\u578B\u8DEF\u7EBF\u3002") : plan.team.workPackages.map((item, index) => /* @__PURE__ */ import_react2.default.createElement("article", { className: "mr-package", key: item.id }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-package-top" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-package-name" }, index + 1, ". ", item.name), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-package-route" }, item.recommendedProvider, "/", item.recommendedModel)), item.objective && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u5177\u4F53\u76EE\u6807\uFF1A", item.objective), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, item.purpose, item.dependsOn.length > 0 ? ` \xB7 \u4F9D\u8D56\uFF1A${item.dependsOn.join("\u3001")}` : ""), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u96BE\u5EA6\uFF1A", { simple: "\u7B80\u5355", balanced: "\u4E2D\u7B49", complex: "\u56F0\u96BE" }[item.difficulty] || item.difficulty || "\u5F85\u8BC4\u4F30", " \xB7 \u8D39\u7528\uFF1A", money(item.estimatedCost)), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-package-copy" }, "\u9A8C\u6536\uFF1A", item.verificationChecklist.join("\uFF1B")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-channel-line" }, /* @__PURE__ */ import_react2.default.createElement(ChannelBadge, { item }))))), plan.mode === "team" && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, plan.team.handoff), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-notice" }, plan.pricingNotice, " ", plan.qualityNotice, " ", plan.availabilityNotice, " ", plan.modalityNotice || "")));
|
|
1401
|
+
}
|
|
1402
|
+
var remoteError = (response, fallback) => text(response?.error?.message) || text(response?.value?.error) || fallback;
|
|
1403
|
+
function probeLabel(probe) {
|
|
1404
|
+
if (!probe) return "\u5C1A\u672A\u68C0\u6D4B";
|
|
1405
|
+
if (probe.installed) return `\u5DF2\u5B89\u88C5${probe.version ? ` \xB7 ${probe.version}` : ""}`;
|
|
1406
|
+
if (probe.status === "not-installed") return "\u672A\u5B89\u88C5";
|
|
1407
|
+
if (probe.status === "probe-timeout") return "\u68C0\u6D4B\u8D85\u65F6";
|
|
1408
|
+
if (probe.status === "probe-failed") return "\u68C0\u6D4B\u5931\u8D25";
|
|
1409
|
+
return probe.detail || "\u672A\u5B89\u88C5";
|
|
1410
|
+
}
|
|
1411
|
+
function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes }) {
|
|
1412
|
+
const [probeState, setProbeState] = import_react2.default.useState({ status: "loading", probes: [], capabilities: [], readiness: [], error: "" });
|
|
1413
|
+
const [jobs, setJobs] = import_react2.default.useState({});
|
|
1414
|
+
const [rowErrors, setRowErrors] = import_react2.default.useState({});
|
|
1415
|
+
const mounted = import_react2.default.useRef(false);
|
|
1416
|
+
const request = import_react2.default.useRef(0);
|
|
1417
|
+
const submitting = import_react2.default.useRef(/* @__PURE__ */ new Set());
|
|
1418
|
+
const polling = import_react2.default.useRef(/* @__PURE__ */ new Set());
|
|
1419
|
+
const refresh = async () => {
|
|
1420
|
+
const current = ++request.current;
|
|
1421
|
+
setProbeState((previous) => ({ ...previous, status: "loading", error: "" }));
|
|
1422
|
+
try {
|
|
1423
|
+
if (typeof listOfficialTools !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
|
|
1424
|
+
const response = await listOfficialTools();
|
|
1425
|
+
if (!mounted.current || current !== request.current) return;
|
|
1426
|
+
if (!response?.ok) throw new Error(remoteError(response, "\u65E0\u6CD5\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u3002"));
|
|
1427
|
+
const probes = Array.isArray(response.value?.tools) ? response.value.tools : [];
|
|
1428
|
+
const capabilities = Array.isArray(response.value?.executionCapabilities) ? response.value.executionCapabilities : [];
|
|
1429
|
+
const readiness = Array.isArray(response.value?.executionReadiness) ? response.value.executionReadiness : [];
|
|
1430
|
+
setProbeState({ status: "ready", probes, capabilities, readiness, error: "" });
|
|
1431
|
+
onProbes({ probes, capabilities, readiness });
|
|
1432
|
+
} catch (error) {
|
|
1433
|
+
if (!mounted.current || current !== request.current) return;
|
|
1434
|
+
setProbeState({ status: "error", probes: [], capabilities: [], readiness: [], error: text(error?.message) || "\u65E0\u6CD5\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u3002" });
|
|
1435
|
+
onProbes({ probes: [], capabilities: [], readiness: [] });
|
|
1436
|
+
}
|
|
1437
|
+
};
|
|
1438
|
+
import_react2.default.useEffect(() => {
|
|
1439
|
+
mounted.current = true;
|
|
1440
|
+
void refresh();
|
|
1441
|
+
if (typeof officialToolInstallStatus === "function") {
|
|
1442
|
+
void Promise.all(OFFICIAL_TOOLS.map(async (tool) => {
|
|
1443
|
+
try {
|
|
1444
|
+
const response = await officialToolInstallStatus(tool.id);
|
|
1445
|
+
return response?.ok && response.value?.job ? [tool.id, response.value.job] : null;
|
|
1446
|
+
} catch {
|
|
1447
|
+
return null;
|
|
1448
|
+
}
|
|
1449
|
+
})).then((entries) => {
|
|
1450
|
+
if (mounted.current) setJobs((previous) => ({ ...Object.fromEntries(entries.filter(Boolean)), ...previous }));
|
|
1451
|
+
});
|
|
1452
|
+
}
|
|
1453
|
+
return () => {
|
|
1454
|
+
mounted.current = false;
|
|
1455
|
+
request.current += 1;
|
|
1456
|
+
};
|
|
1457
|
+
}, []);
|
|
1458
|
+
import_react2.default.useEffect(() => {
|
|
1459
|
+
const active = Object.values(jobs).filter((job) => job?.status === "running").map((job) => job.tool);
|
|
1460
|
+
if (active.length === 0 || typeof officialToolInstallStatus !== "function") return void 0;
|
|
1461
|
+
let listening = true;
|
|
1462
|
+
const poll = async (id2) => {
|
|
1463
|
+
if (polling.current.has(id2)) return;
|
|
1464
|
+
polling.current.add(id2);
|
|
1465
|
+
try {
|
|
1466
|
+
const response = await officialToolInstallStatus(id2);
|
|
1467
|
+
if (!listening || !mounted.current) return;
|
|
1468
|
+
if (!response?.ok) throw new Error(remoteError(response, "\u65E0\u6CD5\u83B7\u53D6\u5B89\u88C5\u8FDB\u5EA6\u3002"));
|
|
1469
|
+
const job = response.value?.job;
|
|
1470
|
+
if (!job) throw new Error("\u5B89\u88C5\u4EFB\u52A1\u72B6\u6001\u6682\u4E0D\u53EF\u7528\u3002");
|
|
1471
|
+
setJobs((previous) => ({ ...previous, [id2]: job }));
|
|
1472
|
+
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
1473
|
+
if (job.status !== "running") void refresh();
|
|
1474
|
+
} catch (error) {
|
|
1475
|
+
if (listening && mounted.current) setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u5B89\u88C5\u72B6\u6001\u8BFB\u53D6\u5931\u8D25\uFF0C\u5C06\u7EE7\u7EED\u91CD\u8BD5\u3002" }));
|
|
1476
|
+
} finally {
|
|
1477
|
+
polling.current.delete(id2);
|
|
1478
|
+
}
|
|
1479
|
+
};
|
|
1480
|
+
const timer = setInterval(() => active.forEach((id2) => {
|
|
1481
|
+
void poll(id2);
|
|
1482
|
+
}), 1500);
|
|
1483
|
+
return () => {
|
|
1484
|
+
listening = false;
|
|
1485
|
+
clearInterval(timer);
|
|
1486
|
+
};
|
|
1487
|
+
}, [jobs, officialToolInstallStatus]);
|
|
1488
|
+
const install = async (id2) => {
|
|
1489
|
+
const tool = OFFICIAL_TOOLS.find((item) => item.id === id2);
|
|
1490
|
+
if (!tool || tool.unsupported || submitting.current.has(id2) || jobs[id2]?.status === "running") return;
|
|
1491
|
+
submitting.current.add(id2);
|
|
1492
|
+
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
1493
|
+
setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "running", outputTail: [] } }));
|
|
1494
|
+
try {
|
|
1495
|
+
if (typeof installOfficialTool !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
|
|
1496
|
+
const response = await installOfficialTool(id2);
|
|
1497
|
+
if (!mounted.current) return;
|
|
1498
|
+
if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u5B89\u88C5\u4EFB\u52A1\u672A\u88AB\u63A5\u53D7\u3002"));
|
|
1499
|
+
setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
|
|
1500
|
+
} catch (error) {
|
|
1501
|
+
if (mounted.current) {
|
|
1502
|
+
setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "failed", error: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" } }));
|
|
1503
|
+
setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" }));
|
|
1504
|
+
}
|
|
1505
|
+
} finally {
|
|
1506
|
+
submitting.current.delete(id2);
|
|
1507
|
+
}
|
|
1508
|
+
};
|
|
1509
|
+
const cancel = async (id2) => {
|
|
1510
|
+
if (typeof cancelOfficialToolInstall !== "function" || jobs[id2]?.status !== "running" || jobs[id2]?.cancelRequested) return;
|
|
1511
|
+
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
1512
|
+
try {
|
|
1513
|
+
const response = await cancelOfficialToolInstall(id2);
|
|
1514
|
+
if (!mounted.current) return;
|
|
1515
|
+
if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u53D6\u6D88\u8BF7\u6C42\u672A\u88AB\u63A5\u53D7\u3002"));
|
|
1516
|
+
setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
|
|
1517
|
+
if (response.value.job.status !== "running") void refresh();
|
|
1518
|
+
} catch (error) {
|
|
1519
|
+
if (mounted.current) setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u65E0\u6CD5\u53D6\u6D88\u5B89\u88C5\u3002" }));
|
|
1520
|
+
}
|
|
1521
|
+
};
|
|
1522
|
+
const byId = Object.fromEntries(probeState.probes.map((probe) => [probe.id, probe]));
|
|
1523
|
+
const capabilitiesById = Object.fromEntries(probeState.capabilities.map((item) => [item.id, item]));
|
|
1524
|
+
const readinessById = Object.fromEntries(probeState.readiness.map((item) => [item.id, item]));
|
|
1525
|
+
return /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card", "aria-label": "\u5B98\u65B9\u5DE5\u5177" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u5B98\u65B9\u5DE5\u5177"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\uFF0C\u5E76\u4ECE\u56FA\u5B9A\u6CE8\u518C\u8868\u4E00\u952E\u4E0B\u8F7D\u5B89\u88C5\u3002ZCode \u4F1A\u6253\u5F00\u5B98\u65B9\u5B89\u88C5\u7A97\u53E3\u4F9B\u4F60\u9009\u62E9\u76EE\u5F55\uFF1B\u5B8C\u6210\u540E\u91CD\u65B0\u68C0\u6D4B\u7248\u672C\u3002")), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: probeState.status === "loading", onClick: () => {
|
|
1526
|
+
void refresh();
|
|
1527
|
+
} }, "\u91CD\u65B0\u68C0\u6D4B")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, probeState.status === "loading" && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-empty", role: "status" }, "\u6B63\u5728\u68C0\u6D4B\u672C\u673A\u5B98\u65B9\u5DE5\u5177\u2026"), probeState.status === "error" && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, probeState.error), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tools", role: "list", "aria-label": "\u5B98\u65B9\u5DE5\u5177\u6CE8\u518C\u8868" }, OFFICIAL_TOOLS.map((tool) => {
|
|
1528
|
+
const command = installCommandLine(tool);
|
|
1529
|
+
const probe = byId[tool.id];
|
|
1530
|
+
const capability = capabilitiesById[tool.id];
|
|
1531
|
+
const readiness = readinessById[tool.id];
|
|
1532
|
+
const job = jobs[tool.id];
|
|
1533
|
+
const running = job?.status === "running";
|
|
1534
|
+
const action = toolInstallAction({ tool, probe, readiness, job, probeStatus: probeState.status });
|
|
1535
|
+
const verified = job?.status === "succeeded" && job.postInstallProbe?.installed === true;
|
|
1536
|
+
const status = running ? job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u5B89\u88C5\u2026" : "\u5B89\u88C5\u4E2D\u2026" : job?.status === "installer-opened" ? "\u5B98\u65B9\u5B89\u88C5\u5668\u5DF2\u6253\u5F00\uFF0C\u8BF7\u5B8C\u6210\u5B89\u88C5\u540E\u91CD\u65B0\u68C0\u6D4B" : job?.status === "cancelled" ? "\u5B89\u88C5\u5DF2\u53D6\u6D88\uFF0C\u8BF7\u91CD\u65B0\u68C0\u6D4B" : verified ? "\u5B89\u88C5\u6210\u529F\u5E76\u9A8C\u8BC1" : probeLabel(probe);
|
|
1537
|
+
return /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tool", role: "listitem", key: tool.id }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tool-info" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route-name", title: tool.purpose }, tool.label), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-route-provider" }, tool.vendor, " \xB7 ", tool.id), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tool-status", role: "status" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: `mr-tool-dot ${running ? "running" : probe?.installed ? "installed" : "missing"}` }), status, tool.version ? ` \xB7 \u76EE\u6807 ${tool.version}` : ""), probe?.installed && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption mr-tool-detail" }, readiness?.ready ? `\u5B98\u65B9\u6267\u884C\u5165\u53E3\u5DF2\u6838\u9A8C\uFF0C\u53EF\u5728\u4F1A\u8BDD\u4E2D\u8C03\u7528 model_router_tool_run\uFF1B${capability?.modes?.includes("read-only") ? "\u652F\u6301\u53EA\u8BFB\u548C\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u4EFB\u52A1" : "\u4EC5\u652F\u6301\u7ECF\u5BA1\u6279\u7684\u53EF\u7F16\u8F91\u9694\u79BB\u5DE5\u4F5C\u533A\u4EFB\u52A1"}\uFF0C\u8D26\u53F7\u53CA\u6A21\u578B\u4ECD\u9700\u5B9E\u6D4B\u3002` : `\u5DF2\u5B89\u88C5\uFF0C\u4F46\u5F53\u524D\u4E0D\u53EF\u6258\u7BA1\u6267\u884C\uFF1A${readiness?.reason || capability?.reason || "\u6267\u884C\u5165\u53E3\u5C1A\u672A\u6838\u9A8C\u3002"}`), command ? /* @__PURE__ */ import_react2.default.createElement("code", { className: "mr-tool-command" }, command) : /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", style: { margin: "6px 0 0" } }, tool.unsupportedReason), probe?.detail && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption mr-tool-detail" }, probe.detail), (rowErrors[tool.id] || job?.error) && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error mr-tool-error", role: "alert" }, rowErrors[tool.id] || job.error), Array.isArray(job?.outputTail) && job.outputTail.length > 0 && /* @__PURE__ */ import_react2.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react2.default.createElement("summary", null, "\u5B89\u88C5\u65E5\u5FD7"), /* @__PURE__ */ import_react2.default.createElement("pre", null, job.outputTail.slice(-6).join("\n")))), command && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-tool-actions" }, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-tool-button", type: "button", disabled: action.disabled, onClick: () => {
|
|
1538
|
+
void install(tool.id);
|
|
1539
|
+
} }, action.label), running && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: job.cancelRequested, onClick: () => {
|
|
1540
|
+
void cancel(tool.id);
|
|
1541
|
+
} }, job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u2026" : "\u53D6\u6D88\u5B89\u88C5")));
|
|
1542
|
+
})), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", style: { marginTop: 12 } }, "\u5B89\u88C5\u7531 Host \u6309\u6CE8\u518C\u8868\u56FA\u5B9A\u6765\u6E90\u6267\u884C\uFF0C\u4E0D\u63A5\u53D7\u81EA\u5B9A\u4E49\u5305\u540D\uFF1B\u53EF\u70B9\u201C\u53D6\u6D88\u5B89\u88C5\u201D\u7EC8\u6B62\u4E0B\u8F7D\u4EFB\u52A1\uFF0C\u968F\u540E\u91CD\u65B0\u68C0\u6D4B\u5B9E\u9645\u7248\u672C\u3002ZCode \u5B89\u88C5\u5668\u542F\u52A8\u540E\u4ECD\u9700\u5728\u539F\u5382\u7A97\u53E3\u9009\u62E9\u76EE\u5F55\u5E76\u5B8C\u6210\u5B89\u88C5\u3002Agent \u4E5F\u53EF\u8C03\u7528 ", /* @__PURE__ */ import_react2.default.createElement("code", null, "model_router_tool_install"), "\uFF0C\u6216\u5728\u4F1A\u8BDD\u4F7F\u7528 ", /* @__PURE__ */ import_react2.default.createElement("code", null, "/tools"), "\u3002")));
|
|
1543
|
+
}
|
|
1544
|
+
function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus }) {
|
|
1545
|
+
const [catalogState, setCatalogState] = import_react2.default.useState({ status: "loading", catalog: null, error: "" });
|
|
1546
|
+
const [task, setTask] = import_react2.default.useState("");
|
|
1547
|
+
const [mode, setMode] = import_react2.default.useState("single");
|
|
1548
|
+
const [budget, setBudget] = import_react2.default.useState(() => String(settingsScope.getSnapshot().value?.budgetUsd ?? 0));
|
|
1549
|
+
const [query, setQuery] = import_react2.default.useState("");
|
|
1550
|
+
const [plan, setPlan] = import_react2.default.useState(null);
|
|
1551
|
+
const [planError, setPlanError] = import_react2.default.useState("");
|
|
1552
|
+
const [toolProbes, setToolProbes] = import_react2.default.useState(null);
|
|
1553
|
+
const budgetEdited = import_react2.default.useRef(false);
|
|
1554
|
+
const budgetValue = import_react2.default.useRef(budget);
|
|
1555
|
+
const mounted = import_react2.default.useRef(false);
|
|
1556
|
+
const catalogRequest = import_react2.default.useRef(0);
|
|
1557
|
+
import_react2.default.useEffect(() => {
|
|
1558
|
+
const syncBudget = () => {
|
|
1559
|
+
if (budgetEdited.current) return;
|
|
1560
|
+
const next = String(settingsScope.getSnapshot().value?.budgetUsd ?? 0);
|
|
1561
|
+
if (next !== budgetValue.current) {
|
|
1562
|
+
budgetValue.current = next;
|
|
1563
|
+
setBudget(next);
|
|
1564
|
+
setPlan(null);
|
|
1565
|
+
setPlanError("");
|
|
1566
|
+
}
|
|
1567
|
+
};
|
|
1568
|
+
syncBudget();
|
|
1569
|
+
return settingsScope.subscribe(syncBudget);
|
|
1570
|
+
}, [settingsScope]);
|
|
1571
|
+
import_react2.default.useEffect(() => {
|
|
1572
|
+
mounted.current = true;
|
|
1573
|
+
const request = ++catalogRequest.current;
|
|
1574
|
+
Promise.resolve().then(loadCatalog).then((response) => {
|
|
1575
|
+
if (!mounted.current || request !== catalogRequest.current) return;
|
|
1576
|
+
if (response?.ok) setCatalogState({ status: "ready", catalog: response.value, error: "" });
|
|
1577
|
+
else setCatalogState({ status: "error", catalog: null, error: text(response?.error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
|
|
1578
|
+
}).catch((error) => {
|
|
1579
|
+
if (mounted.current && request === catalogRequest.current) setCatalogState({ status: "error", catalog: null, error: text(error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
|
|
1580
|
+
});
|
|
1581
|
+
return () => {
|
|
1582
|
+
mounted.current = false;
|
|
1583
|
+
catalogRequest.current += 1;
|
|
1584
|
+
};
|
|
1585
|
+
}, []);
|
|
1586
|
+
const routes = routesFromModelCatalog(catalogState.catalog);
|
|
1587
|
+
const providerCount = new Set(routes.map((route) => route.provider)).size;
|
|
1588
|
+
const refresh = async () => {
|
|
1589
|
+
const request = ++catalogRequest.current;
|
|
1590
|
+
setCatalogState({ status: "loading", catalog: null, error: "" });
|
|
1591
|
+
setPlan(null);
|
|
1592
|
+
try {
|
|
1593
|
+
const response = await loadCatalog();
|
|
1594
|
+
if (!mounted.current || request !== catalogRequest.current) return;
|
|
1595
|
+
if (response?.ok) setCatalogState({ status: "ready", catalog: response.value, error: "" });
|
|
1596
|
+
else setCatalogState({ status: "error", catalog: null, error: text(response?.error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
|
|
1597
|
+
} catch (error) {
|
|
1598
|
+
if (mounted.current && request === catalogRequest.current) setCatalogState({ status: "error", catalog: null, error: text(error?.message) || "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25" });
|
|
1599
|
+
}
|
|
1600
|
+
};
|
|
1601
|
+
const invalidatePlan = () => {
|
|
1602
|
+
setPlan(null);
|
|
1603
|
+
setPlanError("");
|
|
1604
|
+
};
|
|
1605
|
+
const handleToolProbes = import_react2.default.useCallback((snapshot) => {
|
|
1606
|
+
setToolProbes(snapshot);
|
|
1607
|
+
setPlan(null);
|
|
1608
|
+
setPlanError("");
|
|
1609
|
+
}, []);
|
|
1610
|
+
const generate = () => {
|
|
1611
|
+
setPlanError("");
|
|
1612
|
+
try {
|
|
1613
|
+
if (!text(task)) throw new Error("\u8BF7\u5148\u63CF\u8FF0\u4EFB\u52A1\u3002");
|
|
1614
|
+
if (routes.length === 0) throw new Error("\u8BF7\u5148\u5728\u5B98\u65B9\u201C\u6A21\u578B\u201D\u9875\u914D\u7F6E\u81F3\u5C11\u4E00\u6761\u6A21\u578B\u8DEF\u7EBF\u3002");
|
|
1615
|
+
const parsedBudget = Number(budget);
|
|
1616
|
+
if (!Number.isFinite(parsedBudget) || parsedBudget < 0) throw new Error("\u9884\u7B97\u5FC5\u987B\u662F\u4E0D\u5C0F\u4E8E 0 \u7684\u6570\u5B57\u3002");
|
|
1617
|
+
setPlan(createWorkspacePlan(task, catalogState.catalog, {
|
|
1618
|
+
mode,
|
|
1619
|
+
budgetUsd: parsedBudget,
|
|
1620
|
+
modelProfilesJson: settingsScope.getSnapshot().value?.modelProfilesJson ?? "[]",
|
|
1621
|
+
installedToolIds: (toolProbes?.probes ?? []).filter((probe) => probe.installed).map((probe) => probe.id),
|
|
1622
|
+
runnableToolIds: (toolProbes?.readiness ?? []).filter((item) => item.ready).map((item) => item.id)
|
|
1623
|
+
}));
|
|
1624
|
+
} catch (error) {
|
|
1625
|
+
setPlan(null);
|
|
1626
|
+
setPlanError(text(error?.message) || "\u65E0\u6CD5\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE\u3002");
|
|
1627
|
+
}
|
|
1628
|
+
};
|
|
1629
|
+
return /* @__PURE__ */ import_react2.default.createElement("main", { className: "mr-workspace" }, /* @__PURE__ */ import_react2.default.createElement("style", null, router_main_default), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-shell" }, /* @__PURE__ */ import_react2.default.createElement("header", { className: "mr-header" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-eyebrow" }, "Model Router \xB7 DeepSeek Harness"), /* @__PURE__ */ import_react2.default.createElement("h1", { className: "mr-title" }, "\u6A21\u578B\u8DEF\u7531\u5DE5\u4F5C\u53F0"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-subtitle" }, "\u67E5\u770B\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\uFF0C\u4E3A\u4EFB\u52A1\u751F\u6210\u8DEF\u7EBF\u5EFA\u8BAE\u4E0E\u56E2\u961F\u5DE5\u4F5C\u5305\u3002\u4E3B\u4F1A\u8BDD\u6A21\u578B\u4ECD\u7531\u5B98\u65B9\u9009\u62E9\u5668\u7BA1\u7406\u3002")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-status" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: `mr-status-dot ${catalogState.status === "loading" ? "loading" : catalogState.status === "error" ? "error" : ""}` }), catalogState.status === "ready" ? `${providerCount} \u4E2A\u4F9B\u5E94\u5546 \xB7 ${routes.length} \u6761\u8DEF\u7EBF` : catalogState.status === "loading" ? "\u6B63\u5728\u8BFB\u53D6\u6A21\u578B\u76EE\u5F55" : "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-grid" }, /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card", "aria-label": "\u4EFB\u52A1\u89C4\u5212" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u4EFB\u52A1\u89C4\u5212"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u89C4\u5212\u5728\u672C\u673A\u5B8C\u6210\uFF0C\u4E0D\u4F1A\u542F\u52A8\u6A21\u578B\u6216\u56E2\u961F\u4EFB\u52A1\u3002"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-label", htmlFor: "mr-task" }, "\u4EFB\u52A1\u63CF\u8FF0"), /* @__PURE__ */ import_react2.default.createElement("textarea", { className: "mr-textarea", id: "mr-task", value: task, onChange: (event) => {
|
|
1630
|
+
setTask(event.target.value);
|
|
1631
|
+
invalidatePlan();
|
|
1632
|
+
}, placeholder: "\u4F8B\u5982\uFF1A\u5206\u6790\u9879\u76EE\u67B6\u6784\uFF0C\u5206\u5DE5\u4FEE\u590D\u5173\u952E\u95EE\u9898\uFF0C\u5E76\u7ED9\u51FA\u9A8C\u6536\u6E05\u5355" }), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-control-label" }, "\u89C4\u5212\u6A21\u5F0F"), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "\u89C4\u5212\u6A21\u5F0F" }, /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": mode === "single", onClick: () => {
|
|
1633
|
+
setMode("single");
|
|
1634
|
+
invalidatePlan();
|
|
1635
|
+
} }, "\u5355\u4EFB\u52A1"), /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": mode === "team", onClick: () => {
|
|
1636
|
+
setMode("team");
|
|
1637
|
+
invalidatePlan();
|
|
1638
|
+
} }, "\u56E2\u961F\u5206\u5DE5"))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-control-group mr-budget" }, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-control-label", htmlFor: "mr-budget" }, "\u672C\u6B21\u4F30\u7B97\u9884\u7B97\uFF08USD\uFF09"), /* @__PURE__ */ import_react2.default.createElement("input", { className: "mr-input", id: "mr-budget", type: "number", min: "0", step: "0.01", value: budget, onChange: (event) => {
|
|
1639
|
+
budgetEdited.current = true;
|
|
1640
|
+
budgetValue.current = event.target.value;
|
|
1641
|
+
setBudget(event.target.value);
|
|
1642
|
+
invalidatePlan();
|
|
1643
|
+
} }))), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button", type: "button", disabled: catalogState.status !== "ready" || routes.length === 0 || toolProbes === null, onClick: generate }, "\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE"), /* @__PURE__ */ import_react2.default.createElement("span", { className: "mr-caption" }, toolProbes === null ? "\u6B63\u5728\u68C0\u6D4B\u5B98\u65B9\u5DE5\u5177\u2026" : "0 \u8868\u793A\u4E0D\u9650\u5236\u672C\u6B21\u5EFA\u8BAE\uFF1B\u4E0D\u4F1A\u8BBE\u7F6E\u771F\u5B9E\u652F\u51FA\u4E0A\u9650\u3002")), planError && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-error", role: "alert" }, planError))), /* @__PURE__ */ import_react2.default.createElement("section", { className: "mr-card", "aria-label": "\u6A21\u578B\u76EE\u5F55" }, /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-head" }, /* @__PURE__ */ import_react2.default.createElement("div", null, /* @__PURE__ */ import_react2.default.createElement("h2", { className: "mr-card-title" }, "\u6A21\u578B\u76EE\u5F55"), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-card-copy" }, "\u53EA\u663E\u793A\u5B98\u65B9\u5DF2\u767B\u8BB0\u7684 provider/model\uFF0C\u4E0D\u8BFB\u53D6 API Key\u3002")), /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: refresh }, "\u5237\u65B0")), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-card-body" }, catalogState.status === "error" && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-error", role: "alert" }, catalogState.error), catalogState.status === "loading" && /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u52A0\u8F7D\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026"), catalogState.status === "ready" && /* @__PURE__ */ import_react2.default.createElement(import_react2.default.Fragment, null, /* @__PURE__ */ import_react2.default.createElement("label", { className: "mr-label", htmlFor: "mr-model-search" }, "\u641C\u7D22\u8DEF\u7EBF"), /* @__PURE__ */ import_react2.default.createElement("input", { className: "mr-input mr-search", id: "mr-model-search", value: query, onChange: (event) => setQuery(event.target.value), placeholder: "\u6A21\u578B\u6216\u4F9B\u5E94\u5546" }), /* @__PURE__ */ import_react2.default.createElement(RouteList, { routes, query }), catalogState.catalog?.failures?.length > 0 && /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption" }, catalogState.catalog.failures.length, " \u4E2A\u4F9B\u5E94\u5546\u7684\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u5728\u5B98\u65B9\u6A21\u578B\u9875\u68C0\u67E5\u914D\u7F6E\u3002")), /* @__PURE__ */ import_react2.default.createElement("p", { className: "mr-caption", style: { marginTop: 13 } }, "\u76EE\u5F55\u767B\u8BB0\u4E0D\u4EE3\u8868\u51ED\u636E\u6216\u7F51\u7EDC\u5F53\u524D\u53EF\u7528\uFF1B\u56FE\u50CF\u80FD\u529B\u9700\u8981\u5728\u5B9E\u9645\u4F7F\u7528\u524D\u6838\u5BF9\u3002")))), /* @__PURE__ */ import_react2.default.createElement(ModelProfileEditor, { routes, settingsScope, onSaved: invalidatePlan }), plan && /* @__PURE__ */ import_react2.default.createElement(PlanResults, { plan }), /* @__PURE__ */ import_react2.default.createElement(OfficialToolsCard, { listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes: handleToolProbes }), /* @__PURE__ */ import_react2.default.createElement("div", { className: "mr-notice" }, "\u5B9E\u9645\u8C03\u7528\u8BF7\u5728\u5B98\u65B9\u4F1A\u8BDD\u4E2D\u4F7F\u7528 ", /* @__PURE__ */ import_react2.default.createElement("code", null, "model_router_consult"), "\u3001", /* @__PURE__ */ import_react2.default.createElement("code", null, "model_router_tool_run"), " \u6216 ", /* @__PURE__ */ import_react2.default.createElement("code", null, "model_router_team_execute"), "\u3002\u6258\u7BA1\u6267\u884C\u80FD\u529B\u548C\u5C31\u7EEA\u72B6\u6001\u89C1\u4E0A\u65B9\u5404\u5DE5\u5177\u5361\u7247\uFF1B\u5B9E\u9645\u4F7F\u7528\u7684\u6A21\u578B\u4EE5\u5382\u5546\u8BB0\u5F55\u4E3A\u51C6\u3002ZCode 3.14.3 \u4F7F\u7528\u5176\u81EA\u8EAB\u914D\u7F6E\u7684\u9ED8\u8BA4\u6A21\u578B\u3002\u53EF\u7F16\u8F91\u56E2\u961F\u4EFB\u52A1\u8981\u6C42\u5E72\u51C0\u7684 Git \u4ED3\u5E93\uFF0C\u5E76\u7ECF\u5B98\u65B9\u5DE5\u5177\u5BA1\u6279\u3002\u8BBE\u7F6E\u4F4D\u4E8E\u201C\u63D2\u4EF6 \u2192 \u5DF2\u5B89\u88C5 \u2192 @ljwei-stak/model-router-galgame\u201D\u3002")));
|
|
1644
|
+
}
|
|
1645
|
+
|
|
1646
|
+
// .dsh-plugin/shared/official-tools-remote.mjs
|
|
1647
|
+
var OFFICIAL_TOOLS_REMOTE_PACKAGE = "@ljwei-stak/model-router-galgame";
|
|
1648
|
+
var OFFICIAL_TOOLS_REMOTE_NAMESPACE = "modelRouterOfficialTools";
|
|
1649
|
+
function strictCodec(typeSymbol, parse) {
|
|
1650
|
+
return Object.freeze({ mode: "strict", typeSymbol, create: () => ({ parse }) });
|
|
1651
|
+
}
|
|
1652
|
+
function plainObject(value, subject) {
|
|
1653
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) {
|
|
1654
|
+
throw new TypeError(`${subject} must be an object`);
|
|
1655
|
+
}
|
|
1656
|
+
return value;
|
|
1657
|
+
}
|
|
1658
|
+
var toolIdCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolId`, (value) => {
|
|
1659
|
+
if (typeof value !== "string" || !getOfficialTool(value)) {
|
|
1660
|
+
throw new TypeError("toolId must name a fixed official tool");
|
|
1661
|
+
}
|
|
1662
|
+
return value;
|
|
1663
|
+
});
|
|
1664
|
+
var listResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolList`, (value) => {
|
|
1665
|
+
const result = plainObject(value, "tool list result");
|
|
1666
|
+
if (!Array.isArray(result.tools)) throw new TypeError("tool list result needs a tools array");
|
|
1667
|
+
return result;
|
|
1668
|
+
});
|
|
1669
|
+
var installResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolInstall`, (value) => {
|
|
1670
|
+
const result = plainObject(value, "install result");
|
|
1671
|
+
if (typeof result.accepted !== "boolean") throw new TypeError("install result needs accepted");
|
|
1672
|
+
if (result.accepted && !result.job) throw new TypeError("accepted install needs a job");
|
|
1673
|
+
if (!result.accepted && typeof result.error !== "string") throw new TypeError("refused install needs an error");
|
|
1674
|
+
return result;
|
|
1675
|
+
});
|
|
1676
|
+
var statusResultCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#OfficialToolStatus`, (value) => {
|
|
1677
|
+
const result = plainObject(value, "install status result");
|
|
1678
|
+
if (result.job !== null && (typeof result.job !== "object" || Array.isArray(result.job))) {
|
|
1679
|
+
throw new TypeError("install status result needs a job or null");
|
|
1680
|
+
}
|
|
1681
|
+
return result;
|
|
1682
|
+
});
|
|
1683
|
+
function descriptor(method, parameters, result) {
|
|
1684
|
+
return Object.freeze({
|
|
1685
|
+
id: `${OFFICIAL_TOOLS_REMOTE_PACKAGE}#${OFFICIAL_TOOLS_REMOTE_NAMESPACE}/${method}`,
|
|
1686
|
+
service: OFFICIAL_TOOLS_REMOTE_NAMESPACE,
|
|
1687
|
+
namespace: OFFICIAL_TOOLS_REMOTE_NAMESPACE,
|
|
1688
|
+
method,
|
|
1689
|
+
invocation: { kind: "direct" },
|
|
1690
|
+
parameters,
|
|
1691
|
+
result
|
|
1692
|
+
});
|
|
1693
|
+
}
|
|
1694
|
+
var toolIdParameter = Object.freeze({
|
|
1695
|
+
name: "toolId",
|
|
1696
|
+
wire: "toolId",
|
|
1697
|
+
source: "json",
|
|
1698
|
+
codec: toolIdCodec
|
|
1699
|
+
});
|
|
1700
|
+
var OFFICIAL_TOOLS_REMOTE_DESCRIPTORS = Object.freeze([
|
|
1701
|
+
descriptor("list", [], listResultCodec),
|
|
1702
|
+
descriptor("installTool", [toolIdParameter], installResultCodec),
|
|
1703
|
+
descriptor("cancel", [toolIdParameter], installResultCodec),
|
|
1704
|
+
descriptor("status", [toolIdParameter], statusResultCodec)
|
|
1705
|
+
]);
|
|
1706
|
+
var OFFICIAL_TOOLS_CLIENT_REMOTE = Object.freeze({
|
|
1707
|
+
package: OFFICIAL_TOOLS_REMOTE_PACKAGE,
|
|
1708
|
+
descriptors: OFFICIAL_TOOLS_REMOTE_DESCRIPTORS
|
|
1709
|
+
});
|
|
1710
|
+
var OFFICIAL_TOOLS_HOST_TYPERT = Object.freeze({
|
|
1711
|
+
package: OFFICIAL_TOOLS_REMOTE_PACKAGE,
|
|
1712
|
+
face: "host",
|
|
1713
|
+
schemas: [],
|
|
1714
|
+
invocations: OFFICIAL_TOOLS_REMOTE_DESCRIPTORS,
|
|
1715
|
+
model: { services: [], events: [], objects: [] }
|
|
1716
|
+
});
|
|
1717
|
+
|
|
1718
|
+
// .dsh-plugin/client/official-harness.jsx
|
|
1719
|
+
var import_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
1720
|
+
var ROUTER_NAMESPACE = "model-router-galgame";
|
|
1721
|
+
var ROUTER_PACKAGE = "@ljwei-stak/model-router-galgame";
|
|
1722
|
+
var ROUTER_PANEL = "model-router-galgame";
|
|
1723
|
+
var inject = ["remote"];
|
|
1724
|
+
var UI_INJECT = [
|
|
1725
|
+
"slots",
|
|
1726
|
+
"configForms",
|
|
1727
|
+
"remote",
|
|
1728
|
+
"remote.session",
|
|
1729
|
+
`remote.${OFFICIAL_TOOLS_REMOTE_NAMESPACE}`,
|
|
1730
|
+
"layout"
|
|
1731
|
+
];
|
|
1732
|
+
var FORM_LABELS = Object.freeze({
|
|
1733
|
+
unavailable: "\u8BE5\u63D2\u4EF6\u5F53\u524D\u672A\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u7F6E\u3002",
|
|
1734
|
+
readOnly: "\u5F53\u524D\u914D\u7F6E\u4E3A\u53EA\u8BFB\u3002",
|
|
1735
|
+
saveFailed: "\u8BBE\u7F6E\u6CA1\u6709\u88AB\u4FDD\u5B58\uFF0C\u8BF7\u68C0\u67E5\u8F93\u5165\u540E\u91CD\u8BD5\u3002",
|
|
1736
|
+
save: "\u4FDD\u5B58\u8BBE\u7F6E",
|
|
1737
|
+
saving: "\u4FDD\u5B58\u4E2D\u2026"
|
|
1738
|
+
});
|
|
1739
|
+
var FIELD_COPY = Object.freeze({
|
|
1740
|
+
overridden: "\u5DF2\u8986\u76D6",
|
|
1741
|
+
reset: "\u6062\u590D\u9ED8\u8BA4",
|
|
1742
|
+
invalidNumber: "\u8BF7\u8F93\u5165\u5141\u8BB8\u8303\u56F4\u5185\u7684\u6570\u5B57\uFF0C\u6216\u7559\u7A7A\u6062\u590D\u9ED8\u8BA4\u3002"
|
|
1743
|
+
});
|
|
1744
|
+
function boundedNumberField(field2, { minimum = 0, maximum = Number.MAX_SAFE_INTEGER, integer = false } = {}) {
|
|
1745
|
+
const numeric = (0, import_dsh_client_ui_primitives.settingsNumberField)(field2);
|
|
1746
|
+
return {
|
|
1747
|
+
...numeric,
|
|
1748
|
+
parse: (text2) => {
|
|
1749
|
+
const write = numeric.parse(text2);
|
|
1750
|
+
if (write?.kind !== "set") return write;
|
|
1751
|
+
const value = write.value;
|
|
1752
|
+
if (typeof value !== "number" || value < minimum || value > maximum) return void 0;
|
|
1753
|
+
if (integer && (!Number.isSafeInteger(value) || Object.is(value, -0))) return void 0;
|
|
1754
|
+
return write;
|
|
1755
|
+
}
|
|
1756
|
+
};
|
|
1757
|
+
}
|
|
1758
|
+
function modelProfilesField() {
|
|
1759
|
+
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("modelProfilesJson");
|
|
1760
|
+
return {
|
|
1761
|
+
...field2,
|
|
1762
|
+
parse: (text2) => {
|
|
1763
|
+
try {
|
|
1764
|
+
parseModelProfilesJson(text2);
|
|
1765
|
+
return field2.parse(text2);
|
|
1766
|
+
} catch {
|
|
1767
|
+
return void 0;
|
|
1768
|
+
}
|
|
1769
|
+
}
|
|
1770
|
+
};
|
|
1771
|
+
}
|
|
1772
|
+
var RouterSettingsCardController = class {
|
|
1773
|
+
constructor(scope) {
|
|
1774
|
+
this.form = new import_dsh_client_ui_primitives.SettingsFormModel(scope, [
|
|
1775
|
+
boundedNumberField("budgetUsd", { minimum: 0 }),
|
|
1776
|
+
boundedNumberField("maxConsultOutputChars", { minimum: 500, maximum: 5e4, integer: true }),
|
|
1777
|
+
modelProfilesField()
|
|
1778
|
+
]);
|
|
1779
|
+
this.store = this.form.bind(() => ({
|
|
1780
|
+
...this.form.shell(),
|
|
1781
|
+
budgetUsd: this.form.field("budgetUsd"),
|
|
1782
|
+
maxConsultOutputChars: this.form.field("maxConsultOutputChars"),
|
|
1783
|
+
modelProfilesJson: this.form.field("modelProfilesJson")
|
|
1784
|
+
}));
|
|
1785
|
+
}
|
|
1786
|
+
/** Supply the snapshot hook and staged form actions to the Plugins slot. */
|
|
1787
|
+
inject() {
|
|
1788
|
+
return { hooks: { modelRouterSettings: this.store }, ...this.form.actions() };
|
|
1789
|
+
}
|
|
1790
|
+
/** Release the live Host-form subscription when the client plugin unloads. */
|
|
1791
|
+
dispose() {
|
|
1792
|
+
this.form.dispose();
|
|
1793
|
+
}
|
|
1794
|
+
};
|
|
1795
|
+
function RouterSettingsCard(props) {
|
|
1796
|
+
const state = props.useModelRouterSettings((snapshot) => snapshot);
|
|
1797
|
+
if (props.view === "summary") {
|
|
1798
|
+
return "\u4E3A\u5B98\u65B9\u5DF2\u914D\u7F6E\u7684\u6A21\u578B\u751F\u6210\u8DEF\u7531\u8BA1\u5212\uFF0C\u5E76\u63D0\u4F9B\u8DE8\u6A21\u578B\u54A8\u8BE2\u5DE5\u5177\u3002";
|
|
1799
|
+
}
|
|
1800
|
+
const disabled = !state.writable || state.saving;
|
|
1801
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_dsh_client_ui_primitives.SettingsForm, { labels: FORM_LABELS, state, onSave: props.save, onDiscard: props.discard }, /* @__PURE__ */ import_react3.default.createElement(
|
|
1802
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
1803
|
+
{
|
|
1804
|
+
id: "model-router-budget-usd",
|
|
1805
|
+
label: "\u5355\u6B21\u8BA1\u5212\u9884\u7B97\u4E0A\u9650\uFF08USD\uFF09",
|
|
1806
|
+
hint: "0 \u8868\u793A\u4E0D\u8BBE\u9884\u7B97\u4E0A\u9650\u3002\u53EA\u6709\u586B\u5199\u4E0B\u65B9\u5B9E\u9645\u4F7F\u7528\u7684\u5355\u4EF7\u540E\uFF0C\u624D\u80FD\u4F30\u7B97\u8D39\u7528\uFF1B\u9884\u7B97\u53EA\u5F71\u54CD\u8DEF\u7531\u5EFA\u8BAE\u3002",
|
|
1807
|
+
help: {
|
|
1808
|
+
label: "\u9884\u7B97\u8BF4\u660E",
|
|
1809
|
+
content: "\u9884\u7B97\u53EA\u5F71\u54CD\u8DEF\u7531\u5EFA\u8BAE\uFF0C\u4E0D\u4F1A\u9650\u5236\u670D\u52A1\u5546\u6263\u8D39\uFF0C\u4E5F\u4E0D\u4F1A\u8BFB\u53D6\u6216\u4FDD\u5B58 API Key\u3002"
|
|
1810
|
+
},
|
|
1811
|
+
disabled,
|
|
1812
|
+
...state.budgetUsd,
|
|
1813
|
+
overriddenLabel: FIELD_COPY.overridden,
|
|
1814
|
+
resetLabel: FIELD_COPY.reset,
|
|
1815
|
+
invalidLabel: FIELD_COPY.invalidNumber,
|
|
1816
|
+
onEdit: (text2) => {
|
|
1817
|
+
props.edit("budgetUsd", text2);
|
|
1818
|
+
},
|
|
1819
|
+
onReset: () => {
|
|
1820
|
+
props.resetField("budgetUsd");
|
|
1821
|
+
}
|
|
1822
|
+
}
|
|
1823
|
+
), /* @__PURE__ */ import_react3.default.createElement(
|
|
1824
|
+
import_dsh_client_ui_primitives.SettingsValueField,
|
|
1825
|
+
{
|
|
1826
|
+
id: "model-router-consult-output",
|
|
1827
|
+
label: "\u8DE8\u6A21\u578B\u54A8\u8BE2\u6700\u5927\u8F93\u51FA\u5B57\u7B26\u6570",
|
|
1828
|
+
hint: "\u9650\u5236 model_router_consult \u8FD4\u56DE\u7684\u5B57\u7B26\u6570\uFF0C\u9632\u6B62\u5355\u6B21\u54A8\u8BE2\u5360\u7528\u8FC7\u591A\u4E0A\u4E0B\u6587\u3002",
|
|
1829
|
+
numeric: true,
|
|
1830
|
+
disabled,
|
|
1831
|
+
...state.maxConsultOutputChars,
|
|
1832
|
+
overriddenLabel: FIELD_COPY.overridden,
|
|
1833
|
+
resetLabel: FIELD_COPY.reset,
|
|
1834
|
+
invalidLabel: FIELD_COPY.invalidNumber,
|
|
1835
|
+
onEdit: (text2) => {
|
|
1836
|
+
props.edit("maxConsultOutputChars", text2);
|
|
1837
|
+
},
|
|
1838
|
+
onReset: () => {
|
|
1839
|
+
props.resetField("maxConsultOutputChars");
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
), /* @__PURE__ */ import_react3.default.createElement("div", { style: styles.profileEditor }, /* @__PURE__ */ import_react3.default.createElement("label", { htmlFor: "model-router-profiles-json", style: styles.profileLabel }, "\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\uFF08JSON\uFF09"), /* @__PURE__ */ import_react3.default.createElement("p", { style: styles.noticeText }, "\u6309\u201C\u6A21\u578B\u76EE\u5F55\u201D\u4E2D\u7684\u51C6\u786E provider/model \u586B\u5199\u3002quality \u4E3A\u81EA\u5B9A\u7684 0\u2013100 \u5206\uFF1Binput/output \u662F\u7F8E\u5143\u6BCF\u767E\u4E07 token\u3002\u7F3A\u5C11\u4EF7\u683C\u65F6\u53EA\u7ED9\u8DEF\u7EBF\u5EFA\u8BAE\uFF0C\u4E0D\u663E\u793A\u865A\u6784\u8D39\u7528\u3002"), /* @__PURE__ */ import_react3.default.createElement(
|
|
1843
|
+
"textarea",
|
|
1844
|
+
{
|
|
1845
|
+
id: "model-router-profiles-json",
|
|
1846
|
+
value: state.modelProfilesJson.text,
|
|
1847
|
+
disabled,
|
|
1848
|
+
"aria-invalid": state.modelProfilesJson.invalid,
|
|
1849
|
+
onChange: (event) => props.edit("modelProfilesJson", event.target.value),
|
|
1850
|
+
spellCheck: false,
|
|
1851
|
+
style: styles.profileTextarea
|
|
1852
|
+
}
|
|
1853
|
+
), state.modelProfilesJson.invalid && /* @__PURE__ */ import_react3.default.createElement("p", { style: styles.profileError, role: "alert" }, "JSON \u683C\u5F0F\u6216\u67D0\u9879\u914D\u7F6E\u65E0\u6548\u3002\u6BCF\u9879\u9700\u63D0\u4F9B\u51C6\u786E\u7684 provider/model\uFF0C\u5355\u4EF7\u4E3A\u975E\u8D1F USD \u6570\u5B57\uFF0C\u8D28\u91CF\u4E3A 0\u2013100\u3002"), /* @__PURE__ */ import_react3.default.createElement("details", { style: styles.profileExample }, /* @__PURE__ */ import_react3.default.createElement("summary", null, "\u67E5\u770B\u914D\u7F6E\u683C\u5F0F"), /* @__PURE__ */ import_react3.default.createElement("pre", null, `[
|
|
1854
|
+
{
|
|
1855
|
+
"provider": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u4F9B\u5E94\u5546 ID",
|
|
1856
|
+
"model": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u6A21\u578B ID",
|
|
1857
|
+
"quality": 80,
|
|
1858
|
+
"pricing": { "input": 0.2, "output": 0.8 },
|
|
1859
|
+
"specialties": ["code"],
|
|
1860
|
+
"cliModel": "\u5382\u5546 CLI \u4F7F\u7528\u7684\u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"
|
|
1861
|
+
}
|
|
1862
|
+
]`)), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", disabled, onClick: () => props.resetField("modelProfilesJson") }, "\u6062\u590D\u9ED8\u8BA4\u914D\u7F6E")), /* @__PURE__ */ import_react3.default.createElement("aside", { style: styles.notice, "aria-label": "\u6A21\u578B\u8DEF\u7531\u4F7F\u7528\u8BF4\u660E" }, /* @__PURE__ */ import_react3.default.createElement("div", { style: styles.titleLine }, /* @__PURE__ */ import_react3.default.createElement(import_dsh_client_ui_primitives.Tag, { tone: "info" }, "\u5B98\u65B9\u6A21\u578B\u914D\u7F6E")), /* @__PURE__ */ import_react3.default.createElement("p", { style: styles.noticeText }, "\u8BF7\u5728 DeepSeek Harness \u7684\u201C\u6A21\u578B\u201D\u9875\u9762\u914D\u7F6E DeepSeek\u3001OpenAI \u517C\u5BB9\u6216 Anthropic \u517C\u5BB9\u670D\u52A1\u3002\u6B64\u63D2\u4EF6\u8BFB\u53D6\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\uFF0C\u4E0D\u4FDD\u5B58 API Key\uFF1B\u76EE\u5F55\u4E2D\u7684\u8DEF\u7EBF\u4ECD\u9700\u901A\u8FC7\u5B9E\u9645\u8C03\u7528\u9A8C\u8BC1\u8D26\u53F7\u548C\u7F51\u7EDC\u53EF\u7528\u6027\u3002"), /* @__PURE__ */ import_react3.default.createElement("p", { style: styles.noticeText }, "\u4F7F\u7528 ", /* @__PURE__ */ import_react3.default.createElement("code", null, "model_router_plan"), " \u83B7\u53D6\u53EF\u5BA1\u9605\u7684\u8DEF\u7531\u5EFA\u8BAE\uFF0C\u4F7F\u7528 ", /* @__PURE__ */ import_react3.default.createElement("code", null, "model_router_consult"), " \u54A8\u8BE2\u4E00\u4E2A\u5DF2\u914D\u7F6E\u6A21\u578B\u3002\u5EFA\u8BAE\u4E0D\u4F1A\u6539\u5199\u4E3B\u4F1A\u8BDD\u6A21\u578B\uFF1B\u591A\u4EBA\u5206\u5DE5\u7531\u5B98\u65B9 Agent Teams \u5DE5\u5177\u6267\u884C\u3002")));
|
|
1863
|
+
}
|
|
1864
|
+
function OpenRouterWorkspace({ subject, openPanel }) {
|
|
1865
|
+
if (subject?.kind !== "bundle" || subject.pkg?.name !== ROUTER_PACKAGE) return null;
|
|
1866
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", type: "button", onClick: openPanel }, "\u6253\u5F00\u5DE5\u4F5C\u53F0");
|
|
1867
|
+
}
|
|
1868
|
+
function registerUi(ctx) {
|
|
1869
|
+
const officialToolsRemote = ctx.remote[OFFICIAL_TOOLS_REMOTE_NAMESPACE];
|
|
1870
|
+
const settingsScope = ctx.configForms.get(ROUTER_NAMESPACE);
|
|
1871
|
+
const card = new RouterSettingsCardController(settingsScope);
|
|
1872
|
+
ctx.effect(() => () => {
|
|
1873
|
+
card.dispose();
|
|
1874
|
+
}, "model-router-galgame: settings form subscription");
|
|
1875
|
+
ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("plugins.bundle.config", () => ctx.slots.register({
|
|
1876
|
+
name: "plugins.bundle.config",
|
|
1877
|
+
key: ROUTER_PACKAGE,
|
|
1878
|
+
inject: () => card.inject()
|
|
1879
|
+
}, RouterSettingsCard))), "model-router-galgame: installed bundle settings");
|
|
1880
|
+
ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("main", () => ctx.slots.register({
|
|
1881
|
+
name: "main",
|
|
1882
|
+
key: ROUTER_PANEL,
|
|
1883
|
+
inject: () => ({
|
|
1884
|
+
loadCatalog: () => ctx.remote.session.modelCatalog(),
|
|
1885
|
+
settingsScope,
|
|
1886
|
+
listOfficialTools: () => officialToolsRemote.list(),
|
|
1887
|
+
installOfficialTool: (toolId) => officialToolsRemote.installTool(toolId),
|
|
1888
|
+
cancelOfficialToolInstall: (toolId) => officialToolsRemote.cancel(toolId),
|
|
1889
|
+
officialToolInstallStatus: (toolId) => officialToolsRemote.status(toolId)
|
|
1890
|
+
})
|
|
1891
|
+
}, RouterMainPage))), "model-router-galgame: main workspace");
|
|
1892
|
+
ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("sidebar.panellist", () => ctx.slots.register({
|
|
1893
|
+
name: "sidebar.panellist",
|
|
1894
|
+
id: ROUTER_PANEL,
|
|
1895
|
+
order: 30,
|
|
1896
|
+
label: "\u6A21\u578B\u8DEF\u7531"
|
|
1897
|
+
}, RouterPanelIcon))), "model-router-galgame: sidebar entry");
|
|
1898
|
+
ctx.effect(() => ctx.configForms.whileServed([ROUTER_NAMESPACE], () => ctx.slots.inject("plugins.detail.actions", () => ctx.slots.register({
|
|
1899
|
+
name: "plugins.detail.actions",
|
|
1900
|
+
id: "model-router-open-workspace",
|
|
1901
|
+
order: 30,
|
|
1902
|
+
inject: () => ({ openPanel: () => ctx.layout.selectPanel(ROUTER_PANEL) })
|
|
1903
|
+
}, OpenRouterWorkspace))), "model-router-galgame: bundle open action");
|
|
1904
|
+
}
|
|
1905
|
+
async function apply(ctx) {
|
|
1906
|
+
const disposeRemote = await ctx.remote.$mount(OFFICIAL_TOOLS_CLIENT_REMOTE);
|
|
1907
|
+
const ui = ctx.inject(UI_INJECT, registerUi);
|
|
1908
|
+
try {
|
|
1909
|
+
await ui;
|
|
1910
|
+
} catch (error) {
|
|
1911
|
+
await ui.dispose();
|
|
1912
|
+
await disposeRemote();
|
|
1913
|
+
throw error;
|
|
1914
|
+
}
|
|
1915
|
+
return async () => {
|
|
1916
|
+
await ui.dispose();
|
|
1917
|
+
await disposeRemote();
|
|
1918
|
+
};
|
|
1919
|
+
}
|
|
1920
|
+
var styles = Object.freeze({
|
|
1921
|
+
titleLine: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: "8px" },
|
|
1922
|
+
notice: {
|
|
1923
|
+
marginTop: "4px",
|
|
1924
|
+
padding: "12px",
|
|
1925
|
+
border: "1px solid var(--dsw-alias-border-l1)",
|
|
1926
|
+
borderRadius: "8px",
|
|
1927
|
+
background: "var(--dsw-alias-markdown-code-block)"
|
|
1928
|
+
},
|
|
1929
|
+
noticeText: {
|
|
1930
|
+
margin: "7px 0 0",
|
|
1931
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
1932
|
+
fontSize: "13px",
|
|
1933
|
+
lineHeight: "20px"
|
|
1934
|
+
},
|
|
1935
|
+
profileEditor: { display: "grid", gap: "8px", marginTop: "14px" },
|
|
1936
|
+
profileLabel: { fontSize: "13px", fontWeight: 600 },
|
|
1937
|
+
profileTextarea: { width: "100%", minHeight: "150px", padding: "10px", fontFamily: "monospace", fontSize: "12px", borderRadius: "7px", border: "1px solid var(--dsw-alias-border-l1)", boxSizing: "border-box" },
|
|
1938
|
+
profileError: { color: "var(--dsw-alias-label-danger)", fontSize: "12px", margin: 0 },
|
|
1939
|
+
profileExample: { fontSize: "12px", whiteSpace: "pre-wrap" }
|
|
1940
|
+
});
|
|
1941
|
+
return module.exports;
|
|
1942
|
+
},
|
|
1943
|
+
});
|