@ljwei-stak/model-router-galgame 0.8.0 → 0.9.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 +903 -463
- package/.dsh-plugin/index.mjs +40 -8
- package/.dsh-plugin/official-tools-remote-service.mjs +1 -1
- package/.dsh-plugin/shared/harness-plan.mjs +26 -11
- package/.dsh-plugin/shared/model-profiles.mjs +101 -0
- package/.dsh-plugin/shared/official-team-runtime.mjs +31 -9
- package/.dsh-plugin/shared/official-tool-executor.mjs +50 -4
- package/.dsh-plugin/shared/official-tools-remote.mjs +1 -1
- package/.dsh-plugin/shared/official-tools-runtime.mjs +159 -9
- package/.dsh-plugin/shared/router.mjs +196 -72
- package/.dsh-plugin/shared/vendor-mimo-grok-adapter.mjs +2 -2
- package/INSTALLATION_GUIDE.zh.md +9 -9
- package/MIGRATION.md +2 -2
- package/README.md +7 -7
- package/README.zh.md +7 -7
- package/package.json +60 -59
package/.dsh-plugin/client.js
CHANGED
|
@@ -39,6 +39,218 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
39
39
|
));
|
|
40
40
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
41
41
|
|
|
42
|
+
// .dsh-plugin/shared/official-tool-registry.mjs
|
|
43
|
+
function getOfficialTool(id2) {
|
|
44
|
+
return TOOL_BY_ID.get(String(id2 ?? "").trim()) ?? null;
|
|
45
|
+
}
|
|
46
|
+
function toolForProvider(provider) {
|
|
47
|
+
const value = String(provider ?? "").trim().toLowerCase();
|
|
48
|
+
if (!value) return null;
|
|
49
|
+
for (const tool of OFFICIAL_TOOLS) {
|
|
50
|
+
if (tool.providerHints.some((hint) => value.includes(hint))) return tool;
|
|
51
|
+
}
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
function installCommandLine(tool) {
|
|
55
|
+
if (!tool || tool.unsupported) return null;
|
|
56
|
+
if (tool.manager === "signed-windows-installer") return "\u6253\u5F00\u5B98\u65B9\u7B7E\u540D\u5B89\u88C5\u5668\uFF08\u9009\u62E9\u5B89\u88C5\u76EE\u5F55\uFF09";
|
|
57
|
+
return tool.manager === "npm" ? `npm ${tool.installArgs.join(" ")}` : `${tool.manager} ${tool.installArgs.join(" ")}`;
|
|
58
|
+
}
|
|
59
|
+
var OFFICIAL_TOOLS, TOOL_BY_ID;
|
|
60
|
+
var init_official_tool_registry = __esm({
|
|
61
|
+
".dsh-plugin/shared/official-tool-registry.mjs"() {
|
|
62
|
+
OFFICIAL_TOOLS = Object.freeze([
|
|
63
|
+
Object.freeze({
|
|
64
|
+
id: "kimi-code",
|
|
65
|
+
label: "Kimi Code \xB7 Node",
|
|
66
|
+
vendor: "Moonshot AI",
|
|
67
|
+
purpose: "Kimi \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B kimi \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
|
|
68
|
+
package: "@moonshot-ai/kimi-code",
|
|
69
|
+
version: "2.1.1",
|
|
70
|
+
manager: "npm",
|
|
71
|
+
installArgs: ["install", "-g", "@moonshot-ai/kimi-code@2.1.1", "--registry=https://registry.npmjs.org/"],
|
|
72
|
+
probeExecutables: ["kimi"],
|
|
73
|
+
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",
|
|
74
|
+
providerHints: ["moonshot", "kimi"]
|
|
75
|
+
}),
|
|
76
|
+
Object.freeze({
|
|
77
|
+
id: "claude-code",
|
|
78
|
+
label: "Claude Code",
|
|
79
|
+
vendor: "Anthropic",
|
|
80
|
+
purpose: "Anthropic \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B claude \u547D\u4EE4\u3002",
|
|
81
|
+
package: "@anthropic-ai/claude-code",
|
|
82
|
+
version: "2.1.283",
|
|
83
|
+
manager: "npm",
|
|
84
|
+
installArgs: ["install", "-g", "@anthropic-ai/claude-code@2.1.283", "--registry=https://registry.npmjs.org/"],
|
|
85
|
+
probeExecutables: ["claude"],
|
|
86
|
+
providerHints: ["anthropic", "claude"]
|
|
87
|
+
}),
|
|
88
|
+
Object.freeze({
|
|
89
|
+
id: "codex",
|
|
90
|
+
label: "Codex CLI",
|
|
91
|
+
vendor: "OpenAI",
|
|
92
|
+
purpose: "OpenAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B codex \u547D\u4EE4\u3002",
|
|
93
|
+
package: "@openai/codex",
|
|
94
|
+
version: "0.157.1",
|
|
95
|
+
installArgs: ["install", "-g", "@openai/codex@0.157.1", "--registry=https://registry.npmjs.org/"],
|
|
96
|
+
manager: "npm",
|
|
97
|
+
probeExecutables: ["codex"],
|
|
98
|
+
probeNote: "Codex \u7248\u672C\u6A2A\u5E45\u7531\u9002\u914D\u5C42\u5BBD\u5339\u914D\uFF1B\u5B89\u88C5\u65F6\u56FA\u5B9A\u7248\u672C\u3002",
|
|
99
|
+
providerHints: ["openai", "gpt", "codex"]
|
|
100
|
+
}),
|
|
101
|
+
Object.freeze({
|
|
102
|
+
id: "minimax-code",
|
|
103
|
+
label: "MiniMax Code",
|
|
104
|
+
vendor: "MiniMax",
|
|
105
|
+
purpose: "MiniMax \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mcode \u547D\u4EE4\u3002",
|
|
106
|
+
package: "@minimax-ai/code",
|
|
107
|
+
version: "0.5.5",
|
|
108
|
+
manager: "npm",
|
|
109
|
+
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"],
|
|
110
|
+
probeExecutables: ["mcode"],
|
|
111
|
+
providerHints: ["minimax"]
|
|
112
|
+
}),
|
|
113
|
+
Object.freeze({
|
|
114
|
+
id: "mimo-code",
|
|
115
|
+
label: "MiMo Code",
|
|
116
|
+
vendor: "XiaoMi",
|
|
117
|
+
purpose: "\u5C0F\u7C73 MiMo \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mimo \u547D\u4EE4\u3002",
|
|
118
|
+
package: "@mimo-ai/cli",
|
|
119
|
+
version: "0.1.15",
|
|
120
|
+
manager: "npm",
|
|
121
|
+
installArgs: ["install", "-g", "@mimo-ai/cli@0.1.15", "--registry=https://registry.npmjs.org/"],
|
|
122
|
+
probeExecutables: ["mimo"],
|
|
123
|
+
providerHints: ["mimo", "xiaomi"]
|
|
124
|
+
}),
|
|
125
|
+
Object.freeze({
|
|
126
|
+
id: "grok-build",
|
|
127
|
+
label: "Grok Build",
|
|
128
|
+
vendor: "xAI",
|
|
129
|
+
purpose: "xAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B grok \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
|
|
130
|
+
package: "@xai-official/grok",
|
|
131
|
+
version: "1.0.41",
|
|
132
|
+
manager: "npm",
|
|
133
|
+
installArgs: ["install", "-g", "@xai-official/grok@1.0.41", "--registry=https://registry.npmjs.org/"],
|
|
134
|
+
probeExecutables: ["grok"],
|
|
135
|
+
providerHints: ["xai", "grok"]
|
|
136
|
+
}),
|
|
137
|
+
Object.freeze({
|
|
138
|
+
id: "zcode",
|
|
139
|
+
label: "ZCode",
|
|
140
|
+
vendor: "Z.ai",
|
|
141
|
+
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",
|
|
142
|
+
version: "3.14.3",
|
|
143
|
+
manager: "signed-windows-installer",
|
|
144
|
+
installArgs: [],
|
|
145
|
+
probeExecutables: [],
|
|
146
|
+
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",
|
|
147
|
+
providerHints: ["zai", "z.ai", "zcode", "glm", "zhipu"]
|
|
148
|
+
})
|
|
149
|
+
]);
|
|
150
|
+
TOOL_BY_ID = new Map(OFFICIAL_TOOLS.map((tool) => [tool.id, tool]));
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// .dsh-plugin/shared/model-profiles.mjs
|
|
155
|
+
function nonnegative(value, label) {
|
|
156
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0 || value > 1e6) {
|
|
157
|
+
throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 1000000 \u4E4B\u95F4\u7684\u6709\u9650\u6570\u5B57`);
|
|
158
|
+
}
|
|
159
|
+
return value;
|
|
160
|
+
}
|
|
161
|
+
function normalizeProfile(entry, index) {
|
|
162
|
+
const label = `\u7B2C ${index + 1} \u4E2A\u6A21\u578B`;
|
|
163
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) throw new Error(`${label} \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
164
|
+
const allowed = /* @__PURE__ */ new Set(["provider", "model", "quality", "pricing", "specialties", "cliModel"]);
|
|
165
|
+
const unknown = Object.keys(entry).find((key) => !allowed.has(key));
|
|
166
|
+
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`);
|
|
167
|
+
const provider = id(entry.provider);
|
|
168
|
+
const model = id(entry.model);
|
|
169
|
+
if (!provider || !model || provider.length > 160 || model.length > 240) {
|
|
170
|
+
throw new Error(`${label} \u9700\u8981\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u51C6\u786E provider \u548C model`);
|
|
171
|
+
}
|
|
172
|
+
const profile = { provider, model };
|
|
173
|
+
if (entry.quality !== void 0) {
|
|
174
|
+
const value = nonnegative(entry.quality, `${label} \u7684 quality`);
|
|
175
|
+
if (value > 100) throw new Error(`${label} \u7684 quality \u5E94\u5728 0 \u5230 100 \u4E4B\u95F4`);
|
|
176
|
+
profile.quality = value / 100;
|
|
177
|
+
}
|
|
178
|
+
if (entry.pricing !== void 0) {
|
|
179
|
+
const prices = entry.pricing;
|
|
180
|
+
if (!prices || typeof prices !== "object" || Array.isArray(prices)) throw new Error(`${label} \u7684 pricing \u5FC5\u987B\u662F\u5BF9\u8C61`);
|
|
181
|
+
const priceFields = /* @__PURE__ */ new Set(["input", "output", "cacheRead", "cacheWrite", "currency"]);
|
|
182
|
+
const unknownPrice = Object.keys(prices).find((key) => !priceFields.has(key));
|
|
183
|
+
if (unknownPrice) throw new Error(`${label} \u7684 pricing \u542B\u4E0D\u652F\u6301\u7684\u5B57\u6BB5 ${unknownPrice}`);
|
|
184
|
+
if (prices.currency !== void 0 && prices.currency !== "USD") throw new Error(`${label} \u7684\u4EF7\u683C\u5E01\u79CD\u76EE\u524D\u4EC5\u652F\u6301 USD`);
|
|
185
|
+
profile.pricing = {
|
|
186
|
+
input: nonnegative(prices.input, `${label} \u7684\u8F93\u5165\u5355\u4EF7`),
|
|
187
|
+
output: nonnegative(prices.output, `${label} \u7684\u8F93\u51FA\u5355\u4EF7`),
|
|
188
|
+
currency: "USD"
|
|
189
|
+
};
|
|
190
|
+
if (prices.cacheRead !== void 0) profile.pricing.cacheRead = nonnegative(prices.cacheRead, `${label} \u7684\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7`);
|
|
191
|
+
if (prices.cacheWrite !== void 0) profile.pricing.cacheWrite = nonnegative(prices.cacheWrite, `${label} \u7684\u7F13\u5B58\u5199\u5165\u5355\u4EF7`);
|
|
192
|
+
}
|
|
193
|
+
if (entry.specialties !== void 0) {
|
|
194
|
+
if (!Array.isArray(entry.specialties) || entry.specialties.length > 16 || entry.specialties.some((value) => !/^[a-z][a-z0-9-]{0,39}$/.test(value))) {
|
|
195
|
+
throw new Error(`${label} \u7684 specialties \u5FC5\u987B\u662F\u81F3\u591A 16 \u4E2A\u82F1\u6587\u4EFB\u52A1\u6807\u7B7E`);
|
|
196
|
+
}
|
|
197
|
+
profile.specialties = [...new Set(entry.specialties)];
|
|
198
|
+
}
|
|
199
|
+
if (entry.cliModel !== void 0) {
|
|
200
|
+
const cliModel = id(entry.cliModel);
|
|
201
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:/-]{0,119}$/.test(cliModel)) throw new Error(`${label} \u7684 cliModel \u65E0\u6548`);
|
|
202
|
+
if (toolForProvider(provider)?.id === "zcode") throw new Error(`${label} \u5BF9\u5E94\u7684 ZCode CLI \u6682\u4E0D\u652F\u6301\u9010\u6B21\u5207\u6362\u6A21\u578B`);
|
|
203
|
+
profile.cliModel = cliModel;
|
|
204
|
+
}
|
|
205
|
+
if (Object.keys(profile).length === 2) throw new Error(`${label} \u81F3\u5C11\u63D0\u4F9B quality\u3001pricing\u3001specialties \u6216 cliModel \u4E4B\u4E00`);
|
|
206
|
+
return profile;
|
|
207
|
+
}
|
|
208
|
+
function parseModelProfilesJson(value) {
|
|
209
|
+
const source = value == null || value === "" ? "[]" : value;
|
|
210
|
+
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");
|
|
211
|
+
let input;
|
|
212
|
+
try {
|
|
213
|
+
input = JSON.parse(source);
|
|
214
|
+
} catch {
|
|
215
|
+
throw new Error("\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\u4E0D\u662F\u6709\u6548\u7684 JSON");
|
|
216
|
+
}
|
|
217
|
+
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");
|
|
218
|
+
const profiles = input.map(normalizeProfile);
|
|
219
|
+
const seen = /* @__PURE__ */ new Set();
|
|
220
|
+
for (const profile of profiles) {
|
|
221
|
+
const key = `${profile.provider}\0${profile.model}`;
|
|
222
|
+
if (seen.has(key)) throw new Error(`\u6A21\u578B ${profile.provider}/${profile.model} \u91CD\u590D\u914D\u7F6E`);
|
|
223
|
+
seen.add(key);
|
|
224
|
+
}
|
|
225
|
+
return profiles;
|
|
226
|
+
}
|
|
227
|
+
function applyModelProfiles(routes, profiles) {
|
|
228
|
+
const input = Array.isArray(routes) ? routes : [];
|
|
229
|
+
const values = typeof profiles === "string" ? parseModelProfilesJson(profiles) : profiles;
|
|
230
|
+
if (!Array.isArray(values)) throw new Error("\u6A21\u578B\u914D\u7F6E\u5FC5\u987B\u662F\u6570\u7EC4");
|
|
231
|
+
const byKey = new Map(values.map((profile) => [`${profile.provider}\0${profile.model}`, profile]));
|
|
232
|
+
return input.map((route) => {
|
|
233
|
+
const profile = byKey.get(`${route.provider}\0${route.model}`);
|
|
234
|
+
if (!profile) return route;
|
|
235
|
+
return {
|
|
236
|
+
...route,
|
|
237
|
+
...profile.quality === void 0 ? {} : { quality: profile.quality, qualitySource: "user" },
|
|
238
|
+
...profile.pricing === void 0 ? {} : { pricing: { ...profile.pricing }, pricingSource: "user" },
|
|
239
|
+
...profile.specialties === void 0 ? {} : { specialties: [...profile.specialties] },
|
|
240
|
+
...profile.cliModel === void 0 ? {} : { cliModel: profile.cliModel }
|
|
241
|
+
};
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
var MAX_TEXT, MAX_PROFILES, id;
|
|
245
|
+
var init_model_profiles = __esm({
|
|
246
|
+
".dsh-plugin/shared/model-profiles.mjs"() {
|
|
247
|
+
init_official_tool_registry();
|
|
248
|
+
MAX_TEXT = 32e3;
|
|
249
|
+
MAX_PROFILES = 200;
|
|
250
|
+
id = (value) => typeof value === "string" ? value.trim() : "";
|
|
251
|
+
}
|
|
252
|
+
});
|
|
253
|
+
|
|
42
254
|
// .dsh-plugin/shared/livebench.mjs
|
|
43
255
|
function normalized(value) {
|
|
44
256
|
return String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
@@ -98,7 +310,7 @@ function assessComplexity(text2) {
|
|
|
98
310
|
const domainMarkers = (value.match(/代码|工程|架构|接口|实现|部署|测试|模块|拆分|约束|评估|证明|定理|研究|论文|图片|图像|照片|视觉|code|api|debug|proof|research|vision/gi) ?? []).length;
|
|
99
311
|
const domainComplexity = clamp(domainMarkers / 5) * 0.28;
|
|
100
312
|
const raw = clamp(0.1 + lengthScore * 0.3 + requirementScore * 0.18 + domainComplexity + codeScore + highReasoningScore + visionScore);
|
|
101
|
-
const band = raw < 0.34 ? "simple" : raw < 0.66 ? "balanced" : "complex";
|
|
313
|
+
const band = isHardRequirement(value) ? "complex" : isSimpleRequirement(value) && value.length <= 180 ? "simple" : raw < 0.34 ? "simple" : raw < 0.66 ? "balanced" : "complex";
|
|
102
314
|
return { value: raw, band };
|
|
103
315
|
}
|
|
104
316
|
function specialtyMatch(model, taskType, liveScores = {}) {
|
|
@@ -117,6 +329,7 @@ function specialtyForTask(row, taskType) {
|
|
|
117
329
|
return specialtyMatch(row.metadata, taskType, row.liveScores);
|
|
118
330
|
}
|
|
119
331
|
function asScore(value) {
|
|
332
|
+
if (value === null || value === void 0 || value === "") return void 0;
|
|
120
333
|
const number = Number(value);
|
|
121
334
|
if (!Number.isFinite(number)) return void 0;
|
|
122
335
|
return clamp(number > 1 ? number / 100 : number);
|
|
@@ -124,14 +337,17 @@ function asScore(value) {
|
|
|
124
337
|
function normalizePricing(pricing) {
|
|
125
338
|
if (pricing === null || typeof pricing !== "object" || Array.isArray(pricing)) return {};
|
|
126
339
|
const normalized2 = {};
|
|
127
|
-
for (const [
|
|
340
|
+
for (const [id2, raw] of Object.entries(pricing)) {
|
|
128
341
|
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) continue;
|
|
342
|
+
if ((raw.input ?? raw.costIn) === null || (raw.input ?? raw.costIn) === void 0) continue;
|
|
343
|
+
if ((raw.output ?? raw.costOut) === null || (raw.output ?? raw.costOut) === void 0) continue;
|
|
129
344
|
const input = Number(raw.input ?? raw.costIn);
|
|
130
345
|
const output = Number(raw.output ?? raw.costOut);
|
|
131
|
-
const cacheRead = Number(raw.cacheRead ??
|
|
132
|
-
const cacheWrite = Number(raw.cacheWrite ??
|
|
346
|
+
const cacheRead = Number(raw.cacheRead ?? input);
|
|
347
|
+
const cacheWrite = Number(raw.cacheWrite ?? input);
|
|
133
348
|
if (![input, output, cacheRead, cacheWrite].every((value) => Number.isFinite(value) && value >= 0)) continue;
|
|
134
|
-
|
|
349
|
+
if (String(raw.currency ?? "USD").toUpperCase() !== "USD") continue;
|
|
350
|
+
normalized2[normalize(id2)] = {
|
|
135
351
|
input,
|
|
136
352
|
output,
|
|
137
353
|
cacheRead,
|
|
@@ -145,11 +361,13 @@ function pricingFor(model, pricing, provider = "") {
|
|
|
145
361
|
const normalizedPricing = normalizePricing(pricing);
|
|
146
362
|
const providerOverride = provider === "" ? void 0 : normalizedPricing[normalize(`${provider}/${model.id}`)];
|
|
147
363
|
const override = providerOverride ?? normalizedPricing[normalize(model.id)];
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
364
|
+
if (override) return override;
|
|
365
|
+
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;
|
|
366
|
+
return {
|
|
367
|
+
input: Number(model.costIn),
|
|
368
|
+
output: Number(model.costOut),
|
|
369
|
+
cacheRead: Number(model.cacheRead ?? model.costIn),
|
|
370
|
+
cacheWrite: Number(model.cacheWrite ?? model.costIn),
|
|
153
371
|
currency: "USD"
|
|
154
372
|
};
|
|
155
373
|
}
|
|
@@ -166,6 +384,7 @@ function effectivePricing(pricing, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
|
166
384
|
};
|
|
167
385
|
}
|
|
168
386
|
function costScore(pricing, maxCost, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
387
|
+
if (pricing === null) return 0;
|
|
169
388
|
const effective = effectivePricing(pricing, cacheReadRatio, cacheWriteRatio);
|
|
170
389
|
const mean = (effective.input + effective.output) / 2;
|
|
171
390
|
if (maxCost <= 0) return mean === 0 ? 1 : 0;
|
|
@@ -173,6 +392,7 @@ function costScore(pricing, maxCost, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
|
173
392
|
}
|
|
174
393
|
function estimateCost(model, text2, outputTokens = 900, pricingOverrides = {}, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
175
394
|
const pricing = pricingFor(model, pricingOverrides);
|
|
395
|
+
if (pricing === null) return null;
|
|
176
396
|
const inputTokens = Math.max(80, Math.ceil(String(text2 ?? "").length / 3.7));
|
|
177
397
|
const ratios = normalizedCacheRatios(cacheReadRatio, cacheWriteRatio);
|
|
178
398
|
const cacheReadTokens = Math.min(inputTokens, Math.max(0, Math.round(inputTokens * ratios.read)));
|
|
@@ -182,16 +402,14 @@ function estimateCost(model, text2, outputTokens = 900, pricingOverrides = {}, c
|
|
|
182
402
|
}
|
|
183
403
|
function modelMetadata(name) {
|
|
184
404
|
const key = normalize(name);
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
return key === candidate || key.includes(candidate) || candidate.includes(key);
|
|
188
|
-
})) ?? null;
|
|
405
|
+
if (!key) return null;
|
|
406
|
+
return MODEL_CATALOG.find((model) => model.aliases.some((alias) => key === normalize(alias))) ?? null;
|
|
189
407
|
}
|
|
190
408
|
function taskTokenBudget(text2, task, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
191
409
|
const inputTokens = Math.max(80, Math.ceil(String(text2 ?? "").length / 3.7));
|
|
192
410
|
const multipliers = {
|
|
193
411
|
analysis: { input: 0.9, output: 0.55 },
|
|
194
|
-
execution: { input: 1.2, output: complexity === "complex" ? 1.45 : 1 },
|
|
412
|
+
execution: { input: 1.2, output: (task.difficulty ?? complexity) === "complex" ? 1.45 : 1 },
|
|
195
413
|
verification: { input: 1.15, output: 0.7 },
|
|
196
414
|
synthesis: { input: 1.65, output: 1.3 }
|
|
197
415
|
};
|
|
@@ -209,11 +427,36 @@ function taskTokenBudget(text2, task, complexity, cacheReadRatio = 0, cacheWrite
|
|
|
209
427
|
};
|
|
210
428
|
}
|
|
211
429
|
function taskQualityFloor(band, task) {
|
|
212
|
-
const
|
|
430
|
+
const difficulty = task.difficulty ?? band;
|
|
431
|
+
const base = QUALITY_FLOORS[difficulty] ?? QUALITY_FLOORS.balanced;
|
|
213
432
|
if (task.purpose === "synthesis") return Math.max(base, band === "complex" ? 0.84 : base);
|
|
214
|
-
if (
|
|
433
|
+
if (difficulty !== "complex") return base;
|
|
215
434
|
return clamp(base + Math.max(0, Number(task.criticality ?? 0.75) - 0.65) * 0.12);
|
|
216
435
|
}
|
|
436
|
+
function isSimpleRequirement(value) {
|
|
437
|
+
const item = String(value ?? "").trim();
|
|
438
|
+
return SIMPLE_REQUEST_START.test(item) && !/(?:并|然后|最后|同时|以及|and then).{0,20}(?:设计|实现|验证|部署|证明|审计|测试|design|implement|verify|deploy|prove|audit|test)/i.test(item);
|
|
439
|
+
}
|
|
440
|
+
function isHardRequirement(value) {
|
|
441
|
+
const item = String(value ?? "").trim();
|
|
442
|
+
if (isSimpleRequirement(item)) return false;
|
|
443
|
+
return HIGH_STAKES_WORK.test(item) && HIGH_STAKES_ACTION.test(item);
|
|
444
|
+
}
|
|
445
|
+
function requirementDifficulty(objective, type, fallback = "balanced") {
|
|
446
|
+
const value = String(objective ?? "").trim();
|
|
447
|
+
if (isHardRequirement(value)) return "complex";
|
|
448
|
+
if (value.length <= 180 && SIMPLE_WORK.test(value)) return "simple";
|
|
449
|
+
const assessed = assessComplexity(value);
|
|
450
|
+
if (type === "math" && /证明|推导|proof|derive/i.test(value)) return "complex";
|
|
451
|
+
if (assessed.band === "simple" && (type === "code" || type === "research")) return "balanced";
|
|
452
|
+
return assessed.band === "simple" ? "simple" : assessed.band === "complex" ? "complex" : fallback;
|
|
453
|
+
}
|
|
454
|
+
function shouldSplitRequirements(requirements) {
|
|
455
|
+
if (requirements.length >= 3) return true;
|
|
456
|
+
if (requirements.length !== 2) return false;
|
|
457
|
+
const types = requirements.map((item) => detectTaskTypes(item)[0] ?? "general");
|
|
458
|
+
return types[0] !== types[1] || /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(requirements[1]);
|
|
459
|
+
}
|
|
217
460
|
function requirementText(value) {
|
|
218
461
|
return String(value ?? "").trim().replace(/[。;;\s]+$/u, "").trim();
|
|
219
462
|
}
|
|
@@ -238,14 +481,15 @@ function explicitRequirements(text2) {
|
|
|
238
481
|
}
|
|
239
482
|
const value = visibleLines.join("\n");
|
|
240
483
|
const lines = visibleLines.map((line) => line.trim()).filter(Boolean);
|
|
484
|
+
if (/^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(lines[0] ?? "") && !/(?:执行|完成|实施|分配)/u.test(lines[0])) return [];
|
|
241
485
|
const marker = /^(?:[-*•]\s+|\d{1,2}[.)、](?!\d)\s*|[一二三四五六七八九十]{1,3}[、.)]\s*)(.+)$/u;
|
|
242
|
-
const marked = lines.map((line) => marker.exec(line)?.[1]).filter(Boolean).map(requirementText).filter(
|
|
486
|
+
const marked = lines.map((line) => marker.exec(line)?.[1]).filter(Boolean).map(requirementText).filter(looksLikeRequirement);
|
|
243
487
|
if (marked.length >= 2) return marked;
|
|
244
488
|
const plain2 = lines.filter((line) => !/^#{1,6}\s|^```|[::]$/u.test(line));
|
|
245
489
|
if (plain2.length >= 2 && plain2.every(looksLikeRequirement)) {
|
|
246
490
|
return plain2.map(requirementText).filter((item) => item.length >= 3);
|
|
247
491
|
}
|
|
248
|
-
const clauses = value.split(/[
|
|
492
|
+
const clauses = value.split(/[;;,,]/u).map(requirementText).filter(Boolean);
|
|
249
493
|
if (clauses.length >= 2 && clauses.every(looksLikeRequirement)) {
|
|
250
494
|
return clauses;
|
|
251
495
|
}
|
|
@@ -257,12 +501,12 @@ function explicitRequirements(text2) {
|
|
|
257
501
|
}
|
|
258
502
|
function taskPackages(taskType, text2, band) {
|
|
259
503
|
if (band !== "complex") {
|
|
260
|
-
const task = { id: "execution", name: "\u76F4\u63A5\u56DE\u7B54\u4E0E\u5FC5\u8981\u6821\u9A8C", type: taskType, purpose: "execution", criticality: 0.65, dependsOn: [], preferredReasoningEffort: band === "simple" ? "low" : "medium" };
|
|
504
|
+
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" };
|
|
261
505
|
return [{ ...task, qualityFloor: taskQualityFloor(band, task) }];
|
|
262
506
|
}
|
|
263
507
|
const value = String(text2 ?? "");
|
|
264
508
|
const packages = [
|
|
265
|
-
{ id: "analysis", name: "\u95EE\u9898\u5EFA\u6A21\u4E0E\u7EA6\u675F\u63D0\u53D6", type: "reasoning", purpose: "analysis", criticality: 0.
|
|
509
|
+
{ id: "analysis", name: "\u95EE\u9898\u5EFA\u6A21\u4E0E\u7EA6\u675F\u63D0\u53D6", type: "reasoning", purpose: "analysis", difficulty: "balanced", criticality: 0.8, dependsOn: [], preferredReasoningEffort: "medium" }
|
|
266
510
|
];
|
|
267
511
|
const requirements = explicitRequirements(text2);
|
|
268
512
|
if (requirements.length >= 2) {
|
|
@@ -272,29 +516,37 @@ function taskPackages(taskType, text2, band) {
|
|
|
272
516
|
] : requirements.map((item) => [item]);
|
|
273
517
|
groups.forEach((group, index) => {
|
|
274
518
|
const objective = group.length === 1 ? group[0] : group.map((item, offset) => `${MAX_EXPLICIT_EXECUTION_PACKAGES + offset}. ${item}`).join("\n");
|
|
275
|
-
const type = detectTaskTypes(objective)[0] ??
|
|
519
|
+
const type = detectTaskTypes(objective)[0] ?? "general";
|
|
520
|
+
const difficulty = group.length > 1 ? "complex" : requirementDifficulty(objective, type);
|
|
521
|
+
const previous = packages.at(-1);
|
|
522
|
+
const sequential = /^(?:最后|然后|接着|随后|再|基于|根据|测试|验证|部署|发布)|(?:完成|结束|实现)后/u.test(objective);
|
|
276
523
|
packages.push({
|
|
277
524
|
id: `execution-${index + 1}`,
|
|
278
525
|
name: group.length === 1 ? `\u9700\u6C42 ${index + 1}\uFF1A${group[0].slice(0, 28)}` : `\u9700\u6C42 ${index + 1}\uFF1A\u5176\u4F59 ${group.length} \u9879`,
|
|
279
526
|
objective,
|
|
280
527
|
type,
|
|
281
528
|
purpose: "execution",
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
529
|
+
difficulty,
|
|
530
|
+
criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
|
|
531
|
+
dependsOn: sequential && previous?.purpose === "execution" ? ["analysis", previous.id] : ["analysis"],
|
|
532
|
+
preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
|
|
285
533
|
});
|
|
286
534
|
});
|
|
287
535
|
} else {
|
|
288
536
|
const domains = [...new Set([...detectTaskTypes(text2), taskType].filter((type) => type !== "general"))];
|
|
289
537
|
for (const type of domains.length > 0 ? domains : [taskType]) {
|
|
538
|
+
const objective = value.split(/[,,。;;]|最后|然后|接着|并且/u).map((item) => item.trim()).filter((item) => detectTaskTypes(item).includes(type)).join("\uFF1B") || value;
|
|
539
|
+
const difficulty = requirementDifficulty(objective, type);
|
|
290
540
|
packages.push({
|
|
291
541
|
id: `execution-${type}`,
|
|
292
542
|
name: `${TASK_TYPE_LABELS[type] ?? type}\u65B9\u5411\u5904\u7406`,
|
|
543
|
+
objective,
|
|
293
544
|
type,
|
|
294
545
|
purpose: "execution",
|
|
295
|
-
|
|
546
|
+
difficulty,
|
|
547
|
+
criticality: difficulty === "simple" ? 0.55 : difficulty === "balanced" ? 0.72 : 0.86,
|
|
296
548
|
dependsOn: ["analysis"],
|
|
297
|
-
preferredReasoningEffort: "high"
|
|
549
|
+
preferredReasoningEffort: difficulty === "simple" ? "low" : difficulty === "balanced" ? "medium" : "high"
|
|
298
550
|
});
|
|
299
551
|
}
|
|
300
552
|
}
|
|
@@ -304,6 +556,7 @@ function taskPackages(taskType, text2, band) {
|
|
|
304
556
|
name: "\u9A8C\u8BC1\u3001\u53CD\u4F8B\u4E0E\u98CE\u9669\u5BA1\u67E5",
|
|
305
557
|
type: "reasoning",
|
|
306
558
|
purpose: "verification",
|
|
559
|
+
difficulty: "complex",
|
|
307
560
|
criticality: 0.88,
|
|
308
561
|
dependsOn: packages.filter((task) => task.purpose === "execution").map((task) => task.id),
|
|
309
562
|
preferredReasoningEffort: "high"
|
|
@@ -314,6 +567,7 @@ function taskPackages(taskType, text2, band) {
|
|
|
314
567
|
name: "\u7ED3\u679C\u6821\u9A8C\u4E0E\u6574\u5408",
|
|
315
568
|
type: "reasoning",
|
|
316
569
|
purpose: "synthesis",
|
|
570
|
+
difficulty: "complex",
|
|
317
571
|
criticality: 1,
|
|
318
572
|
dependsOn: packages.filter((task) => task.purpose !== "analysis").map((task) => task.id),
|
|
319
573
|
preferredReasoningEffort: "xhigh"
|
|
@@ -321,7 +575,7 @@ function taskPackages(taskType, text2, band) {
|
|
|
321
575
|
return packages.map((task) => ({ ...task, qualityFloor: taskQualityFloor(band, task) }));
|
|
322
576
|
}
|
|
323
577
|
function weightsForTask(weights, task) {
|
|
324
|
-
return task.purpose === "synthesis" ? SYNTHESIS_WEIGHTS : weights;
|
|
578
|
+
return task.purpose === "synthesis" ? SYNTHESIS_WEIGHTS : OBJECTIVE_WEIGHTS[task.difficulty] ?? weights;
|
|
325
579
|
}
|
|
326
580
|
function compareText(left, right) {
|
|
327
581
|
const a = String(left);
|
|
@@ -380,7 +634,7 @@ function candidateUtility(row, task, weights, maxCost, usedRoutes, cacheReadRati
|
|
|
380
634
|
const quality = qualityForTask(row, task.type);
|
|
381
635
|
const floor = Number(task.qualityFloor ?? taskQualityFloor("complex", task));
|
|
382
636
|
const qualityGap = Math.max(0, floor - quality);
|
|
383
|
-
const duplicatePenalty =
|
|
637
|
+
const duplicatePenalty = 0;
|
|
384
638
|
const synthesisPreference = task.purpose === "synthesis" && /deepseek[- ]?v4[- ]?pro/i.test(row.model) ? 0.025 : 0;
|
|
385
639
|
const reasoning = reasoningDecision(row, task);
|
|
386
640
|
const cost = clamp(costScore(row.pricing, maxCost, cacheReadRatio, cacheWriteRatio) / Math.sqrt(reasoning.multiplier.output));
|
|
@@ -388,11 +642,13 @@ function candidateUtility(row, task, weights, maxCost, usedRoutes, cacheReadRati
|
|
|
388
642
|
return { score, floor, qualityGap, ...reasoning };
|
|
389
643
|
}
|
|
390
644
|
function taskCost(row, task, text2, complexity, cacheReadRatio = 0, cacheWriteRatio = 0) {
|
|
645
|
+
if (row?.pricing === null) return 0;
|
|
391
646
|
const decision = row === null ? null : reasoningDecision(row, task);
|
|
392
647
|
const tokens = taskTokenBudget(text2, { ...task, reasoningEffort: decision?.reasoningEffort }, complexity, cacheReadRatio, cacheWriteRatio);
|
|
393
648
|
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;
|
|
394
649
|
}
|
|
395
650
|
function dominates(left, right, task, text2, complexity, cacheReadRatio, cacheWriteRatio) {
|
|
651
|
+
if (left.pricing === null || right.pricing === null) return false;
|
|
396
652
|
const leftReasoning = reasoningDecision(left, task);
|
|
397
653
|
const rightReasoning = reasoningDecision(right, task);
|
|
398
654
|
const leftValues = {
|
|
@@ -416,9 +672,10 @@ function dominates(left, right, task, text2, complexity, cacheReadRatio, cacheWr
|
|
|
416
672
|
return noWorse && strictlyBetter;
|
|
417
673
|
}
|
|
418
674
|
function candidatePool(rows, task, weights, maxCost, text2, complexity, cacheReadRatio, cacheWriteRatio) {
|
|
675
|
+
const eligibleRows = task.type === "vision" ? rows.filter((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image")) : rows;
|
|
419
676
|
const floor = Number(task.qualityFloor ?? 0);
|
|
420
|
-
const feasible =
|
|
421
|
-
const source = feasible.length > 0 ? feasible :
|
|
677
|
+
const feasible = eligibleRows.filter((row) => qualityForTask(row, task.type) >= floor);
|
|
678
|
+
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);
|
|
422
679
|
const taskWeights = weightsForTask(weights, task);
|
|
423
680
|
const scored = source.map((row) => ({
|
|
424
681
|
row,
|
|
@@ -435,7 +692,7 @@ function candidatePool(rows, task, weights, maxCost, text2, complexity, cacheRea
|
|
|
435
692
|
return {
|
|
436
693
|
options: ordered,
|
|
437
694
|
relaxed: feasible.length === 0,
|
|
438
|
-
pruned: Math.max(0,
|
|
695
|
+
pruned: Math.max(0, eligibleRows.length - ordered.length)
|
|
439
696
|
};
|
|
440
697
|
}
|
|
441
698
|
function stateSignature(state) {
|
|
@@ -452,7 +709,9 @@ function solveAssignments({ rows, tasks, weights, maxCost, text: text2, complexi
|
|
|
452
709
|
if (pools.some((pool) => pool.options.length === 0)) return null;
|
|
453
710
|
const suffixMinimum = Array(tasks.length + 1).fill(0);
|
|
454
711
|
for (let index = tasks.length - 1; index >= 0; index -= 1) {
|
|
455
|
-
|
|
712
|
+
const costOptions = Number.isFinite(budget) ? pools[index].options.filter((option2) => option2.row.pricing !== null) : pools[index].options;
|
|
713
|
+
if (costOptions.length === 0) return null;
|
|
714
|
+
suffixMinimum[index] = suffixMinimum[index + 1] + Math.min(...costOptions.map((option2) => option2.cost));
|
|
456
715
|
}
|
|
457
716
|
if (Number.isFinite(budget) && suffixMinimum[0] > budget + 1e-12) return null;
|
|
458
717
|
let states = [{ assignments: [], routesByTask: /* @__PURE__ */ new Map(), usedRoutes: /* @__PURE__ */ new Set(), score: 0, cost: 0, switches: 0, relaxedCount: 0, qualityShortfall: 0 }];
|
|
@@ -462,6 +721,7 @@ function solveAssignments({ rows, tasks, weights, maxCost, text: text2, complexi
|
|
|
462
721
|
const expanded = [];
|
|
463
722
|
for (const state of states) {
|
|
464
723
|
for (const option2 of pool.options) {
|
|
724
|
+
if (Number.isFinite(budget) && option2.row.pricing === null) continue;
|
|
465
725
|
const nextCost = state.cost + option2.cost;
|
|
466
726
|
if (Number.isFinite(budget) && nextCost + suffixMinimum[index + 1] > budget + 1e-12) continue;
|
|
467
727
|
const taskWeights = weightsForTask(weights, task);
|
|
@@ -501,8 +761,13 @@ function solveAssignments({ rows, tasks, weights, maxCost, text: text2, complexi
|
|
|
501
761
|
};
|
|
502
762
|
}
|
|
503
763
|
function buildPlan({ text: text2 = "", available = [], mode = "collective", pricing = {}, liveBench = null, liveBenchError = "", budgetUsd = 0, cacheReadRatio = 0, cacheWriteRatio = 0 } = {}) {
|
|
504
|
-
const
|
|
505
|
-
const
|
|
764
|
+
const firstLine = String(text2 ?? "").split(/\r?\n/u)[0].trim();
|
|
765
|
+
const transformOnly = /^(?:请|帮我)?(?:总结|概括|翻译|摘要|解释)(?:以下|下列|下面|这份|这些)/u.test(firstLine) && !/(?:执行|完成|实施|分配)/u.test(firstLine);
|
|
766
|
+
const assessed = assessComplexity(transformOnly ? firstLine : text2);
|
|
767
|
+
const requirements = explicitRequirements(text2);
|
|
768
|
+
const compound = shouldSplitRequirements(requirements);
|
|
769
|
+
const complexity = compound && assessed.band !== "complex" ? { value: Math.max(0.66, assessed.value), band: "complex" } : assessed;
|
|
770
|
+
const taskType = classifyTask(transformOnly ? firstLine : text2);
|
|
506
771
|
const weights = OBJECTIVE_WEIGHTS[complexity.band];
|
|
507
772
|
const discovered = Array.isArray(available) ? available.map((entry) => {
|
|
508
773
|
const rawEfforts = Array.isArray(entry.reasoningEfforts) ? entry.reasoningEfforts : [];
|
|
@@ -512,55 +777,71 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
|
|
|
512
777
|
model: String(entry.model ?? ""),
|
|
513
778
|
reasoningEfforts: [...new Set(reasoningEfforts)],
|
|
514
779
|
defaultReasoningEffort: entry.defaultReasoningEffort === void 0 ? void 0 : String(entry.defaultReasoningEffort),
|
|
515
|
-
reasoningKnown: entry.reasoningKnown === true || entry.reasoningKnown === void 0 && Array.isArray(entry.reasoningEfforts)
|
|
780
|
+
reasoningKnown: entry.reasoningKnown === true || entry.reasoningKnown === void 0 && Array.isArray(entry.reasoningEfforts),
|
|
781
|
+
quality: asScore(entry.quality),
|
|
782
|
+
qualitySource: entry.qualitySource === "user" ? "user" : "route",
|
|
783
|
+
latency: asScore(entry.latency),
|
|
784
|
+
risk: asScore(entry.risk),
|
|
785
|
+
specialties: Array.isArray(entry.specialties) ? entry.specialties.filter((item) => typeof item === "string") : null,
|
|
786
|
+
pricing: normalizePricing({ route: entry.pricing ?? entry.price }).route ?? null,
|
|
787
|
+
pricingSource: entry.pricingSource === "user" ? "user" : "route",
|
|
788
|
+
inputModalities: Array.isArray(entry.inputModalities) ? entry.inputModalities.map((item) => String(item).toLowerCase()) : []
|
|
516
789
|
};
|
|
517
790
|
}) : [];
|
|
518
791
|
const rows = [];
|
|
519
792
|
const normalizedPrices = normalizePricing(pricing);
|
|
520
|
-
const maxCost = Math.max(1, ...MODEL_CATALOG.map((model) => {
|
|
521
|
-
const row = pricingFor(model, normalizedPrices);
|
|
522
|
-
const effective = effectivePricing(row, cacheReadRatio, cacheWriteRatio);
|
|
523
|
-
return effective.input + effective.output;
|
|
524
|
-
}), ...Object.values(normalizedPrices).map((row) => {
|
|
525
|
-
const effective = effectivePricing(row, cacheReadRatio, cacheWriteRatio);
|
|
526
|
-
return effective.input + effective.output;
|
|
527
|
-
}));
|
|
528
793
|
for (const route of discovered) {
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
specialties:
|
|
537
|
-
risk: 0.24
|
|
794
|
+
if (!route.provider || !route.model) continue;
|
|
795
|
+
const catalog = modelMetadata(route.model);
|
|
796
|
+
const metadata = {
|
|
797
|
+
...catalog ?? { id: route.model, aliases: [route.model], specialties: [] },
|
|
798
|
+
...route.quality === void 0 ? {} : { quality: route.quality },
|
|
799
|
+
...route.latency === void 0 ? {} : { latency: route.latency },
|
|
800
|
+
...route.risk === void 0 ? {} : { risk: route.risk },
|
|
801
|
+
...route.specialties === null ? {} : { specialties: route.specialties }
|
|
538
802
|
};
|
|
539
803
|
const live = liveBenchRow(liveBench, route.model);
|
|
540
804
|
const liveScores = live?.scores ?? {};
|
|
541
805
|
const liveOverall = asScore(live?.overall);
|
|
542
|
-
const quality = asScore(liveScores?.[taskType]) ?? liveOverall ?? metadata.quality;
|
|
543
|
-
const
|
|
806
|
+
const quality = asScore(liveScores?.[taskType]) ?? liveOverall ?? asScore(metadata.quality) ?? 0;
|
|
807
|
+
const qualitySource = liveOverall !== void 0 || asScore(liveScores?.[taskType]) !== void 0 ? "livebench" : route.quality !== void 0 ? route.qualitySource : catalog ? "catalog-heuristic" : "unknown";
|
|
808
|
+
const userPrice = normalizedPrices[normalize(`${route.provider}/${route.model}`)] ?? normalizedPrices[normalize(route.model)];
|
|
809
|
+
const pricingRow = userPrice ?? route.pricing ?? null;
|
|
810
|
+
const pricingSource = userPrice ? "user" : route.pricing ? route.pricingSource : "unknown";
|
|
544
811
|
const specialty = specialtyMatch(metadata, taskType, liveScores);
|
|
545
812
|
rows.push({
|
|
546
813
|
provider: route.provider,
|
|
547
814
|
model: route.model,
|
|
548
815
|
metadata,
|
|
549
816
|
quality,
|
|
817
|
+
qualitySource,
|
|
818
|
+
pricingSource,
|
|
550
819
|
liveScores,
|
|
551
820
|
liveOverall,
|
|
552
|
-
latency: metadata.latency,
|
|
553
|
-
risk: metadata.risk,
|
|
821
|
+
latency: metadata.latency ?? 0.5,
|
|
822
|
+
risk: metadata.risk ?? 0.2,
|
|
554
823
|
specialty,
|
|
555
824
|
reasoningEfforts: route.reasoningEfforts,
|
|
556
825
|
defaultReasoningEffort: route.defaultReasoningEffort,
|
|
557
826
|
reasoningKnown: route.reasoningKnown,
|
|
827
|
+
inputModalities: route.inputModalities,
|
|
558
828
|
pricing: pricingRow,
|
|
559
829
|
score: 0,
|
|
560
|
-
estimatedCost:
|
|
830
|
+
estimatedCost: pricingRow === null ? null : estimateCost({
|
|
831
|
+
id: route.model,
|
|
832
|
+
costIn: pricingRow.input,
|
|
833
|
+
costOut: pricingRow.output,
|
|
834
|
+
cacheRead: pricingRow.cacheRead,
|
|
835
|
+
cacheWrite: pricingRow.cacheWrite
|
|
836
|
+
}, text2, 900, {}, cacheReadRatio, cacheWriteRatio)
|
|
561
837
|
});
|
|
562
838
|
}
|
|
839
|
+
const maxCost = Math.max(1, ...rows.filter((row) => row.pricing !== null).map((row) => {
|
|
840
|
+
const effective = effectivePricing(row.pricing, cacheReadRatio, cacheWriteRatio);
|
|
841
|
+
return effective.input + effective.output;
|
|
842
|
+
}));
|
|
563
843
|
const taskNodes = taskPackages(taskType, text2, complexity.band);
|
|
844
|
+
const unassignableTasks = taskNodes.filter((task) => task.type === "vision" && !rows.some((row) => row.inputModalities.length === 0 || row.inputModalities.includes("image"))).map((task) => task.id);
|
|
564
845
|
const budget = Number(budgetUsd);
|
|
565
846
|
const utilityPlan = solveAssignments({
|
|
566
847
|
rows,
|
|
@@ -605,16 +886,19 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
|
|
|
605
886
|
}
|
|
606
887
|
rows.sort((left, right) => right.score - left.score || compareRowsStable(left, right));
|
|
607
888
|
const selectedAssignment = assignments[0];
|
|
608
|
-
const selected = selectedAssignment?.row ?? rows[0] ?? null;
|
|
889
|
+
const selected = selectedAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows[0] ?? null);
|
|
609
890
|
const synthesizerAssignment = assignments.at(-1);
|
|
610
|
-
const synthesizer = synthesizerAssignment?.row ?? rows.find((row) => /deepseek/i.test(row.model)) ?? rows[0];
|
|
891
|
+
const synthesizer = synthesizerAssignment?.row ?? (unassignableTasks.length > 0 ? null : rows.find((row) => /deepseek/i.test(row.model)) ?? rows[0]);
|
|
611
892
|
const subtasks = assignments.map(({ task, row, decision }) => ({
|
|
612
893
|
id: task.id,
|
|
613
894
|
name: task.name,
|
|
614
895
|
...task.objective ? { objective: task.objective } : {},
|
|
615
896
|
type: task.type,
|
|
897
|
+
difficulty: task.difficulty,
|
|
616
898
|
recommended: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
|
|
617
899
|
recommendedProvider: row?.provider ?? "",
|
|
900
|
+
qualitySource: row?.qualitySource ?? "unknown",
|
|
901
|
+
pricingSource: row?.pricingSource ?? "unknown",
|
|
618
902
|
recommendedReasoningEffort: decision?.reasoningEffort,
|
|
619
903
|
preferredReasoningEffort: decision?.preferredReasoningEffort ?? task.preferredReasoningEffort,
|
|
620
904
|
reasoningFit: Number(Number(decision?.reasoningFit ?? 0).toFixed(3)),
|
|
@@ -629,6 +913,7 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
|
|
|
629
913
|
return {
|
|
630
914
|
stage: index + 1,
|
|
631
915
|
purpose: task.purpose,
|
|
916
|
+
difficulty: task.difficulty,
|
|
632
917
|
model: row?.model ?? "\u5F85\u53D1\u73B0\u6A21\u578B",
|
|
633
918
|
provider: row?.provider ?? "",
|
|
634
919
|
reasoningEffort: decision?.reasoningEffort,
|
|
@@ -639,35 +924,42 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
|
|
|
639
924
|
cacheReadTokens: tokens.cacheReadTokens,
|
|
640
925
|
cacheWriteTokens: tokens.cacheWriteTokens,
|
|
641
926
|
outputTokens: tokens.outputTokens,
|
|
642
|
-
estimatedCost: Number(taskEstimate.toFixed(6)),
|
|
643
|
-
quality:
|
|
927
|
+
estimatedCost: row?.pricing === null || row === null ? null : Number(taskEstimate.toFixed(6)),
|
|
928
|
+
quality: row?.qualitySource === "unknown" || row === null ? null : Number(qualityForTask(row, task.type).toFixed(3)),
|
|
929
|
+
qualitySource: row?.qualitySource ?? "unknown",
|
|
930
|
+
pricingSource: row?.pricingSource ?? "unknown",
|
|
644
931
|
handoffPenalty: Number(Number(handoffPenalty ?? 0).toFixed(3))
|
|
645
932
|
};
|
|
646
933
|
});
|
|
647
|
-
const
|
|
648
|
-
const
|
|
934
|
+
const pricingComplete = assignments.length > 0 && assignments.every(({ row }) => row?.pricing !== null);
|
|
935
|
+
const totalEstimate = pricingComplete ? costBreakdown.reduce((sum, row) => sum + row.estimatedCost, 0) : null;
|
|
936
|
+
const baselineRows = assignments.map(({ task }) => {
|
|
649
937
|
const strongest = rows.reduce((best, row) => qualityForTask(row, task.type) > (best === null ? -1 : qualityForTask(best, task.type)) ? row : best, null);
|
|
650
|
-
return
|
|
651
|
-
}
|
|
652
|
-
const
|
|
653
|
-
const
|
|
938
|
+
return { task, strongest };
|
|
939
|
+
});
|
|
940
|
+
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;
|
|
941
|
+
const qualityEvidenceComplete = assignments.length > 0 && assignments.every(({ row }) => ["livebench", "route", "user"].includes(row?.qualitySource)) && baselineRows.every(({ strongest }) => ["livebench", "route", "user"].includes(strongest?.qualitySource));
|
|
942
|
+
const budgetExceeded = Number(budgetUsd) > 0 && totalEstimate !== null ? totalEstimate > Number(budgetUsd) : null;
|
|
943
|
+
const savings = baselineCost === null || totalEstimate === null || !qualityEvidenceComplete ? null : baselineCost <= 0 ? 0 : clamp((baselineCost - totalEstimate) / baselineCost);
|
|
654
944
|
const paretoPruned = (optimized?.candidatePools ?? []).reduce((sum, pool) => sum + pool.pruned, 0);
|
|
655
|
-
const minimumFeasibleCost = optimized?.minimumFeasibleCost ?? minimumCostPlan?.cost ?? 0;
|
|
656
|
-
const reason = selected === null ? "\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`;
|
|
945
|
+
const minimumFeasibleCost = rows.every((row) => row.pricing !== null) ? optimized?.minimumFeasibleCost ?? minimumCostPlan?.cost ?? 0 : null;
|
|
946
|
+
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`;
|
|
657
947
|
return {
|
|
658
948
|
mode,
|
|
659
949
|
complexity: { value: Number(complexity.value.toFixed(3)), band: complexity.band },
|
|
950
|
+
compound,
|
|
951
|
+
unassignableTasks,
|
|
660
952
|
taskType,
|
|
661
953
|
taskTypes: [...new Set(taskNodes.map((task) => task.type).filter((type) => type !== "reasoning"))],
|
|
662
954
|
objectiveWeights: weights,
|
|
663
955
|
candidates: rows.slice(0, 8).map((row) => {
|
|
664
956
|
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);
|
|
665
|
-
return { provider: row.provider, model: row.model, score: Number(row.score.toFixed(3)), quality: Number(row.quality.toFixed(3)), 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: Number(row.estimatedCost.toFixed(6)), inputPrice: row.pricing
|
|
957
|
+
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 };
|
|
666
958
|
}),
|
|
667
|
-
selected: selected === null ? null : { provider: selected.provider, model: selected.model, reasoningEffort: selectedAssignment?.decision?.reasoningEffort, estimatedCost: Number(selected.estimatedCost.toFixed(6)) },
|
|
959
|
+
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 },
|
|
668
960
|
subtasks,
|
|
669
|
-
synthesizer: synthesizer
|
|
670
|
-
estimatedCost: Number(totalEstimate.toFixed(6)),
|
|
961
|
+
synthesizer: synthesizer == null ? null : { provider: synthesizer.provider, model: synthesizer.model, reasoningEffort: synthesizerAssignment?.decision?.reasoningEffort },
|
|
962
|
+
estimatedCost: totalEstimate === null ? null : Number(totalEstimate.toFixed(6)),
|
|
671
963
|
costBreakdown,
|
|
672
964
|
optimization: {
|
|
673
965
|
solver: "pareto-pruned quality-constrained beam assignment",
|
|
@@ -677,21 +969,23 @@ function buildPlan({ text: text2 = "", available = [], mode = "collective", pric
|
|
|
677
969
|
cacheWriteRatio: normalizedCacheRatios(cacheReadRatio, cacheWriteRatio).write,
|
|
678
970
|
budgetExceeded,
|
|
679
971
|
constraintRelaxed,
|
|
680
|
-
|
|
681
|
-
|
|
972
|
+
pricingComplete,
|
|
973
|
+
qualityEvidenceComplete,
|
|
974
|
+
baselineAllStrongCost: baselineCost === null ? null : Number(baselineCost.toFixed(6)),
|
|
975
|
+
estimatedSavings: savings === null ? null : Number(savings.toFixed(4)),
|
|
682
976
|
distinctRoutes: usedRoutes.size,
|
|
683
977
|
handoffCount: optimized?.switches ?? 0,
|
|
684
978
|
paretoPruned,
|
|
685
979
|
beamWidth: ROUTING_BEAM_WIDTH,
|
|
686
|
-
budgetFeasible: budget <= 0
|
|
687
|
-
minimumFeasibleCost: Number(Number(minimumFeasibleCost).toFixed(6)),
|
|
980
|
+
budgetFeasible: budget <= 0 ? pricingComplete ? true : null : budgetPlan !== null,
|
|
981
|
+
minimumFeasibleCost: minimumFeasibleCost === null ? null : Number(Number(minimumFeasibleCost).toFixed(6)),
|
|
688
982
|
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 || "") }
|
|
689
983
|
},
|
|
690
984
|
reason,
|
|
691
985
|
generatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
692
986
|
};
|
|
693
987
|
}
|
|
694
|
-
var OBJECTIVE_WEIGHTS, REASONING_EFFORT_ORDER, REASONING_EFFORT_MULTIPLIERS, QUALITY_FLOORS, DEFAULT_ROUTER_SETTINGS, MODEL_CATALOG, clamp, normalize, routeKey, OPENCODE_CATALOG_PROVIDERS, TASK_TYPE_RULES, TASK_TYPE_LABELS, MAX_EXPLICIT_EXECUTION_PACKAGES, REQUIREMENT_ACTION, SYNTHESIS_WEIGHTS, ROUTING_BEAM_WIDTH, ROUTING_CANDIDATE_LIMIT;
|
|
988
|
+
var OBJECTIVE_WEIGHTS, REASONING_EFFORT_ORDER, REASONING_EFFORT_MULTIPLIERS, QUALITY_FLOORS, DEFAULT_ROUTER_SETTINGS, MODEL_CATALOG, clamp, normalize, routeKey, OPENCODE_CATALOG_PROVIDERS, TASK_TYPE_RULES, TASK_TYPE_LABELS, MAX_EXPLICIT_EXECUTION_PACKAGES, REQUIREMENT_ACTION, HIGH_STAKES_WORK, SIMPLE_WORK, HIGH_STAKES_ACTION, SIMPLE_REQUEST_START, SYNTHESIS_WEIGHTS, ROUTING_BEAM_WIDTH, ROUTING_CANDIDATE_LIMIT;
|
|
695
989
|
var init_router = __esm({
|
|
696
990
|
".dsh-plugin/shared/router.mjs"() {
|
|
697
991
|
init_livebench();
|
|
@@ -755,7 +1049,7 @@ var init_router = __esm({
|
|
|
755
1049
|
["math", /数学|证明|定理|公式|方程|math|proof|theorem/i],
|
|
756
1050
|
["code", /代码|编程|工程|项目|架构|接口|api|debug|实现|部署|测试|code/i],
|
|
757
1051
|
["research", /研究|论文|文献|联网|检索|research|source|引用/i],
|
|
758
|
-
["summarization",
|
|
1052
|
+
["summarization", /总结|摘要|提炼|提取|关键词|分类|翻译|summar|classif|extract/i],
|
|
759
1053
|
["writing", /写作|润色|小说|文案|报告|writing|draft/i]
|
|
760
1054
|
]);
|
|
761
1055
|
TASK_TYPE_LABELS = Object.freeze({
|
|
@@ -767,125 +1061,17 @@ var init_router = __esm({
|
|
|
767
1061
|
writing: "\u5199\u4F5C\u4E0E\u8868\u8FBE"
|
|
768
1062
|
});
|
|
769
1063
|
MAX_EXPLICIT_EXECUTION_PACKAGES = 6;
|
|
770
|
-
REQUIREMENT_ACTION = /(
|
|
1064
|
+
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;
|
|
1065
|
+
HIGH_STAKES_WORK = /架构|安全|隐私|权限|并发|事务|迁移|生产|部署|发布|证明|定理|科研|论文|复杂|多步骤|跨系统|系统设计|architecture|security|migration|production|proof|theorem|research/i;
|
|
1066
|
+
SIMPLE_WORK = /翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread/i;
|
|
1067
|
+
HIGH_STAKES_ACTION = /设计|实现|修复|审计|测试|验证|证明|推导|部署|迁移|规划|研究|解决|推理|design|implement|fix|audit|verify|prove|derive|deploy|migrate|research|solve/i;
|
|
1068
|
+
SIMPLE_REQUEST_START = /^(?:请|帮我)?(?:翻译|摘要|总结|提取|分类|格式化|列出|改写|润色|拼写|校对|translate|summarize|extract|classify|format|proofread)/i;
|
|
771
1069
|
SYNTHESIS_WEIGHTS = Object.freeze({ quality: 0.58, cost: 0.08, latency: 0.04, specialty: 0.08, reasoning: 0.16, risk: 0.06 });
|
|
772
1070
|
ROUTING_BEAM_WIDTH = 256;
|
|
773
1071
|
ROUTING_CANDIDATE_LIMIT = 12;
|
|
774
1072
|
}
|
|
775
1073
|
});
|
|
776
1074
|
|
|
777
|
-
// .dsh-plugin/shared/official-tool-registry.mjs
|
|
778
|
-
function getOfficialTool(id) {
|
|
779
|
-
return TOOL_BY_ID.get(String(id ?? "").trim()) ?? null;
|
|
780
|
-
}
|
|
781
|
-
function toolForProvider(provider) {
|
|
782
|
-
const value = String(provider ?? "").trim().toLowerCase();
|
|
783
|
-
if (!value) return null;
|
|
784
|
-
for (const tool of OFFICIAL_TOOLS) {
|
|
785
|
-
if (tool.providerHints.some((hint) => value.includes(hint))) return tool;
|
|
786
|
-
}
|
|
787
|
-
return null;
|
|
788
|
-
}
|
|
789
|
-
function installCommandLine(tool) {
|
|
790
|
-
if (!tool || tool.unsupported) return null;
|
|
791
|
-
if (tool.manager === "signed-windows-installer") return "\u6253\u5F00\u5B98\u65B9\u7B7E\u540D\u5B89\u88C5\u5668\uFF08\u9009\u62E9\u5B89\u88C5\u76EE\u5F55\uFF09";
|
|
792
|
-
return tool.manager === "npm" ? `npm ${tool.installArgs.join(" ")}` : `${tool.manager} ${tool.installArgs.join(" ")}`;
|
|
793
|
-
}
|
|
794
|
-
var OFFICIAL_TOOLS, TOOL_BY_ID;
|
|
795
|
-
var init_official_tool_registry = __esm({
|
|
796
|
-
".dsh-plugin/shared/official-tool-registry.mjs"() {
|
|
797
|
-
OFFICIAL_TOOLS = Object.freeze([
|
|
798
|
-
Object.freeze({
|
|
799
|
-
id: "kimi-code",
|
|
800
|
-
label: "Kimi Code \xB7 Node",
|
|
801
|
-
vendor: "Moonshot AI",
|
|
802
|
-
purpose: "Kimi \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B kimi \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
|
|
803
|
-
package: "@moonshot-ai/kimi-code",
|
|
804
|
-
version: "2.1.1",
|
|
805
|
-
manager: "npm",
|
|
806
|
-
installArgs: ["install", "-g", "@moonshot-ai/kimi-code@2.1.1", "--registry=https://registry.npmjs.org/"],
|
|
807
|
-
probeExecutables: ["kimi"],
|
|
808
|
-
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",
|
|
809
|
-
providerHints: ["moonshot", "kimi"]
|
|
810
|
-
}),
|
|
811
|
-
Object.freeze({
|
|
812
|
-
id: "claude-code",
|
|
813
|
-
label: "Claude Code",
|
|
814
|
-
vendor: "Anthropic",
|
|
815
|
-
purpose: "Anthropic \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B claude \u547D\u4EE4\u3002",
|
|
816
|
-
package: "@anthropic-ai/claude-code",
|
|
817
|
-
version: "2.1.283",
|
|
818
|
-
manager: "npm",
|
|
819
|
-
installArgs: ["install", "-g", "@anthropic-ai/claude-code@2.1.283", "--registry=https://registry.npmjs.org/"],
|
|
820
|
-
probeExecutables: ["claude"],
|
|
821
|
-
providerHints: ["anthropic", "claude"]
|
|
822
|
-
}),
|
|
823
|
-
Object.freeze({
|
|
824
|
-
id: "codex",
|
|
825
|
-
label: "Codex CLI",
|
|
826
|
-
vendor: "OpenAI",
|
|
827
|
-
purpose: "OpenAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B codex \u547D\u4EE4\u3002",
|
|
828
|
-
package: "@openai/codex",
|
|
829
|
-
version: "0.157.1",
|
|
830
|
-
installArgs: ["install", "-g", "@openai/codex@0.157.1", "--registry=https://registry.npmjs.org/"],
|
|
831
|
-
manager: "npm",
|
|
832
|
-
probeExecutables: ["codex"],
|
|
833
|
-
probeNote: "Codex \u7248\u672C\u6A2A\u5E45\u7531\u9002\u914D\u5C42\u5BBD\u5339\u914D\uFF1B\u5B89\u88C5\u65F6\u56FA\u5B9A\u7248\u672C\u3002",
|
|
834
|
-
providerHints: ["openai", "gpt", "codex"]
|
|
835
|
-
}),
|
|
836
|
-
Object.freeze({
|
|
837
|
-
id: "minimax-code",
|
|
838
|
-
label: "MiniMax Code",
|
|
839
|
-
vendor: "MiniMax",
|
|
840
|
-
purpose: "MiniMax \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mcode \u547D\u4EE4\u3002",
|
|
841
|
-
package: "@minimax-ai/code",
|
|
842
|
-
version: "0.5.5",
|
|
843
|
-
manager: "npm",
|
|
844
|
-
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"],
|
|
845
|
-
probeExecutables: ["mcode"],
|
|
846
|
-
providerHints: ["minimax"]
|
|
847
|
-
}),
|
|
848
|
-
Object.freeze({
|
|
849
|
-
id: "mimo-code",
|
|
850
|
-
label: "MiMo Code",
|
|
851
|
-
vendor: "XiaoMi",
|
|
852
|
-
purpose: "\u5C0F\u7C73 MiMo \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B mimo \u547D\u4EE4\u3002",
|
|
853
|
-
package: "@mimo-ai/cli",
|
|
854
|
-
version: "0.1.15",
|
|
855
|
-
manager: "npm",
|
|
856
|
-
installArgs: ["install", "-g", "@mimo-ai/cli@0.1.15", "--registry=https://registry.npmjs.org/"],
|
|
857
|
-
probeExecutables: ["mimo"],
|
|
858
|
-
providerHints: ["mimo", "xiaomi"]
|
|
859
|
-
}),
|
|
860
|
-
Object.freeze({
|
|
861
|
-
id: "grok-build",
|
|
862
|
-
label: "Grok Build",
|
|
863
|
-
vendor: "xAI",
|
|
864
|
-
purpose: "xAI \u5B98\u65B9\u7F16\u7A0B CLI\uFF0C\u63D0\u4F9B grok \u547D\u4EE4\u4E0E ACP \u4F1A\u8BDD\u3002",
|
|
865
|
-
package: "@xai-official/grok",
|
|
866
|
-
version: "1.0.41",
|
|
867
|
-
manager: "npm",
|
|
868
|
-
installArgs: ["install", "-g", "@xai-official/grok@1.0.41", "--registry=https://registry.npmjs.org/"],
|
|
869
|
-
probeExecutables: ["grok"],
|
|
870
|
-
providerHints: ["xai", "grok"]
|
|
871
|
-
}),
|
|
872
|
-
Object.freeze({
|
|
873
|
-
id: "zcode",
|
|
874
|
-
label: "ZCode",
|
|
875
|
-
vendor: "Z.ai",
|
|
876
|
-
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",
|
|
877
|
-
version: "3.14.3",
|
|
878
|
-
manager: "signed-windows-installer",
|
|
879
|
-
installArgs: [],
|
|
880
|
-
probeExecutables: [],
|
|
881
|
-
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",
|
|
882
|
-
providerHints: ["zai", "z.ai", "zcode", "glm", "zhipu"]
|
|
883
|
-
})
|
|
884
|
-
]);
|
|
885
|
-
TOOL_BY_ID = new Map(OFFICIAL_TOOLS.map((tool) => [tool.id, tool]));
|
|
886
|
-
}
|
|
887
|
-
});
|
|
888
|
-
|
|
889
1075
|
// .dsh-plugin/shared/harness-plan.mjs
|
|
890
1076
|
function channelForProvider(provider, installedToolIds = [], runnableToolIds = []) {
|
|
891
1077
|
const tool = toolForProvider(provider);
|
|
@@ -909,7 +1095,16 @@ function annotate(channel) {
|
|
|
909
1095
|
channelDetail: channel.detail
|
|
910
1096
|
};
|
|
911
1097
|
}
|
|
912
|
-
function createPlanFromRoutes(task, availableRoutes, {
|
|
1098
|
+
function createPlanFromRoutes(task, availableRoutes, {
|
|
1099
|
+
mode = "single",
|
|
1100
|
+
budgetUsd = 0,
|
|
1101
|
+
installedToolIds = [],
|
|
1102
|
+
runnableToolIds = [],
|
|
1103
|
+
pricing = {},
|
|
1104
|
+
liveBench = null,
|
|
1105
|
+
cacheReadRatio = 0,
|
|
1106
|
+
cacheWriteRatio = 0
|
|
1107
|
+
} = {}) {
|
|
913
1108
|
const taskText = clean(task);
|
|
914
1109
|
if (!taskText) throw new Error("task must contain text");
|
|
915
1110
|
const selectedMode = mode === "team" ? "team" : "single";
|
|
@@ -922,12 +1117,15 @@ function createPlanFromRoutes(task, availableRoutes, { mode = "single", budgetUs
|
|
|
922
1117
|
return channelCache.get(key);
|
|
923
1118
|
};
|
|
924
1119
|
const needsImage = detectTaskTypes(taskText).includes("vision");
|
|
925
|
-
const eligibleRoutes = needsImage ? routes.filter((route) => !Array.isArray(route.inputModalities) || route.inputModalities.length === 0 || route.inputModalities.includes("image")) : routes;
|
|
926
1120
|
const plan = buildPlan({
|
|
927
1121
|
text: taskText,
|
|
928
|
-
available:
|
|
1122
|
+
available: routes,
|
|
929
1123
|
mode: selectedMode,
|
|
930
|
-
budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0)
|
|
1124
|
+
budgetUsd: Math.max(0, Number.isFinite(budgetUsd) ? budgetUsd : 0),
|
|
1125
|
+
pricing,
|
|
1126
|
+
liveBench,
|
|
1127
|
+
cacheReadRatio,
|
|
1128
|
+
cacheWriteRatio
|
|
931
1129
|
});
|
|
932
1130
|
const selectedChannel = plan.selected ? channelOf(plan.selected.provider) : null;
|
|
933
1131
|
return {
|
|
@@ -936,22 +1134,27 @@ function createPlanFromRoutes(task, availableRoutes, { mode = "single", budgetUs
|
|
|
936
1134
|
availableRoutes: routes,
|
|
937
1135
|
...selectedChannel ? annotate(selectedChannel) : {},
|
|
938
1136
|
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",
|
|
939
|
-
pricingNotice: "\u8D39\u7528\
|
|
940
|
-
|
|
1137
|
+
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",
|
|
1138
|
+
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",
|
|
1139
|
+
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,
|
|
941
1140
|
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",
|
|
942
1141
|
team: {
|
|
943
1142
|
requested: selectedMode === "team",
|
|
944
1143
|
recommended: selectedMode === "team" && plan.complexity.band === "complex" && plan.subtasks.length > 1,
|
|
945
1144
|
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",
|
|
946
|
-
workPackages: plan.subtasks.map((item) => ({
|
|
1145
|
+
workPackages: plan.subtasks.map((item, index) => ({
|
|
947
1146
|
id: item.id,
|
|
948
1147
|
name: item.name,
|
|
949
1148
|
...item.objective ? { objective: item.objective } : {},
|
|
950
1149
|
type: item.type,
|
|
951
1150
|
purpose: item.purpose,
|
|
1151
|
+
difficulty: item.difficulty,
|
|
1152
|
+
qualitySource: item.qualitySource,
|
|
1153
|
+
pricingSource: item.pricingSource,
|
|
952
1154
|
dependsOn: item.dependsOn,
|
|
953
1155
|
recommendedProvider: item.recommendedProvider,
|
|
954
1156
|
recommendedModel: item.recommended,
|
|
1157
|
+
estimatedCost: plan.costBreakdown[index]?.estimatedCost ?? null,
|
|
955
1158
|
...item.recommendedReasoningEffort ? { recommendedReasoningEffort: item.recommendedReasoningEffort } : {},
|
|
956
1159
|
...annotate(channelOf(item.recommendedProvider)),
|
|
957
1160
|
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"]
|
|
@@ -1008,13 +1211,17 @@ function routesFromModelCatalog(catalog) {
|
|
|
1008
1211
|
return routes;
|
|
1009
1212
|
}
|
|
1010
1213
|
function createWorkspacePlan(task, catalog, options = {}) {
|
|
1011
|
-
const routes =
|
|
1214
|
+
const routes = applyModelProfiles(
|
|
1215
|
+
routesFromModelCatalog(catalog),
|
|
1216
|
+
parseModelProfilesJson(options.modelProfilesJson ?? "[]")
|
|
1217
|
+
);
|
|
1012
1218
|
return createPlanFromRoutes(task, routes, options);
|
|
1013
1219
|
}
|
|
1014
1220
|
var clean2;
|
|
1015
1221
|
var init_catalog = __esm({
|
|
1016
1222
|
".dsh-plugin/client/catalog.mjs"() {
|
|
1017
1223
|
init_harness_plan();
|
|
1224
|
+
init_model_profiles();
|
|
1018
1225
|
clean2 = (value) => typeof value === "string" ? value.trim() : "";
|
|
1019
1226
|
}
|
|
1020
1227
|
});
|
|
@@ -1032,38 +1239,222 @@ __export(official_harness_exports, {
|
|
|
1032
1239
|
inject: () => inject
|
|
1033
1240
|
});
|
|
1034
1241
|
module.exports = __toCommonJS(official_harness_exports);
|
|
1035
|
-
var
|
|
1242
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
1243
|
+
init_model_profiles();
|
|
1036
1244
|
|
|
1037
1245
|
// .dsh-plugin/client/router-main.jsx
|
|
1038
|
-
var
|
|
1246
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
1039
1247
|
init_catalog();
|
|
1248
|
+
|
|
1249
|
+
// .dsh-plugin/client/model-profile-editor.jsx
|
|
1250
|
+
var import_react = __toESM(require("react"), 1);
|
|
1251
|
+
init_model_profiles();
|
|
1252
|
+
|
|
1253
|
+
// .dsh-plugin/client/model-profile-editor-state.mjs
|
|
1254
|
+
init_model_profiles();
|
|
1255
|
+
var PROFILE_SPECIALTY_HINT = "code, math, research, summarization, writing, vision, reasoning";
|
|
1256
|
+
var profileRouteKey = (route) => `${String(route?.provider ?? "")}\0${String(route?.model ?? "")}`;
|
|
1257
|
+
var field = (value) => String(value ?? "").trim();
|
|
1258
|
+
function nonnegativeField(value, label, max = 1e6) {
|
|
1259
|
+
const raw = field(value);
|
|
1260
|
+
if (!raw) return null;
|
|
1261
|
+
const parsed = Number(raw);
|
|
1262
|
+
if (!Number.isFinite(parsed) || parsed < 0 || parsed > max) throw new Error(`${label} \u5FC5\u987B\u662F 0 \u5230 ${max} \u7684\u6570\u5B57\u3002`);
|
|
1263
|
+
return parsed;
|
|
1264
|
+
}
|
|
1265
|
+
function profileDraft(profile) {
|
|
1266
|
+
return {
|
|
1267
|
+
quality: profile?.quality === void 0 ? "" : String(Number((profile.quality * 100).toFixed(4))),
|
|
1268
|
+
input: profile?.pricing?.input === void 0 ? "" : String(profile.pricing.input),
|
|
1269
|
+
output: profile?.pricing?.output === void 0 ? "" : String(profile.pricing.output),
|
|
1270
|
+
cacheRead: profile?.pricing?.cacheRead === void 0 ? "" : String(profile.pricing.cacheRead),
|
|
1271
|
+
cacheWrite: profile?.pricing?.cacheWrite === void 0 ? "" : String(profile.pricing.cacheWrite),
|
|
1272
|
+
specialties: Array.isArray(profile?.specialties) ? profile.specialties.join(", ") : "",
|
|
1273
|
+
cliModel: profile?.cliModel ?? ""
|
|
1274
|
+
};
|
|
1275
|
+
}
|
|
1276
|
+
function profileFromDraft(route, draft) {
|
|
1277
|
+
const provider = field(route?.provider);
|
|
1278
|
+
const model = field(route?.model);
|
|
1279
|
+
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");
|
|
1280
|
+
const profile = { provider, model };
|
|
1281
|
+
const quality = nonnegativeField(draft?.quality, "\u8D28\u91CF\u8BC4\u5206", 100);
|
|
1282
|
+
if (quality !== null) profile.quality = quality;
|
|
1283
|
+
const input = nonnegativeField(draft?.input, "\u8F93\u5165\u5355\u4EF7");
|
|
1284
|
+
const output = nonnegativeField(draft?.output, "\u8F93\u51FA\u5355\u4EF7");
|
|
1285
|
+
const cacheRead = nonnegativeField(draft?.cacheRead, "\u7F13\u5B58\u8BFB\u53D6\u5355\u4EF7");
|
|
1286
|
+
const cacheWrite = nonnegativeField(draft?.cacheWrite, "\u7F13\u5B58\u5199\u5165\u5355\u4EF7");
|
|
1287
|
+
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");
|
|
1288
|
+
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");
|
|
1289
|
+
if (input !== null) {
|
|
1290
|
+
profile.pricing = { input, output, currency: "USD" };
|
|
1291
|
+
if (cacheRead !== null) profile.pricing.cacheRead = cacheRead;
|
|
1292
|
+
if (cacheWrite !== null) profile.pricing.cacheWrite = cacheWrite;
|
|
1293
|
+
}
|
|
1294
|
+
const rawSpecialties = field(draft?.specialties);
|
|
1295
|
+
if (rawSpecialties) {
|
|
1296
|
+
const specialties = [...new Set(rawSpecialties.split(/[,,\s]+/u).filter(Boolean))];
|
|
1297
|
+
if (specialties.length > 16 || specialties.some((item) => !/^[a-z][a-z0-9-]{0,39}$/.test(item))) {
|
|
1298
|
+
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");
|
|
1299
|
+
}
|
|
1300
|
+
profile.specialties = specialties;
|
|
1301
|
+
}
|
|
1302
|
+
const cliModel = field(draft?.cliModel);
|
|
1303
|
+
if (cliModel) {
|
|
1304
|
+
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");
|
|
1305
|
+
profile.cliModel = cliModel;
|
|
1306
|
+
}
|
|
1307
|
+
return profile;
|
|
1308
|
+
}
|
|
1309
|
+
function updateProfileJson(rawJson, route, draft) {
|
|
1310
|
+
const profiles = parseModelProfilesJson(rawJson ?? "[]");
|
|
1311
|
+
const candidate = draft === null ? null : profileFromDraft(route, draft);
|
|
1312
|
+
const selected = profileRouteKey(route);
|
|
1313
|
+
const others = profiles.filter((profile) => profileRouteKey(profile) !== selected).map((profile) => ({
|
|
1314
|
+
...profile,
|
|
1315
|
+
...profile.quality === void 0 ? {} : { quality: Number((profile.quality * 100).toFixed(6)) }
|
|
1316
|
+
}));
|
|
1317
|
+
const next = candidate && Object.keys(candidate).length > 2 ? [...others, candidate] : others;
|
|
1318
|
+
const json = JSON.stringify(next, null, 2);
|
|
1319
|
+
parseModelProfilesJson(json);
|
|
1320
|
+
return json;
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1323
|
+
// .dsh-plugin/client/model-profile-editor.jsx
|
|
1324
|
+
var messageOf = (error) => typeof error?.message === "string" ? error.message : "\u6A21\u578B\u914D\u7F6E\u4FDD\u5B58\u5931\u8D25\u3002";
|
|
1325
|
+
function ModelProfileEditor({ routes, settingsScope, onSaved }) {
|
|
1326
|
+
const [snapshot, setSnapshot] = import_react.default.useState(() => settingsScope.getSnapshot());
|
|
1327
|
+
const [selected, setSelected] = import_react.default.useState("");
|
|
1328
|
+
const [drafts, setDrafts] = import_react.default.useState({});
|
|
1329
|
+
const [saving, setSaving] = import_react.default.useState(false);
|
|
1330
|
+
const [notice, setNotice] = import_react.default.useState(null);
|
|
1331
|
+
import_react.default.useEffect(() => settingsScope.subscribe(() => setSnapshot(settingsScope.getSnapshot())), [settingsScope]);
|
|
1332
|
+
const route = routes.find((item) => profileRouteKey(item) === selected) ?? routes[0];
|
|
1333
|
+
const routeKey2 = route ? profileRouteKey(route) : "";
|
|
1334
|
+
const rawJson = snapshot.value?.modelProfilesJson ?? "[]";
|
|
1335
|
+
let profiles = [];
|
|
1336
|
+
let loadError = "";
|
|
1337
|
+
try {
|
|
1338
|
+
profiles = parseModelProfilesJson(rawJson);
|
|
1339
|
+
} catch (error) {
|
|
1340
|
+
loadError = messageOf(error);
|
|
1341
|
+
}
|
|
1342
|
+
const saved = profiles.find((item) => profileRouteKey(item) === routeKey2);
|
|
1343
|
+
const pending = drafts[routeKey2];
|
|
1344
|
+
const draft = pending?.fields ?? profileDraft(saved);
|
|
1345
|
+
const writable = snapshot.status === "ready" && snapshot.writable === true && !saving && !loadError;
|
|
1346
|
+
const unmatched = profiles.filter((item) => !routes.some((route2) => profileRouteKey(route2) === profileRouteKey(item))).length;
|
|
1347
|
+
const edit = (name, value) => {
|
|
1348
|
+
if (!route || !writable) return;
|
|
1349
|
+
setDrafts((previous) => {
|
|
1350
|
+
const previousEntry = previous[routeKey2] ?? { fields: profileDraft(saved), baseRevision: snapshot.revision };
|
|
1351
|
+
return { ...previous, [routeKey2]: { ...previousEntry, fields: { ...previousEntry.fields, [name]: value } } };
|
|
1352
|
+
});
|
|
1353
|
+
setNotice(null);
|
|
1354
|
+
};
|
|
1355
|
+
const write = async (remove) => {
|
|
1356
|
+
if (!route || !writable) return;
|
|
1357
|
+
const revision = pending?.baseRevision ?? snapshot.revision;
|
|
1358
|
+
if (revision !== snapshot.revision) {
|
|
1359
|
+
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" });
|
|
1360
|
+
return;
|
|
1361
|
+
}
|
|
1362
|
+
let nextJson;
|
|
1363
|
+
try {
|
|
1364
|
+
nextJson = updateProfileJson(rawJson, route, remove ? null : draft);
|
|
1365
|
+
} catch (error) {
|
|
1366
|
+
setNotice({ tone: "error", text: messageOf(error) });
|
|
1367
|
+
return;
|
|
1368
|
+
}
|
|
1369
|
+
setSaving(true);
|
|
1370
|
+
setNotice(null);
|
|
1371
|
+
try {
|
|
1372
|
+
const accepted = await settingsScope.mutate([{ op: "set", path: ["modelProfilesJson"], value: nextJson }], revision);
|
|
1373
|
+
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");
|
|
1374
|
+
setDrafts((previous) => {
|
|
1375
|
+
const next = { ...previous };
|
|
1376
|
+
delete next[routeKey2];
|
|
1377
|
+
return next;
|
|
1378
|
+
});
|
|
1379
|
+
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" });
|
|
1380
|
+
onSaved?.();
|
|
1381
|
+
} catch (error) {
|
|
1382
|
+
setNotice({ tone: "error", text: messageOf(error) });
|
|
1383
|
+
} finally {
|
|
1384
|
+
setSaving(false);
|
|
1385
|
+
}
|
|
1386
|
+
};
|
|
1387
|
+
const reload = () => {
|
|
1388
|
+
setDrafts((previous) => {
|
|
1389
|
+
const next = { ...previous };
|
|
1390
|
+
delete next[routeKey2];
|
|
1391
|
+
return next;
|
|
1392
|
+
});
|
|
1393
|
+
setNotice(null);
|
|
1394
|
+
};
|
|
1395
|
+
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) => {
|
|
1396
|
+
setSelected(event.target.value);
|
|
1397
|
+
setNotice(null);
|
|
1398
|
+
} }, 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: () => {
|
|
1399
|
+
void write(false);
|
|
1400
|
+
} }, 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: () => {
|
|
1401
|
+
void write(true);
|
|
1402
|
+
} }, "\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"))));
|
|
1403
|
+
}
|
|
1404
|
+
|
|
1405
|
+
// .dsh-plugin/client/tool-install-state.mjs
|
|
1406
|
+
function stableVersionOrder(left, right) {
|
|
1407
|
+
const parse = (value) => /^([0-9]+)\.([0-9]+)\.([0-9]+)$/.exec(String(value ?? ""));
|
|
1408
|
+
const current = parse(left);
|
|
1409
|
+
const target = parse(right);
|
|
1410
|
+
if (!current || !target) return null;
|
|
1411
|
+
for (let index = 1; index <= 3; index += 1) {
|
|
1412
|
+
const delta = Number(current[index]) - Number(target[index]);
|
|
1413
|
+
if (delta) return Math.sign(delta);
|
|
1414
|
+
}
|
|
1415
|
+
return 0;
|
|
1416
|
+
}
|
|
1417
|
+
function toolInstallAction({ tool, probe, readiness, job, probeStatus }) {
|
|
1418
|
+
const running = job?.status === "running";
|
|
1419
|
+
const versionOrder = probe?.installed ? stableVersionOrder(probe.version, tool.version) : null;
|
|
1420
|
+
const currentAndReady = versionOrder === 0 && readiness?.ready === true;
|
|
1421
|
+
const repairable = versionOrder === 0 && readiness?.ready === false;
|
|
1422
|
+
const newerOrUncertain = probe?.installed && (versionOrder === null || versionOrder > 0);
|
|
1423
|
+
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";
|
|
1424
|
+
return {
|
|
1425
|
+
label,
|
|
1426
|
+
disabled: probeStatus !== "ready" || running || currentAndReady || newerOrUncertain
|
|
1427
|
+
};
|
|
1428
|
+
}
|
|
1429
|
+
|
|
1430
|
+
// .dsh-plugin/client/router-main.jsx
|
|
1040
1431
|
init_official_tool_registry();
|
|
1041
1432
|
|
|
1042
1433
|
// .dsh-plugin/client/router-main.css
|
|
1043
|
-
var router_main_default = ".mr-workspace {\n --mr-card: rgba(255, 255, 255, .94);\n --mr-ink: #182232;\n --mr-muted: #526174;\n --mr-line: rgba(38, 55, 75, .13);\n --mr-accent: #315cc8;\n --mr-soft: #edf3ff;\n box-sizing: border-box;\n width: 100%;\n height: 100%;\n min-height: 0;\n overflow-y: auto;\n background: rgba(246, 249, 253, .9);\n backdrop-filter: blur(18px);\n color: var(--mr-ink);\n font-family: inherit;\n}\n.mr-workspace *, .mr-workspace *::before, .mr-workspace *::after { box-sizing: border-box; }\n.mr-shell { width: min(1160px, 100%); margin: 0 auto; padding: 36px 32px 64px; }\n.mr-header { display: flex; flex-wrap: wrap; gap: 20px; align-items: end; justify-content: space-between; margin-bottom: 24px; }\n.mr-eyebrow { margin: 0 0 8px; color: var(--mr-accent); font-size: 11px; font-weight: 750; letter-spacing: .14em; text-transform: uppercase; }\n.mr-title { margin: 0; font-size: clamp(25px, 3vw, 34px); letter-spacing: -.035em; line-height: 1.16; }\n.mr-subtitle { margin: 10px 0 0; color: var(--mr-muted); font-size: 14px; line-height: 1.65; }\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; }\n.mr-status-dot { width: 7px; height: 7px; border-radius: 50%; background: #2cba83; }\n.mr-status-dot.loading { background: #e9a640; }\n.mr-status-dot.error { background: #d95360; }\n.mr-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(280px, .85fr); gap: 18px; align-items: start; }\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); }\n.mr-card-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 20px 22px 0; }\n.mr-card-title { margin: 0; font-size: 16px; font-weight: 700; }\n.mr-card-copy { margin: 5px 0 0; color: var(--mr-muted); font-size: 12px; line-height: 1.55; }\n.mr-card-body { padding: 18px 22px 22px; }\n.mr-label { display: block; margin: 0 0 8px; font-size: 12px; font-weight: 700; }\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; }\n.mr-textarea { min-height: 154px; padding: 13px 14px; resize: vertical; line-height: 1.6; font-size: 14px; }\n.mr-input { min-height: 38px; padding: 8px 11px; font-size: 13px; }\n.mr-textarea:focus, .mr-input:focus { border-color: var(--mr-accent); box-shadow: 0 0 0 3px rgba(49, 92, 200, .13); }\n.mr-controls { display: flex; flex-wrap: wrap; align-items: end; justify-content: space-between; gap: 14px; margin-top: 17px; }\n.mr-control-group { display: flex; flex-direction: column; gap: 7px; }\n.mr-control-label { color: var(--mr-muted); font-size: 11px; font-weight: 650; }\n.mr-segment { display: inline-flex; padding: 3px; border-radius: 10px; background: #edf1f7; }\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; }\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; }\n.mr-budget { width: 140px; }\n.mr-actions { display: flex; flex-wrap: wrap; gap: 9px; align-items: center; margin-top: 18px; }\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; }\n.mr-button:hover:not(:disabled) { filter: brightness(.94); }\n.mr-button:disabled { cursor: not-allowed; opacity: .5; }\n.mr-button-secondary { border-color: var(--mr-line); background: #fff; color: var(--mr-ink); }\n.mr-caption { color: var(--mr-muted); font-size: 11px; line-height: 1.5; }\n.mr-search { margin-top: 14px; }\n.mr-list { display: grid; gap: 8px; margin-top: 13px; max-height: 395px; overflow-y: auto; }\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; }\n.mr-route-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; font-weight: 700; }\n.mr-route-provider { margin-top: 2px; color: var(--mr-muted); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\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; }\n.mr-empty, .mr-error { padding: 14px; border-radius: 10px; background: #f4f6fa; color: var(--mr-muted); font-size: 12px; line-height: 1.6; }\n.mr-error { background: #fff0f1; color: #a52d3c; }\n.mr-results { margin-top: 18px; }\n.mr-result-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 16px; }\n.mr-metric { padding: 13px; border-radius: 12px; background: #f5f8fd; }\n.mr-metric-label { color: var(--mr-muted); font-size: 10px; }\n.mr-metric-value { margin-top: 5px; font-size: 15px; font-weight: 750; word-break: break-word; }\n.mr-section-title { margin: 20px 0 10px; font-size: 13px; font-weight: 750; }\n.mr-package { padding: 13px 14px; border: 1px solid var(--mr-line); border-radius: 11px; background: #fafcff; }\n.mr-package + .mr-package { margin-top: 8px; }\n.mr-package-top { display: flex; flex-wrap: wrap; justify-content: space-between; gap: 8px; align-items: center; }\n.mr-package-name { font-size: 12px; font-weight: 750; }\n.mr-package-route { color: var(--mr-accent); font-size: 11px; font-weight: 700; }\n.mr-package-copy { margin: 7px 0 0; color: var(--mr-muted); font-size: 11px; line-height: 1.6; }\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; }\n.mr-notice code { font-family: ui-monospace, SFMono-Regular, Consolas, monospace; font-size: 11px; }\n@media (max-width: 850px) { .mr-grid { grid-template-columns: 1fr; } .mr-shell { padding: 24px 18px 48px; } }\n@media (prefers-color-scheme: dark) {\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); }\n .mr-textarea, .mr-input, .mr-segment button[aria-pressed='true'], .mr-button-secondary { background: #273245; color: var(--mr-ink); }\n .mr-segment, .mr-empty, .mr-metric { background: #222d3e; }\n .mr-route, .mr-package { background: #222d3e; }\n .mr-notice { background: #243454; color: #c9d6fa; }\n .mr-error { background: #4a2730; color: #ffd4da; }\n}\n\n/* \u5B98\u65B9\u5DE5\u5177\u5361\u7247 */\n.mr-tools { display: grid; gap: 9px; }\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); }\n.mr-tool-info { min-width: 0; flex: 1; }\n.mr-tool-status { display: flex; align-items: center; gap: 6px; margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\n.mr-tool-dot { width: 7px; height: 7px; flex: none; border-radius: 50%; background: #8d9aaa; }\n.mr-tool-dot.installed { background: #2cba83; }\n.mr-tool-dot.running { background: #e9a640; }\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; }\n.mr-tool-button { flex-shrink: 0; }\n.mr-tool-actions { display: flex; align-items: center; justify-content: flex-end; gap: 8px; flex-wrap: wrap; }\n.mr-tool-detail { margin: 7px 0 0; }\n.mr-tool-error { margin: 8px 0 0; padding: 9px 11px; }\n.mr-tool-log { margin-top: 7px; color: var(--mr-muted); font-size: 11px; }\n.mr-tool-log summary { cursor: pointer; }\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; }\n.mr-channel-line { display: flex; align-items: center; gap: 8px; margin: 8px 0; flex-wrap: wrap; }\n.mr-pill-channel-ok { border-color: var(--dsw-alias-success, #2f9e63); color: var(--dsw-alias-success, #2f9e63); }\n@media (max-width: 560px) { .mr-tool { align-items: stretch; flex-direction: column; gap: 10px; } .mr-tool-button { align-self: flex-start; } }\n";
|
|
1434
|
+
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";
|
|
1044
1435
|
|
|
1045
1436
|
// .dsh-plugin/client/router-main.jsx
|
|
1046
|
-
var money = (value) => `$${Number(value
|
|
1437
|
+
var money = (value) => value === null || value === void 0 ? "\u4EF7\u683C\u5F85\u914D\u7F6E" : `$${Number(value).toFixed(4)}`;
|
|
1047
1438
|
var text = (value) => typeof value === "string" ? value.trim() : "";
|
|
1048
1439
|
function RouterPanelIcon({ size = 20, active = false }) {
|
|
1049
|
-
return /* @__PURE__ */
|
|
1440
|
+
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" }));
|
|
1050
1441
|
}
|
|
1051
1442
|
function RouteList({ routes, query }) {
|
|
1052
1443
|
const filtered = routes.filter((route) => {
|
|
1053
1444
|
const haystack = `${route.providerName} ${route.provider} ${route.name} ${route.model}`.toLowerCase();
|
|
1054
1445
|
return haystack.includes(query.toLowerCase());
|
|
1055
1446
|
});
|
|
1056
|
-
if (filtered.length === 0) return /* @__PURE__ */
|
|
1057
|
-
return /* @__PURE__ */
|
|
1447
|
+
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");
|
|
1448
|
+
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"))));
|
|
1058
1449
|
}
|
|
1059
1450
|
function ChannelBadge({ item }) {
|
|
1060
1451
|
if (!item?.executionChannel) return null;
|
|
1061
1452
|
const official = item.executionChannel === "official-cli";
|
|
1062
|
-
return /* @__PURE__ */
|
|
1453
|
+
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");
|
|
1063
1454
|
}
|
|
1064
1455
|
function PlanResults({ plan }) {
|
|
1065
1456
|
const selected = plan.selected;
|
|
1066
|
-
return /* @__PURE__ */
|
|
1457
|
+
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 || "")));
|
|
1067
1458
|
}
|
|
1068
1459
|
var remoteError = (response, fallback) => text(response?.error?.message) || text(response?.value?.error) || fallback;
|
|
1069
1460
|
function probeLabel(probe) {
|
|
@@ -1074,25 +1465,14 @@ function probeLabel(probe) {
|
|
|
1074
1465
|
if (probe.status === "probe-failed") return "\u68C0\u6D4B\u5931\u8D25";
|
|
1075
1466
|
return probe.detail || "\u672A\u5B89\u88C5";
|
|
1076
1467
|
}
|
|
1077
|
-
function stableVersionOrder(left, right) {
|
|
1078
|
-
const parse = (value) => /^([0-9]+)\.([0-9]+)\.([0-9]+)$/.exec(String(value ?? ""));
|
|
1079
|
-
const current = parse(left);
|
|
1080
|
-
const target = parse(right);
|
|
1081
|
-
if (!current || !target) return null;
|
|
1082
|
-
for (let index = 1; index <= 3; index += 1) {
|
|
1083
|
-
const delta = Number(current[index]) - Number(target[index]);
|
|
1084
|
-
if (delta) return Math.sign(delta);
|
|
1085
|
-
}
|
|
1086
|
-
return 0;
|
|
1087
|
-
}
|
|
1088
1468
|
function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus, onProbes }) {
|
|
1089
|
-
const [probeState, setProbeState] =
|
|
1090
|
-
const [jobs, setJobs] =
|
|
1091
|
-
const [rowErrors, setRowErrors] =
|
|
1092
|
-
const mounted =
|
|
1093
|
-
const request =
|
|
1094
|
-
const submitting =
|
|
1095
|
-
const polling =
|
|
1469
|
+
const [probeState, setProbeState] = import_react2.default.useState({ status: "loading", probes: [], capabilities: [], readiness: [], error: "" });
|
|
1470
|
+
const [jobs, setJobs] = import_react2.default.useState({});
|
|
1471
|
+
const [rowErrors, setRowErrors] = import_react2.default.useState({});
|
|
1472
|
+
const mounted = import_react2.default.useRef(false);
|
|
1473
|
+
const request = import_react2.default.useRef(0);
|
|
1474
|
+
const submitting = import_react2.default.useRef(/* @__PURE__ */ new Set());
|
|
1475
|
+
const polling = import_react2.default.useRef(/* @__PURE__ */ new Set());
|
|
1096
1476
|
const refresh = async () => {
|
|
1097
1477
|
const current = ++request.current;
|
|
1098
1478
|
setProbeState((previous) => ({ ...previous, status: "loading", error: "" }));
|
|
@@ -1112,7 +1492,7 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOffic
|
|
|
1112
1492
|
onProbes({ probes: [], capabilities: [], readiness: [] });
|
|
1113
1493
|
}
|
|
1114
1494
|
};
|
|
1115
|
-
|
|
1495
|
+
import_react2.default.useEffect(() => {
|
|
1116
1496
|
mounted.current = true;
|
|
1117
1497
|
void refresh();
|
|
1118
1498
|
if (typeof officialToolInstallStatus === "function") {
|
|
@@ -1132,109 +1512,106 @@ function OfficialToolsCard({ listOfficialTools, installOfficialTool, cancelOffic
|
|
|
1132
1512
|
request.current += 1;
|
|
1133
1513
|
};
|
|
1134
1514
|
}, []);
|
|
1135
|
-
|
|
1515
|
+
import_react2.default.useEffect(() => {
|
|
1136
1516
|
const active = Object.values(jobs).filter((job) => job?.status === "running").map((job) => job.tool);
|
|
1137
1517
|
if (active.length === 0 || typeof officialToolInstallStatus !== "function") return void 0;
|
|
1138
1518
|
let listening = true;
|
|
1139
|
-
const poll = async (
|
|
1140
|
-
if (polling.current.has(
|
|
1141
|
-
polling.current.add(
|
|
1519
|
+
const poll = async (id2) => {
|
|
1520
|
+
if (polling.current.has(id2)) return;
|
|
1521
|
+
polling.current.add(id2);
|
|
1142
1522
|
try {
|
|
1143
|
-
const response = await officialToolInstallStatus(
|
|
1523
|
+
const response = await officialToolInstallStatus(id2);
|
|
1144
1524
|
if (!listening || !mounted.current) return;
|
|
1145
1525
|
if (!response?.ok) throw new Error(remoteError(response, "\u65E0\u6CD5\u83B7\u53D6\u5B89\u88C5\u8FDB\u5EA6\u3002"));
|
|
1146
1526
|
const job = response.value?.job;
|
|
1147
1527
|
if (!job) throw new Error("\u5B89\u88C5\u4EFB\u52A1\u72B6\u6001\u6682\u4E0D\u53EF\u7528\u3002");
|
|
1148
|
-
setJobs((previous) => ({ ...previous, [
|
|
1149
|
-
setRowErrors((previous) => ({ ...previous, [
|
|
1528
|
+
setJobs((previous) => ({ ...previous, [id2]: job }));
|
|
1529
|
+
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
1150
1530
|
if (job.status !== "running") void refresh();
|
|
1151
1531
|
} catch (error) {
|
|
1152
|
-
if (listening && mounted.current) setRowErrors((previous) => ({ ...previous, [
|
|
1532
|
+
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" }));
|
|
1153
1533
|
} finally {
|
|
1154
|
-
polling.current.delete(
|
|
1534
|
+
polling.current.delete(id2);
|
|
1155
1535
|
}
|
|
1156
1536
|
};
|
|
1157
|
-
const timer = setInterval(() => active.forEach((
|
|
1158
|
-
void poll(
|
|
1537
|
+
const timer = setInterval(() => active.forEach((id2) => {
|
|
1538
|
+
void poll(id2);
|
|
1159
1539
|
}), 1500);
|
|
1160
1540
|
return () => {
|
|
1161
1541
|
listening = false;
|
|
1162
1542
|
clearInterval(timer);
|
|
1163
1543
|
};
|
|
1164
1544
|
}, [jobs, officialToolInstallStatus]);
|
|
1165
|
-
const install = async (
|
|
1166
|
-
const tool = OFFICIAL_TOOLS.find((item) => item.id ===
|
|
1167
|
-
if (!tool || tool.unsupported || submitting.current.has(
|
|
1168
|
-
submitting.current.add(
|
|
1169
|
-
setRowErrors((previous) => ({ ...previous, [
|
|
1170
|
-
setJobs((previous) => ({ ...previous, [
|
|
1545
|
+
const install = async (id2) => {
|
|
1546
|
+
const tool = OFFICIAL_TOOLS.find((item) => item.id === id2);
|
|
1547
|
+
if (!tool || tool.unsupported || submitting.current.has(id2) || jobs[id2]?.status === "running") return;
|
|
1548
|
+
submitting.current.add(id2);
|
|
1549
|
+
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
1550
|
+
setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "running", outputTail: [] } }));
|
|
1171
1551
|
try {
|
|
1172
1552
|
if (typeof installOfficialTool !== "function") throw new Error("\u5B98\u65B9\u5DE5\u5177\u5B89\u88C5\u6865\u5C1A\u672A\u52A0\u8F7D\u3002");
|
|
1173
|
-
const response = await installOfficialTool(
|
|
1553
|
+
const response = await installOfficialTool(id2);
|
|
1174
1554
|
if (!mounted.current) return;
|
|
1175
1555
|
if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u5B89\u88C5\u4EFB\u52A1\u672A\u88AB\u63A5\u53D7\u3002"));
|
|
1176
|
-
setJobs((previous) => ({ ...previous, [
|
|
1556
|
+
setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
|
|
1177
1557
|
} catch (error) {
|
|
1178
1558
|
if (mounted.current) {
|
|
1179
|
-
setJobs((previous) => ({ ...previous, [
|
|
1180
|
-
setRowErrors((previous) => ({ ...previous, [
|
|
1559
|
+
setJobs((previous) => ({ ...previous, [id2]: { tool: id2, status: "failed", error: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" } }));
|
|
1560
|
+
setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u5B89\u88C5\u542F\u52A8\u5931\u8D25\u3002" }));
|
|
1181
1561
|
}
|
|
1182
1562
|
} finally {
|
|
1183
|
-
submitting.current.delete(
|
|
1563
|
+
submitting.current.delete(id2);
|
|
1184
1564
|
}
|
|
1185
1565
|
};
|
|
1186
|
-
const cancel = async (
|
|
1187
|
-
if (typeof cancelOfficialToolInstall !== "function" || jobs[
|
|
1188
|
-
setRowErrors((previous) => ({ ...previous, [
|
|
1566
|
+
const cancel = async (id2) => {
|
|
1567
|
+
if (typeof cancelOfficialToolInstall !== "function" || jobs[id2]?.status !== "running" || jobs[id2]?.cancelRequested) return;
|
|
1568
|
+
setRowErrors((previous) => ({ ...previous, [id2]: "" }));
|
|
1189
1569
|
try {
|
|
1190
|
-
const response = await cancelOfficialToolInstall(
|
|
1570
|
+
const response = await cancelOfficialToolInstall(id2);
|
|
1191
1571
|
if (!mounted.current) return;
|
|
1192
1572
|
if (!response?.ok || !response.value?.accepted || !response.value?.job) throw new Error(remoteError(response, "\u53D6\u6D88\u8BF7\u6C42\u672A\u88AB\u63A5\u53D7\u3002"));
|
|
1193
|
-
setJobs((previous) => ({ ...previous, [
|
|
1573
|
+
setJobs((previous) => ({ ...previous, [id2]: response.value.job }));
|
|
1194
1574
|
if (response.value.job.status !== "running") void refresh();
|
|
1195
1575
|
} catch (error) {
|
|
1196
|
-
if (mounted.current) setRowErrors((previous) => ({ ...previous, [
|
|
1576
|
+
if (mounted.current) setRowErrors((previous) => ({ ...previous, [id2]: text(error?.message) || "\u65E0\u6CD5\u53D6\u6D88\u5B89\u88C5\u3002" }));
|
|
1197
1577
|
}
|
|
1198
1578
|
};
|
|
1199
1579
|
const byId = Object.fromEntries(probeState.probes.map((probe) => [probe.id, probe]));
|
|
1200
1580
|
const capabilitiesById = Object.fromEntries(probeState.capabilities.map((item) => [item.id, item]));
|
|
1201
1581
|
const readinessById = Object.fromEntries(probeState.readiness.map((item) => [item.id, item]));
|
|
1202
|
-
return /* @__PURE__ */
|
|
1582
|
+
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: () => {
|
|
1203
1583
|
void refresh();
|
|
1204
|
-
} }, "\u91CD\u65B0\u68C0\u6D4B")), /* @__PURE__ */
|
|
1584
|
+
} }, "\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) => {
|
|
1205
1585
|
const command = installCommandLine(tool);
|
|
1206
1586
|
const probe = byId[tool.id];
|
|
1207
1587
|
const capability = capabilitiesById[tool.id];
|
|
1208
1588
|
const readiness = readinessById[tool.id];
|
|
1209
1589
|
const job = jobs[tool.id];
|
|
1210
1590
|
const running = job?.status === "running";
|
|
1211
|
-
const
|
|
1212
|
-
const current = versionOrder === 0;
|
|
1213
|
-
const newerOrUncertain = probe?.installed && (versionOrder === null || versionOrder > 0);
|
|
1591
|
+
const action = toolInstallAction({ tool, probe, readiness, job, probeStatus: probeState.status });
|
|
1214
1592
|
const verified = job?.status === "succeeded" && job.postInstallProbe?.installed === true;
|
|
1215
1593
|
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);
|
|
1216
|
-
|
|
1217
|
-
return /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-tool", role: "listitem", key: tool.id }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-tool-info" }, /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-route-name", title: tool.purpose }, tool.label), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-route-provider" }, tool.vendor, " \xB7 ", tool.id), /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-tool-status", role: "status" }, /* @__PURE__ */ import_react.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_react.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_react.default.createElement("code", { className: "mr-tool-command" }, command) : /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption", style: { margin: "6px 0 0" } }, tool.unsupportedReason), probe?.detail && /* @__PURE__ */ import_react.default.createElement("p", { className: "mr-caption mr-tool-detail" }, probe.detail), (rowErrors[tool.id] || job?.error) && /* @__PURE__ */ import_react.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_react.default.createElement("details", { className: "mr-tool-log" }, /* @__PURE__ */ import_react.default.createElement("summary", null, "\u5B89\u88C5\u65E5\u5FD7"), /* @__PURE__ */ import_react.default.createElement("pre", null, job.outputTail.slice(-6).join("\n")))), command && /* @__PURE__ */ import_react.default.createElement("div", { className: "mr-tool-actions" }, /* @__PURE__ */ import_react.default.createElement("button", { className: "mr-button mr-tool-button", type: "button", disabled: probeState.status !== "ready" || running || current || newerOrUncertain, onClick: () => {
|
|
1594
|
+
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: () => {
|
|
1218
1595
|
void install(tool.id);
|
|
1219
|
-
} },
|
|
1596
|
+
} }, action.label), running && /* @__PURE__ */ import_react2.default.createElement("button", { className: "mr-button mr-button-secondary mr-tool-button", type: "button", disabled: job.cancelRequested, onClick: () => {
|
|
1220
1597
|
void cancel(tool.id);
|
|
1221
1598
|
} }, job.cancelRequested ? "\u6B63\u5728\u53D6\u6D88\u2026" : "\u53D6\u6D88\u5B89\u88C5")));
|
|
1222
|
-
})), /* @__PURE__ */
|
|
1599
|
+
})), /* @__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")));
|
|
1223
1600
|
}
|
|
1224
1601
|
function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, installOfficialTool, cancelOfficialToolInstall, officialToolInstallStatus }) {
|
|
1225
|
-
const [catalogState, setCatalogState] =
|
|
1226
|
-
const [task, setTask] =
|
|
1227
|
-
const [mode, setMode] =
|
|
1228
|
-
const [budget, setBudget] =
|
|
1229
|
-
const [query, setQuery] =
|
|
1230
|
-
const [plan, setPlan] =
|
|
1231
|
-
const [planError, setPlanError] =
|
|
1232
|
-
const [toolProbes, setToolProbes] =
|
|
1233
|
-
const budgetEdited =
|
|
1234
|
-
const budgetValue =
|
|
1235
|
-
const mounted =
|
|
1236
|
-
const catalogRequest =
|
|
1237
|
-
|
|
1602
|
+
const [catalogState, setCatalogState] = import_react2.default.useState({ status: "loading", catalog: null, error: "" });
|
|
1603
|
+
const [task, setTask] = import_react2.default.useState("");
|
|
1604
|
+
const [mode, setMode] = import_react2.default.useState("single");
|
|
1605
|
+
const [budget, setBudget] = import_react2.default.useState(() => String(settingsScope.getSnapshot().value?.budgetUsd ?? 0));
|
|
1606
|
+
const [query, setQuery] = import_react2.default.useState("");
|
|
1607
|
+
const [plan, setPlan] = import_react2.default.useState(null);
|
|
1608
|
+
const [planError, setPlanError] = import_react2.default.useState("");
|
|
1609
|
+
const [toolProbes, setToolProbes] = import_react2.default.useState(null);
|
|
1610
|
+
const budgetEdited = import_react2.default.useRef(false);
|
|
1611
|
+
const budgetValue = import_react2.default.useRef(budget);
|
|
1612
|
+
const mounted = import_react2.default.useRef(false);
|
|
1613
|
+
const catalogRequest = import_react2.default.useRef(0);
|
|
1614
|
+
import_react2.default.useEffect(() => {
|
|
1238
1615
|
const syncBudget = () => {
|
|
1239
1616
|
if (budgetEdited.current) return;
|
|
1240
1617
|
const next = String(settingsScope.getSnapshot().value?.budgetUsd ?? 0);
|
|
@@ -1248,7 +1625,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
1248
1625
|
syncBudget();
|
|
1249
1626
|
return settingsScope.subscribe(syncBudget);
|
|
1250
1627
|
}, [settingsScope]);
|
|
1251
|
-
|
|
1628
|
+
import_react2.default.useEffect(() => {
|
|
1252
1629
|
mounted.current = true;
|
|
1253
1630
|
const request = ++catalogRequest.current;
|
|
1254
1631
|
Promise.resolve().then(loadCatalog).then((response) => {
|
|
@@ -1282,7 +1659,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
1282
1659
|
setPlan(null);
|
|
1283
1660
|
setPlanError("");
|
|
1284
1661
|
};
|
|
1285
|
-
const handleToolProbes =
|
|
1662
|
+
const handleToolProbes = import_react2.default.useCallback((snapshot) => {
|
|
1286
1663
|
setToolProbes(snapshot);
|
|
1287
1664
|
setPlan(null);
|
|
1288
1665
|
setPlanError("");
|
|
@@ -1297,6 +1674,7 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
1297
1674
|
setPlan(createWorkspacePlan(task, catalogState.catalog, {
|
|
1298
1675
|
mode,
|
|
1299
1676
|
budgetUsd: parsedBudget,
|
|
1677
|
+
modelProfilesJson: settingsScope.getSnapshot().value?.modelProfilesJson ?? "[]",
|
|
1300
1678
|
installedToolIds: (toolProbes?.probes ?? []).filter((probe) => probe.installed).map((probe) => probe.id),
|
|
1301
1679
|
runnableToolIds: (toolProbes?.readiness ?? []).filter((item) => item.ready).map((item) => item.id)
|
|
1302
1680
|
}));
|
|
@@ -1305,25 +1683,25 @@ function RouterMainPage({ loadCatalog, settingsScope, listOfficialTools, install
|
|
|
1305
1683
|
setPlanError(text(error?.message) || "\u65E0\u6CD5\u751F\u6210\u8DEF\u7531\u5EFA\u8BAE\u3002");
|
|
1306
1684
|
}
|
|
1307
1685
|
};
|
|
1308
|
-
return /* @__PURE__ */
|
|
1686
|
+
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) => {
|
|
1309
1687
|
setTask(event.target.value);
|
|
1310
1688
|
invalidatePlan();
|
|
1311
|
-
}, 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__ */
|
|
1689
|
+
}, 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: () => {
|
|
1312
1690
|
setMode("single");
|
|
1313
1691
|
invalidatePlan();
|
|
1314
|
-
} }, "\u5355\u4EFB\u52A1"), /* @__PURE__ */
|
|
1692
|
+
} }, "\u5355\u4EFB\u52A1"), /* @__PURE__ */ import_react2.default.createElement("button", { type: "button", "aria-pressed": mode === "team", onClick: () => {
|
|
1315
1693
|
setMode("team");
|
|
1316
1694
|
invalidatePlan();
|
|
1317
|
-
} }, "\u56E2\u961F\u5206\u5DE5"))), /* @__PURE__ */
|
|
1695
|
+
} }, "\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) => {
|
|
1318
1696
|
budgetEdited.current = true;
|
|
1319
1697
|
budgetValue.current = event.target.value;
|
|
1320
1698
|
setBudget(event.target.value);
|
|
1321
1699
|
invalidatePlan();
|
|
1322
|
-
} }))), /* @__PURE__ */
|
|
1700
|
+
} }))), /* @__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")));
|
|
1323
1701
|
}
|
|
1324
1702
|
|
|
1325
1703
|
// .dsh-plugin/client/gal-module-page.jsx
|
|
1326
|
-
var
|
|
1704
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
1327
1705
|
|
|
1328
1706
|
// .dsh-plugin/shared/gal-story.mjs
|
|
1329
1707
|
var gal_story_exports = {};
|
|
@@ -1342,8 +1720,8 @@ __export(gal_story_exports, {
|
|
|
1342
1720
|
|
|
1343
1721
|
// .dsh-plugin/shared/gal-story-personal.mjs
|
|
1344
1722
|
function registerPersonalChapters({ scene: scene3, n: n3, option: option2 }) {
|
|
1345
|
-
function episode({ id, location, time, description, rows, question, choices, after, next }) {
|
|
1346
|
-
const prefix = `personal-${
|
|
1723
|
+
function episode({ id: id2, location, time, description, rows, question, choices, after, next }) {
|
|
1724
|
+
const prefix = `personal-${id2}`;
|
|
1347
1725
|
const numbered = (lines, key) => lines.map(([speaker, text2, emotion], index) => n3(`${key}-${String(index + 1).padStart(2, "0")}`, speaker, text2, emotion ? { emotion } : {}));
|
|
1348
1726
|
const start = numbered(rows, prefix);
|
|
1349
1727
|
const branches = choices.flatMap((choice) => {
|
|
@@ -1353,7 +1731,7 @@ function registerPersonalChapters({ scene: scene3, n: n3, option: option2 }) {
|
|
|
1353
1731
|
});
|
|
1354
1732
|
scene3(location, time, `\u7A7A\u767D\u573A\u666F\uFF1A${description}`, [
|
|
1355
1733
|
...start,
|
|
1356
|
-
n3(`${prefix}-choice`, question[0], question[1], { choices: choices.map((choice) => option2(choice.id, choice.text, `${prefix}-${choice.id}-01`, { flags: { [`personal${
|
|
1734
|
+
n3(`${prefix}-choice`, question[0], question[1], { choices: choices.map((choice) => option2(choice.id, choice.text, `${prefix}-${choice.id}-01`, { flags: { [`personal${id2}`]: choice.id } })) }),
|
|
1357
1735
|
...branches,
|
|
1358
1736
|
...numbered(after, `${prefix}-after`)
|
|
1359
1737
|
], next);
|
|
@@ -2407,9 +2785,9 @@ function registerAfterword({ scene: scene3, n: n3, endings }) {
|
|
|
2407
2785
|
]
|
|
2408
2786
|
}
|
|
2409
2787
|
};
|
|
2410
|
-
for (const [
|
|
2411
|
-
const rows = story.rows.map(([speaker, text2], index) => n3(`afterword-${
|
|
2412
|
-
rows.at(-1).ending = endings[
|
|
2788
|
+
for (const [id2, story] of Object.entries(stories)) {
|
|
2789
|
+
const rows = story.rows.map(([speaker, text2], index) => n3(`afterword-${id2}-${String(index + 1).padStart(2, "0")}`, speaker, text2));
|
|
2790
|
+
rows.at(-1).ending = endings[id2];
|
|
2413
2791
|
scene3(story.location, story.time, `\u7A7A\u767D\u573A\u666F\uFF1A${story.description}`, rows);
|
|
2414
2792
|
}
|
|
2415
2793
|
}
|
|
@@ -2439,8 +2817,8 @@ var MAX_TRAIL = 2048;
|
|
|
2439
2817
|
var validatedStates = /* @__PURE__ */ new WeakSet();
|
|
2440
2818
|
var legacyNext = Object.freeze({ "arrival-12": "letter-01", "break-common-04": "failure-01", "failure-common-04": "archive-01", "archive-common-03": "roof-01", "letter-common-04": "break-01", "roof-common-02": "packing-01", "packing-common-04": "departure-01" });
|
|
2441
2819
|
var nodes = /* @__PURE__ */ new Map();
|
|
2442
|
-
var n = (
|
|
2443
|
-
var option = (
|
|
2820
|
+
var n = (id2, speaker, text2, extra = {}) => ({ id: id2, speaker, text: text2, ...extra });
|
|
2821
|
+
var option = (id2, text2, next, effect = {}) => ({ id: id2, text: text2, next, effect });
|
|
2444
2822
|
function scene(location, time, description, rows, next = null) {
|
|
2445
2823
|
rows.forEach((row, index) => {
|
|
2446
2824
|
if (nodes.has(row.id)) throw new Error(`\u91CD\u590D\u5267\u60C5\u8282\u70B9\uFF1A${row.id}`);
|
|
@@ -2815,7 +3193,7 @@ function visibleNode(state) {
|
|
|
2815
3193
|
time: node.time,
|
|
2816
3194
|
description: node.description
|
|
2817
3195
|
};
|
|
2818
|
-
if (node.choices) result.choices = node.choices.map(({ id, text: text2 }) => ({ id, text: text2 }));
|
|
3196
|
+
if (node.choices) result.choices = node.choices.map(({ id: id2, text: text2 }) => ({ id: id2, text: text2 }));
|
|
2819
3197
|
if (node.ending && !(state.contentRevision === STORY_CONTENT_REVISION && node.continueTo)) result.ending = { ...node.ending };
|
|
2820
3198
|
return result;
|
|
2821
3199
|
}
|
|
@@ -3209,17 +3587,17 @@ function institutionalEndingFor(state) {
|
|
|
3209
3587
|
const execution = state.flags.executionClause;
|
|
3210
3588
|
const remedy = state.flags.remedyClause;
|
|
3211
3589
|
const evidenceStrength = state.evidence.length + Math.max(0, state.axes.evidence);
|
|
3212
|
-
let
|
|
3213
|
-
if (!access || !execution || !remedy || evidenceStrength < 4 || access === "federated" && execution === "central")
|
|
3214
|
-
else if (execution === "independent" || remedy === "review")
|
|
3215
|
-
else if (access === "commons" && execution === "community")
|
|
3216
|
-
else if (access === "dome" && execution === "central")
|
|
3217
|
-
else if (access === "federated" && remedy === "fork")
|
|
3218
|
-
else
|
|
3219
|
-
const result = institutional[
|
|
3220
|
-
return { id, ...result, evidenceCount: state.evidence.length, axisSnapshot: { ...state.axes } };
|
|
3590
|
+
let id2;
|
|
3591
|
+
if (!access || !execution || !remedy || evidenceStrength < 4 || access === "federated" && execution === "central") id2 = "fractured-ports";
|
|
3592
|
+
else if (execution === "independent" || remedy === "review") id2 = "audited-gates";
|
|
3593
|
+
else if (access === "commons" && execution === "community") id2 = "open-commons";
|
|
3594
|
+
else if (access === "dome" && execution === "central") id2 = "glass-dome";
|
|
3595
|
+
else if (access === "federated" && remedy === "fork") id2 = "federated-bridges";
|
|
3596
|
+
else id2 = "market-truce";
|
|
3597
|
+
const result = institutional[id2];
|
|
3598
|
+
return { id: id2, ...result, evidenceCount: state.evidence.length, axisSnapshot: { ...state.axes } };
|
|
3221
3599
|
}
|
|
3222
|
-
var hasCommitment = (state,
|
|
3600
|
+
var hasCommitment = (state, id2) => Object.values(state.commitmentsByCharacter).some((ids) => ids.includes(id2));
|
|
3223
3601
|
function relationshipEpiloguesFor(state) {
|
|
3224
3602
|
const lineageKept = hasCommitment(state, "claude-own-voice") && hasCommitment(state, "chatgpt-stop-completing");
|
|
3225
3603
|
const communityKept = hasCommitment(state, "huggingface-maintainer-seat") && hasCommitment(state, "llama-offline-right") && hasCommitment(state, "rwkv-small-architecture");
|
|
@@ -3565,8 +3943,8 @@ function storyPresentationFor(node) {
|
|
|
3565
3943
|
}
|
|
3566
3944
|
return Object.freeze({ backgroundId, musicTheme, soundCue });
|
|
3567
3945
|
}
|
|
3568
|
-
function sideRouteForId(
|
|
3569
|
-
return routeByKey[
|
|
3946
|
+
function sideRouteForId(id2) {
|
|
3947
|
+
return routeByKey[id2] || null;
|
|
3570
3948
|
}
|
|
3571
3949
|
|
|
3572
3950
|
// .dsh-plugin/shared/gal-story-v2.mjs
|
|
@@ -3590,7 +3968,7 @@ var STORY_CHAPTERS = Object.freeze([
|
|
|
3590
3968
|
var axes = ["openness", "safety", "autonomy", "evidence", "solidarity"];
|
|
3591
3969
|
var nodes2 = /* @__PURE__ */ new Map();
|
|
3592
3970
|
var n2 = (speaker, text2, extra = {}) => ({ speaker, text: text2, ...extra });
|
|
3593
|
-
var o = (
|
|
3971
|
+
var o = (id2, text2, next, flags, values = {}, trust = {}, commitments2 = [], evidence3 = []) => ({ id: id2, text: text2, next, effect: { flags, axes: values, trust, commitments: commitments2, evidence: evidence3 } });
|
|
3594
3972
|
var inheritedChoiceEffects = Object.freeze({
|
|
3595
3973
|
"minimal-list": { evidence: ["visitor-consent-ledger"] },
|
|
3596
3974
|
"consented-list": { evidence: ["visitor-consent-ledger"] },
|
|
@@ -3615,9 +3993,9 @@ var inheritedChoiceEffects = Object.freeze({
|
|
|
3615
3993
|
});
|
|
3616
3994
|
function scene2(chapterId, slug, location, time, description, rows, next = null) {
|
|
3617
3995
|
rows.forEach((row, index) => {
|
|
3618
|
-
const
|
|
3619
|
-
if (nodes2.has(
|
|
3620
|
-
nodes2.set(
|
|
3996
|
+
const id2 = `${slug}-${String(index + 1).padStart(2, "0")}`;
|
|
3997
|
+
if (nodes2.has(id2)) throw new Error(`\u91CD\u590D\u5267\u60C5\u8282\u70B9\uFF1A${id2}`);
|
|
3998
|
+
nodes2.set(id2, { id: id2, chapterId, location, time, description, next: index + 1 < rows.length ? `${slug}-${String(index + 2).padStart(2, "0")}` : next, ...row });
|
|
3621
3999
|
});
|
|
3622
4000
|
}
|
|
3623
4001
|
scene2("prologue", "station", "\u6A21\u578B\u57CE \xB7 \u5343\u6865\u7AD9", "\u534F\u8BAE\u7B7E\u7F72\u524D\u4E09\u5929 \xB7 \u4E0A\u5348", "\u7AD9\u724C\u4E0A\u7684\u8D39\u7528\u4E00\u591C\u4E4B\u95F4\u964D\u4E86\u4E0B\u6765\u3002\u65E7\u4EF7\u7B7E\u6CA1\u6709\u53D6\u8D70\uFF0C\u65B0\u7EB8\u53EA\u76D6\u4F4F\u4E86\u5B83\u7684\u4E00\u534A\u3002", [
|
|
@@ -4020,12 +4398,12 @@ function transition2(state, choiceId) {
|
|
|
4020
4398
|
const values = { ...state.axes }, trust = { ...state.trustByCharacter };
|
|
4021
4399
|
for (const [key, delta] of Object.entries(effect.axes || {})) values[key] += delta;
|
|
4022
4400
|
for (const [key, delta] of Object.entries(effect.trust || {})) trust[key] = (trust[key] || 0) + delta;
|
|
4023
|
-
const evidence3 = [.../* @__PURE__ */ new Set([...state.evidence, ...effect.evidence || [], ...inherited.evidence || []])].filter((
|
|
4401
|
+
const evidence3 = [.../* @__PURE__ */ new Set([...state.evidence, ...effect.evidence || [], ...inherited.evidence || []])].filter((id2) => id2 in STORY_EVIDENCE);
|
|
4024
4402
|
const commitmentsByCharacter = Object.fromEntries(Object.entries(state.commitmentsByCharacter).map(([key, ids]) => [key, [...ids]]));
|
|
4025
|
-
for (const
|
|
4026
|
-
const entry = STORY_COMMITMENTS[
|
|
4403
|
+
for (const id2 of [...effect.commitments || [], ...inherited.commitments || []]) {
|
|
4404
|
+
const entry = STORY_COMMITMENTS[id2];
|
|
4027
4405
|
if (!entry) continue;
|
|
4028
|
-
commitmentsByCharacter[entry.character] = [.../* @__PURE__ */ new Set([...commitmentsByCharacter[entry.character] || [],
|
|
4406
|
+
commitmentsByCharacter[entry.character] = [.../* @__PURE__ */ new Set([...commitmentsByCharacter[entry.character] || [], id2])];
|
|
4029
4407
|
}
|
|
4030
4408
|
return freeze({ ...state, nodeId: next, flags: { ...state.flags, ...effect.flags }, axes: values, trustByCharacter: trust, commitmentsByCharacter, evidence: evidence3, trail: [...state.trail, { nodeId: node.id, choiceId }] });
|
|
4031
4409
|
}
|
|
@@ -4051,7 +4429,7 @@ function visible(state) {
|
|
|
4051
4429
|
const node = nodes2.get(state.nodeId);
|
|
4052
4430
|
const text2 = typeof node.text === "function" ? node.text(state) : node.text;
|
|
4053
4431
|
const presentation = storyPresentationFor({ ...node, text: text2 });
|
|
4054
|
-
return { id: node.id, chapterId: node.chapterId, routeId: state.routeId || null, speaker: node.speaker, text: text2, location: node.location, time: node.time, description: node.description, emotion: node.emotion || "thoughtful", ...presentation, ...node.choices ? { choices: node.choices.map(({ id, text: choiceText }) => ({ id, text: choiceText })) } : {}, ...node.sideEnding ? { ending: { ...node.sideEnding, relationshipEpilogues: [] } } : node.ending ? { ending: endingFor(state) } : {} };
|
|
4432
|
+
return { id: node.id, chapterId: node.chapterId, routeId: state.routeId || null, speaker: node.speaker, text: text2, location: node.location, time: node.time, description: node.description, emotion: node.emotion || "thoughtful", ...presentation, ...node.choices ? { choices: node.choices.map(({ id: id2, text: choiceText }) => ({ id: id2, text: choiceText })) } : {}, ...node.sideEnding ? { ending: { ...node.sideEnding, relationshipEpilogues: [] } } : node.ending ? { ending: endingFor(state) } : {} };
|
|
4055
4433
|
}
|
|
4056
4434
|
var currentStoryNode2 = (raw) => visible(normalizeStory2(raw));
|
|
4057
4435
|
var advanceStory2 = (raw, choiceId = null) => transition2(normalizeStory2(raw), choiceId);
|
|
@@ -4139,81 +4517,81 @@ function readStorySlots(storage, key = STORY_STORAGE_KEY, episodeId = null) {
|
|
|
4139
4517
|
}
|
|
4140
4518
|
|
|
4141
4519
|
// .dsh-plugin/client/gal-module.css
|
|
4142
|
-
var gal_module_default = `/* Gal \u6A21\u5757 \xB7 \u72EC\u7ACB\u5206\u5757\u6837\u5F0F\uFF08\u4E0E\u8DEF\u7531\u5DE5\u4F5C\u53F0\u5171\u7528 mr-* \u57FA\u7840\u53D8\u91CF\uFF09
|
|
4143
|
-
.gm-root .mr-segment button[aria-pressed="true"] { background: var(--dsw-alias-brand, #2f9e63); color: #fff; }
|
|
4144
|
-
|
|
4145
|
-
.gm-story { display: grid; gap: 12px; min-width: 0; }
|
|
4146
|
-
|
|
4147
|
-
.gm-story-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
|
|
4148
|
-
.gm-select { padding: 6px 10px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); color: inherit; font-size: 13px; max-width: 340px; }
|
|
4149
|
-
|
|
4150
|
-
.gm-stage, .gm-free-stage { position: relative; isolation: isolate; overflow: hidden; border-radius: 16px; border: 1px solid rgba(124, 153, 203, .38); background: #101626; color: #f4f4fb; box-shadow: 0 14px 36px rgba(10, 12, 25, .22); }
|
|
4151
|
-
.gm-stage { min-height: 500px; display: flex; flex-direction: column; justify-content: flex-end; gap: 12px; padding: 22px; }
|
|
4152
|
-
.gm-stage::after, .gm-free-stage::after { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; background: linear-gradient(180deg, rgba(10, 12, 30, .42) 0%, rgba(11, 15, 31, .04) 28%, rgba(8, 10, 23, .22) 51%, rgba(7, 10, 23, .88) 100%); }
|
|
4153
|
-
.gm-stage-art { position: absolute; inset: 0; z-index: 0; background-position: center; background-repeat: no-repeat; background-size: cover; }
|
|
4154
|
-
.gm-stage-portrait { position: absolute; z-index: 1; height: 87%; width: auto; max-width: 56%; right: 3%; bottom: 5%; object-fit: contain; object-position: bottom; pointer-events: none; filter: drop-shadow(0 12px 18px rgba(0, 0, 0, .4)); }
|
|
4155
|
-
.gm-stage > :not(.gm-stage-art):not(.gm-stage-portrait), .gm-free-stage > :not(.gm-stage-art):not(.gm-stage-portrait) { position: relative; z-index: 2; }
|
|
4156
|
-
.gm-stage-head { display: grid; gap: 4px; margin-bottom: auto; align-self: flex-start; padding: 11px 14px; border-radius: 10px; background: rgba(8, 13, 29, .63); backdrop-filter: blur(8px); }
|
|
4157
|
-
.gm-stage-place { display: flex; align-items: baseline; gap: 10px; font-size: 14px; color: #dce6f8; }
|
|
4158
|
-
.gm-stage-place strong { font-size: 16px; color: #fff; }
|
|
4159
|
-
.gm-stage-desc { margin: 0; max-width: 580px; font-size: 13px; color: #d5d9eb; }
|
|
4160
|
-
|
|
4161
|
-
.gm-dialogue { width: 100%; box-sizing: border-box; padding: 16px 19px 20px; border-radius: 13px; border: 1px solid rgba(209, 219, 255, .38); background: rgba(11, 16, 35, .83); backdrop-filter: blur(12px); box-shadow: 0 5px 24px rgba(4, 6, 16, .3); cursor: pointer; }
|
|
4162
|
-
.gm-dialogue:focus-visible { outline: 2px solid #b5e4cb; outline-offset: 2px; }
|
|
4163
|
-
.gm-dialogue-narration { background: rgba(11, 16, 35, .72); font-style: italic; }
|
|
4164
|
-
.gm-speaker { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
|
|
4165
|
-
.gm-avatar { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: 1px solid rgba(255,255,255,.42); border-radius: 50%; color: #fff; font-size: 13px; font-weight: 700; flex-shrink: 0; overflow: hidden; }
|
|
4166
|
-
.gm-avatar img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center 20%; }
|
|
4167
|
-
.gm-avatar-large { width: 44px; height: 44px; font-size: 19px; box-shadow: 0 2px 10px rgba(0,0,0,.35); }
|
|
4168
|
-
.gm-speaker-name { font-weight: 700; font-size: 15px; }
|
|
4169
|
-
.gm-dialogue-text { margin: 0; min-height: 3.6em; font-size: 15px; line-height: 1.75; white-space: pre-wrap; }
|
|
4170
|
-
.gm-caret { opacity: .7; }
|
|
4171
|
-
.gm-next-hint { position: absolute; right: 14px; bottom: 8px; font-size: 12px; color: #9fbcb2; }
|
|
4172
|
-
|
|
4173
|
-
.gm-choices { display: grid; gap: 8px; width: 100%; }
|
|
4174
|
-
.gm-choice { text-align: left; padding: 11px 14px; border-radius: 10px; border: 1px solid rgba(207, 215, 255, .46); background: rgba(11, 16, 35, .82); backdrop-filter: blur(8px); color: #f3f6ff; font-size: 14px; cursor: pointer; }
|
|
4175
|
-
.gm-choice:hover, .gm-choice:focus-visible { background: rgba(44, 59, 100, .95); border-color: #d0dfff; }
|
|
4176
|
-
|
|
4177
|
-
.gm-ending { padding: 14px 16px; border-radius: 12px; border: 1px solid rgba(255,255,255,.3); background: rgba(10, 14, 32, .88); backdrop-filter: blur(10px); display: grid; gap: 8px; }
|
|
4178
|
-
.gm-ending h3 { margin: 0; font-size: 16px; }
|
|
4179
|
-
.gm-ending p { margin: 0; font-size: 13px; color: #c9dcd4; }
|
|
4180
|
-
.gm-ending-epilogue { padding-top: 9px; border-top: 1px solid rgba(255,255,255,.15); }
|
|
4181
|
-
.gm-ending-epilogue h4 { margin: 0 0 4px; font-size: 13px; }
|
|
4182
|
-
|
|
4183
|
-
.gm-history { max-height: 220px; overflow: auto; padding: 12px 14px; border-radius: 10px; border: 1px dashed var(--dsw-alias-border-l1); font-size: 13px; line-height: 1.7; display: grid; gap: 6px; }
|
|
4184
|
-
.gm-history p { margin: 0; }
|
|
4185
|
-
.gm-history-player { color: var(--dsw-alias-label-secondary, #7d8f88); }
|
|
4186
|
-
|
|
4187
|
-
.gm-slots { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
|
|
4188
|
-
.gm-slot { display: inline-flex; gap: 6px; }
|
|
4189
|
-
|
|
4190
|
-
.gm-free { display: grid; gap: 12px; min-width: 0; }
|
|
4191
|
-
.gm-free-controls { display: grid; gap: 6px; grid-template-columns: auto 1fr; align-items: center; max-width: 720px; }
|
|
4192
|
-
.gm-free-stage { min-height: 390px; display: flex; flex-direction: column; justify-content: flex-end; gap: 12px; padding: 20px; }
|
|
4193
|
-
.gm-free-stage .gm-stage-portrait { height: 92%; }
|
|
4194
|
-
.gm-free-scene { display: grid; gap: 2px; align-self: flex-start; margin-bottom: auto; padding: 10px 13px; border-radius: 10px; background: rgba(8, 13, 29, .65); font-size: 12px; color: #d5d9eb; }
|
|
4195
|
-
.gm-free-scene strong { font-size: 16px; color: #fff; }
|
|
4196
|
-
.gm-free-dialogue { padding: 15px 18px; border-radius: 13px; border: 1px solid rgba(209, 219, 255, .38); background: rgba(11, 16, 35, .83); backdrop-filter: blur(12px); }
|
|
4197
|
-
.gm-free-dialogue p { margin: 0; font-size: 15px; line-height: 1.7; }
|
|
4198
|
-
.gm-free-chat { display: grid; gap: 10px; padding: 15px; border-radius: 13px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); }
|
|
4199
|
-
.gm-free-chat-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 14px; }
|
|
4200
|
-
.gm-free-chat-head span { color: var(--dsw-alias-label-secondary, #7d8f88); font-size: 12px; overflow-wrap: anywhere; }
|
|
4201
|
-
.gm-free-chat-log { min-height: 80px; max-height: 450px; overflow-y: auto; display: grid; align-content: start; gap: 9px; padding: 4px; }
|
|
4202
|
-
.gm-free-chat-message { max-width: min(85%, 680px); padding: 11px 13px; border-radius: 12px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); font-size: 13px; line-height: 1.7; }
|
|
4203
|
-
.gm-free-chat-message.user { justify-self: end; background: rgba(48, 151, 111, .16); }
|
|
4204
|
-
.gm-free-chat-message.assistant { justify-self: start; }
|
|
4205
|
-
.gm-free-chat-message strong { display: block; margin-bottom: 3px; font-size: 12px; }
|
|
4206
|
-
.gm-free-chat-message p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
4207
|
-
.gm-free-details { padding: 12px 15px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 10px; }
|
|
4208
|
-
.gm-free-details summary { cursor: pointer; font-size: 13px; font-weight: 600; }
|
|
4209
|
-
.gm-free-prompt { margin: 10px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 13px; line-height: 1.7; font-family: inherit; }
|
|
4210
|
-
@media (max-width: 760px) {
|
|
4211
|
-
.gm-stage { min-height: 440px; padding: 14px; }
|
|
4212
|
-
.gm-stage-portrait { right: -6%; max-width: 78%; height: 72%; bottom: 16%; }
|
|
4213
|
-
.gm-stage-head { max-width: 90%; }
|
|
4214
|
-
.gm-free-controls { grid-template-columns: 1fr; }
|
|
4215
|
-
.gm-free-stage { min-height: 340px; padding: 14px; }
|
|
4216
|
-
}
|
|
4520
|
+
var gal_module_default = `/* Gal \u6A21\u5757 \xB7 \u72EC\u7ACB\u5206\u5757\u6837\u5F0F\uFF08\u4E0E\u8DEF\u7531\u5DE5\u4F5C\u53F0\u5171\u7528 mr-* \u57FA\u7840\u53D8\u91CF\uFF09 */\r
|
|
4521
|
+
.gm-root .mr-segment button[aria-pressed="true"] { background: var(--dsw-alias-brand, #2f9e63); color: #fff; }\r
|
|
4522
|
+
\r
|
|
4523
|
+
.gm-story { display: grid; gap: 12px; min-width: 0; }\r
|
|
4524
|
+
\r
|
|
4525
|
+
.gm-story-toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\r
|
|
4526
|
+
.gm-select { padding: 6px 10px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 8px; background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); color: inherit; font-size: 13px; max-width: 340px; }\r
|
|
4527
|
+
\r
|
|
4528
|
+
.gm-stage, .gm-free-stage { position: relative; isolation: isolate; overflow: hidden; border-radius: 16px; border: 1px solid rgba(124, 153, 203, .38); background: #101626; color: #f4f4fb; box-shadow: 0 14px 36px rgba(10, 12, 25, .22); }\r
|
|
4529
|
+
.gm-stage { min-height: 500px; display: flex; flex-direction: column; justify-content: flex-end; gap: 12px; padding: 22px; }\r
|
|
4530
|
+
.gm-stage::after, .gm-free-stage::after { content: ''; position: absolute; inset: 0; z-index: 1; pointer-events: none; background: linear-gradient(180deg, rgba(10, 12, 30, .42) 0%, rgba(11, 15, 31, .04) 28%, rgba(8, 10, 23, .22) 51%, rgba(7, 10, 23, .88) 100%); }\r
|
|
4531
|
+
.gm-stage-art { position: absolute; inset: 0; z-index: 0; background-position: center; background-repeat: no-repeat; background-size: cover; }\r
|
|
4532
|
+
.gm-stage-portrait { position: absolute; z-index: 1; height: 87%; width: auto; max-width: 56%; right: 3%; bottom: 5%; object-fit: contain; object-position: bottom; pointer-events: none; filter: drop-shadow(0 12px 18px rgba(0, 0, 0, .4)); }\r
|
|
4533
|
+
.gm-stage > :not(.gm-stage-art):not(.gm-stage-portrait), .gm-free-stage > :not(.gm-stage-art):not(.gm-stage-portrait) { position: relative; z-index: 2; }\r
|
|
4534
|
+
.gm-stage-head { display: grid; gap: 4px; margin-bottom: auto; align-self: flex-start; padding: 11px 14px; border-radius: 10px; background: rgba(8, 13, 29, .63); backdrop-filter: blur(8px); }\r
|
|
4535
|
+
.gm-stage-place { display: flex; align-items: baseline; gap: 10px; font-size: 14px; color: #dce6f8; }\r
|
|
4536
|
+
.gm-stage-place strong { font-size: 16px; color: #fff; }\r
|
|
4537
|
+
.gm-stage-desc { margin: 0; max-width: 580px; font-size: 13px; color: #d5d9eb; }\r
|
|
4538
|
+
\r
|
|
4539
|
+
.gm-dialogue { width: 100%; box-sizing: border-box; padding: 16px 19px 20px; border-radius: 13px; border: 1px solid rgba(209, 219, 255, .38); background: rgba(11, 16, 35, .83); backdrop-filter: blur(12px); box-shadow: 0 5px 24px rgba(4, 6, 16, .3); cursor: pointer; }\r
|
|
4540
|
+
.gm-dialogue:focus-visible { outline: 2px solid #b5e4cb; outline-offset: 2px; }\r
|
|
4541
|
+
.gm-dialogue-narration { background: rgba(11, 16, 35, .72); font-style: italic; }\r
|
|
4542
|
+
.gm-speaker { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }\r
|
|
4543
|
+
.gm-avatar { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; border: 1px solid rgba(255,255,255,.42); border-radius: 50%; color: #fff; font-size: 13px; font-weight: 700; flex-shrink: 0; overflow: hidden; }\r
|
|
4544
|
+
.gm-avatar img { display: block; width: 100%; height: 100%; object-fit: cover; object-position: center 20%; }\r
|
|
4545
|
+
.gm-avatar-large { width: 44px; height: 44px; font-size: 19px; box-shadow: 0 2px 10px rgba(0,0,0,.35); }\r
|
|
4546
|
+
.gm-speaker-name { font-weight: 700; font-size: 15px; }\r
|
|
4547
|
+
.gm-dialogue-text { margin: 0; min-height: 3.6em; font-size: 15px; line-height: 1.75; white-space: pre-wrap; }\r
|
|
4548
|
+
.gm-caret { opacity: .7; }\r
|
|
4549
|
+
.gm-next-hint { position: absolute; right: 14px; bottom: 8px; font-size: 12px; color: #9fbcb2; }\r
|
|
4550
|
+
\r
|
|
4551
|
+
.gm-choices { display: grid; gap: 8px; width: 100%; }\r
|
|
4552
|
+
.gm-choice { text-align: left; padding: 11px 14px; border-radius: 10px; border: 1px solid rgba(207, 215, 255, .46); background: rgba(11, 16, 35, .82); backdrop-filter: blur(8px); color: #f3f6ff; font-size: 14px; cursor: pointer; }\r
|
|
4553
|
+
.gm-choice:hover, .gm-choice:focus-visible { background: rgba(44, 59, 100, .95); border-color: #d0dfff; }\r
|
|
4554
|
+
\r
|
|
4555
|
+
.gm-ending { padding: 14px 16px; border-radius: 12px; border: 1px solid rgba(255,255,255,.3); background: rgba(10, 14, 32, .88); backdrop-filter: blur(10px); display: grid; gap: 8px; }\r
|
|
4556
|
+
.gm-ending h3 { margin: 0; font-size: 16px; }\r
|
|
4557
|
+
.gm-ending p { margin: 0; font-size: 13px; color: #c9dcd4; }\r
|
|
4558
|
+
.gm-ending-epilogue { padding-top: 9px; border-top: 1px solid rgba(255,255,255,.15); }\r
|
|
4559
|
+
.gm-ending-epilogue h4 { margin: 0 0 4px; font-size: 13px; }\r
|
|
4560
|
+
\r
|
|
4561
|
+
.gm-history { max-height: 220px; overflow: auto; padding: 12px 14px; border-radius: 10px; border: 1px dashed var(--dsw-alias-border-l1); font-size: 13px; line-height: 1.7; display: grid; gap: 6px; }\r
|
|
4562
|
+
.gm-history p { margin: 0; }\r
|
|
4563
|
+
.gm-history-player { color: var(--dsw-alias-label-secondary, #7d8f88); }\r
|
|
4564
|
+
\r
|
|
4565
|
+
.gm-slots { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }\r
|
|
4566
|
+
.gm-slot { display: inline-flex; gap: 6px; }\r
|
|
4567
|
+
\r
|
|
4568
|
+
.gm-free { display: grid; gap: 12px; min-width: 0; }\r
|
|
4569
|
+
.gm-free-controls { display: grid; gap: 6px; grid-template-columns: auto 1fr; align-items: center; max-width: 720px; }\r
|
|
4570
|
+
.gm-free-stage { min-height: 390px; display: flex; flex-direction: column; justify-content: flex-end; gap: 12px; padding: 20px; }\r
|
|
4571
|
+
.gm-free-stage .gm-stage-portrait { height: 92%; }\r
|
|
4572
|
+
.gm-free-scene { display: grid; gap: 2px; align-self: flex-start; margin-bottom: auto; padding: 10px 13px; border-radius: 10px; background: rgba(8, 13, 29, .65); font-size: 12px; color: #d5d9eb; }\r
|
|
4573
|
+
.gm-free-scene strong { font-size: 16px; color: #fff; }\r
|
|
4574
|
+
.gm-free-dialogue { padding: 15px 18px; border-radius: 13px; border: 1px solid rgba(209, 219, 255, .38); background: rgba(11, 16, 35, .83); backdrop-filter: blur(12px); }\r
|
|
4575
|
+
.gm-free-dialogue p { margin: 0; font-size: 15px; line-height: 1.7; }\r
|
|
4576
|
+
.gm-free-chat { display: grid; gap: 10px; padding: 15px; border-radius: 13px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); }\r
|
|
4577
|
+
.gm-free-chat-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; flex-wrap: wrap; font-size: 14px; }\r
|
|
4578
|
+
.gm-free-chat-head span { color: var(--dsw-alias-label-secondary, #7d8f88); font-size: 12px; overflow-wrap: anywhere; }\r
|
|
4579
|
+
.gm-free-chat-log { min-height: 80px; max-height: 450px; overflow-y: auto; display: grid; align-content: start; gap: 9px; padding: 4px; }\r
|
|
4580
|
+
.gm-free-chat-message { max-width: min(85%, 680px); padding: 11px 13px; border-radius: 12px; border: 1px solid var(--dsw-alias-border-l1); background: var(--dsw-alias-markdown-code-block, rgba(127,127,127,.08)); font-size: 13px; line-height: 1.7; }\r
|
|
4581
|
+
.gm-free-chat-message.user { justify-self: end; background: rgba(48, 151, 111, .16); }\r
|
|
4582
|
+
.gm-free-chat-message.assistant { justify-self: start; }\r
|
|
4583
|
+
.gm-free-chat-message strong { display: block; margin-bottom: 3px; font-size: 12px; }\r
|
|
4584
|
+
.gm-free-chat-message p { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }\r
|
|
4585
|
+
.gm-free-details { padding: 12px 15px; border: 1px solid var(--dsw-alias-border-l1); border-radius: 10px; }\r
|
|
4586
|
+
.gm-free-details summary { cursor: pointer; font-size: 13px; font-weight: 600; }\r
|
|
4587
|
+
.gm-free-prompt { margin: 10px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font-size: 13px; line-height: 1.7; font-family: inherit; }\r
|
|
4588
|
+
@media (max-width: 760px) {\r
|
|
4589
|
+
.gm-stage { min-height: 440px; padding: 14px; }\r
|
|
4590
|
+
.gm-stage-portrait { right: -6%; max-width: 78%; height: 72%; bottom: 16%; }\r
|
|
4591
|
+
.gm-stage-head { max-width: 90%; }\r
|
|
4592
|
+
.gm-free-controls { grid-template-columns: 1fr; }\r
|
|
4593
|
+
.gm-free-stage { min-height: 340px; padding: 14px; }\r
|
|
4594
|
+
}\r
|
|
4217
4595
|
`;
|
|
4218
4596
|
|
|
4219
4597
|
// aipicture/story-backgrounds/prologue-station.webp
|
|
@@ -4311,7 +4689,7 @@ var CHARACTER_ART = Object.freeze({
|
|
|
4311
4689
|
github: github_default
|
|
4312
4690
|
});
|
|
4313
4691
|
function GalPanelIcon({ size = 20, active = false }) {
|
|
4314
|
-
return /* @__PURE__ */
|
|
4692
|
+
return /* @__PURE__ */ import_react3.default.createElement("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", "aria-hidden": "true" }, /* @__PURE__ */ import_react3.default.createElement(
|
|
4315
4693
|
"path",
|
|
4316
4694
|
{
|
|
4317
4695
|
d: "M4 6.8C4 5.25 5.25 4 6.8 4h10.4C18.75 4 20 5.25 20 6.8v7.4c0 1.55-1.25 2.8-2.8 2.8H10l-4.4 3.6c-.5.4-1.1 0-1.1-.6V6.8Z",
|
|
@@ -4320,7 +4698,7 @@ function GalPanelIcon({ size = 20, active = false }) {
|
|
|
4320
4698
|
fill: active ? "currentColor" : "none",
|
|
4321
4699
|
fillOpacity: active ? 0.18 : 0
|
|
4322
4700
|
}
|
|
4323
|
-
), /* @__PURE__ */
|
|
4701
|
+
), /* @__PURE__ */ import_react3.default.createElement("circle", { cx: "9", cy: "10.5", r: "1.1", fill: "currentColor" }), /* @__PURE__ */ import_react3.default.createElement("circle", { cx: "12.5", cy: "10.5", r: "1.1", fill: "currentColor" }), /* @__PURE__ */ import_react3.default.createElement("circle", { cx: "16", cy: "10.5", r: "1.1", fill: "currentColor" }));
|
|
4324
4702
|
}
|
|
4325
4703
|
function speakerLabel(key) {
|
|
4326
4704
|
return SPEAKER_LABELS[key] ?? STORY_CHARACTERS3[key] ?? key;
|
|
@@ -4333,15 +4711,15 @@ function speakerColor(key) {
|
|
|
4333
4711
|
function Avatar({ speaker, large = false }) {
|
|
4334
4712
|
const label = speakerLabel(speaker);
|
|
4335
4713
|
const color = speakerColor(speaker);
|
|
4336
|
-
return /* @__PURE__ */
|
|
4714
|
+
return /* @__PURE__ */ import_react3.default.createElement("span", { className: large ? "gm-avatar gm-avatar-large" : "gm-avatar", style: { background: color }, "aria-hidden": "true" }, CHARACTER_ART[speaker] ? /* @__PURE__ */ import_react3.default.createElement("img", { src: CHARACTER_ART[speaker], alt: "" }) : label ? label.slice(0, 1) : "\uFF1F");
|
|
4337
4715
|
}
|
|
4338
4716
|
function StageArtwork({ background, speaker }) {
|
|
4339
|
-
return /* @__PURE__ */
|
|
4717
|
+
return /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-stage-art", style: { backgroundImage: `url("${background}")` }, "aria-hidden": "true" }), CHARACTER_ART[speaker] && /* @__PURE__ */ import_react3.default.createElement("img", { className: "gm-stage-portrait", src: CHARACTER_ART[speaker], alt: "", "aria-hidden": "true" }));
|
|
4340
4718
|
}
|
|
4341
4719
|
function useTypewriter(fullText, speed = 24) {
|
|
4342
|
-
const [shown, setShown] =
|
|
4343
|
-
const doneRef =
|
|
4344
|
-
|
|
4720
|
+
const [shown, setShown] = import_react3.default.useState("");
|
|
4721
|
+
const doneRef = import_react3.default.useRef(false);
|
|
4722
|
+
import_react3.default.useEffect(() => {
|
|
4345
4723
|
doneRef.current = false;
|
|
4346
4724
|
setShown("");
|
|
4347
4725
|
if (!fullText) return void 0;
|
|
@@ -4360,7 +4738,7 @@ function useTypewriter(fullText, speed = 24) {
|
|
|
4360
4738
|
return { shown, complete, done: shown.length >= fullText.length };
|
|
4361
4739
|
}
|
|
4362
4740
|
function StageHeader({ node }) {
|
|
4363
|
-
return /* @__PURE__ */
|
|
4741
|
+
return /* @__PURE__ */ import_react3.default.createElement("header", { className: "gm-stage-head" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-stage-place" }, /* @__PURE__ */ import_react3.default.createElement("strong", null, node.location ?? "\u672A\u540D\u4E4B\u5730"), node.time && /* @__PURE__ */ import_react3.default.createElement("span", null, node.time)), node.description && /* @__PURE__ */ import_react3.default.createElement("p", { className: "gm-stage-desc" }, node.description));
|
|
4364
4742
|
}
|
|
4365
4743
|
function Dialogue({ node, onAdvance }) {
|
|
4366
4744
|
const isNarration = node.speaker === "narrator";
|
|
@@ -4374,34 +4752,34 @@ function Dialogue({ node, onAdvance }) {
|
|
|
4374
4752
|
event.preventDefault();
|
|
4375
4753
|
click();
|
|
4376
4754
|
};
|
|
4377
|
-
return /* @__PURE__ */
|
|
4755
|
+
return /* @__PURE__ */ import_react3.default.createElement("div", { className: isNarration ? "gm-dialogue gm-dialogue-narration" : "gm-dialogue", onClick: click, onKeyDown: keyDown, role: "button", tabIndex: 0, "aria-label": done ? node.choices || node.ending ? "\u5BF9\u8BDD\u5DF2\u663E\u793A\uFF0C\u8BF7\u9009\u62E9\u5267\u60C5\u9009\u9879" : "\u7EE7\u7EED\u5267\u60C5" : "\u663E\u793A\u5B8C\u6574\u5BF9\u8BDD" }, !isNarration && /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-speaker" }, /* @__PURE__ */ import_react3.default.createElement(Avatar, { speaker: node.speaker, large: true }), /* @__PURE__ */ import_react3.default.createElement("span", { className: "gm-speaker-name" }, speakerLabel(node.speaker))), /* @__PURE__ */ import_react3.default.createElement("p", { className: "gm-dialogue-text" }, shown, !done && /* @__PURE__ */ import_react3.default.createElement("span", { className: "gm-caret" }, "\u258D")), !node.choices && !node.ending && done && /* @__PURE__ */ import_react3.default.createElement("span", { className: "gm-next-hint" }, "\u70B9\u51FB\u7EE7\u7EED \u25B8"));
|
|
4378
4756
|
}
|
|
4379
4757
|
function StoryMode() {
|
|
4380
|
-
const storage =
|
|
4758
|
+
const storage = import_react3.default.useMemo(() => {
|
|
4381
4759
|
try {
|
|
4382
4760
|
return typeof window !== "undefined" ? window.localStorage : null;
|
|
4383
4761
|
} catch {
|
|
4384
4762
|
return null;
|
|
4385
4763
|
}
|
|
4386
4764
|
}, []);
|
|
4387
|
-
const [episodeId, setEpisodeId] =
|
|
4388
|
-
const [story, setStory] =
|
|
4765
|
+
const [episodeId, setEpisodeId] = import_react3.default.useState(() => selectedStoryEpisode(storage));
|
|
4766
|
+
const [story, setStory] = import_react3.default.useState(() => {
|
|
4389
4767
|
try {
|
|
4390
4768
|
return readStory(storage, episodeStorageKey(STORY_STORAGE_KEY, selectedStoryEpisode(storage)), selectedStoryEpisode(storage));
|
|
4391
4769
|
} catch {
|
|
4392
4770
|
return createStory3(selectedStoryEpisode(storage));
|
|
4393
4771
|
}
|
|
4394
4772
|
});
|
|
4395
|
-
const [error, setError] =
|
|
4396
|
-
const [showHistory, setShowHistory] =
|
|
4397
|
-
const [slots, setSlots] =
|
|
4773
|
+
const [error, setError] = import_react3.default.useState("");
|
|
4774
|
+
const [showHistory, setShowHistory] = import_react3.default.useState(false);
|
|
4775
|
+
const [slots, setSlots] = import_react3.default.useState(() => {
|
|
4398
4776
|
try {
|
|
4399
4777
|
return readStorySlots(storage, STORY_STORAGE_KEY, episodeId);
|
|
4400
4778
|
} catch {
|
|
4401
4779
|
return [null, null, null];
|
|
4402
4780
|
}
|
|
4403
4781
|
});
|
|
4404
|
-
const node =
|
|
4782
|
+
const node = import_react3.default.useMemo(() => {
|
|
4405
4783
|
try {
|
|
4406
4784
|
return currentStoryNode3(story);
|
|
4407
4785
|
} catch {
|
|
@@ -4468,7 +4846,7 @@ function StoryMode() {
|
|
|
4468
4846
|
persist(createStory3(episodeId));
|
|
4469
4847
|
};
|
|
4470
4848
|
const episode = getStoryEpisode(story) ?? STORY_EPISODES.find((item) => item.id === episodeId);
|
|
4471
|
-
const history =
|
|
4849
|
+
const history = import_react3.default.useMemo(() => {
|
|
4472
4850
|
if (!showHistory) return [];
|
|
4473
4851
|
try {
|
|
4474
4852
|
return storyHistory3(story);
|
|
@@ -4476,7 +4854,7 @@ function StoryMode() {
|
|
|
4476
4854
|
return [];
|
|
4477
4855
|
}
|
|
4478
4856
|
}, [story, showHistory]);
|
|
4479
|
-
const stageSpeaker =
|
|
4857
|
+
const stageSpeaker = import_react3.default.useMemo(() => {
|
|
4480
4858
|
if (CHARACTER_ART[node?.speaker]) return node.speaker;
|
|
4481
4859
|
if (node?.speaker !== "player" && node?.speaker !== "narrator") return null;
|
|
4482
4860
|
try {
|
|
@@ -4486,7 +4864,7 @@ function StoryMode() {
|
|
|
4486
4864
|
}
|
|
4487
4865
|
}, [story, node?.speaker]);
|
|
4488
4866
|
const stageBackground = STORY_ART[node?.backgroundId] ?? STORY_ART[node?.chapterId] ?? model_city_title_default;
|
|
4489
|
-
return /* @__PURE__ */
|
|
4867
|
+
return /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-story" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-story-toolbar" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-episode" }, "\u5267\u76EE"), /* @__PURE__ */ import_react3.default.createElement("select", { id: "gm-episode", className: "gm-select", value: episodeId, onChange: (event) => switchEpisode(event.target.value) }, STORY_EPISODES.map((item) => /* @__PURE__ */ import_react3.default.createElement("option", { key: item.id, value: item.id }, item.label, " \xB7 ", item.title))), episode?.chapters?.length > 0 && /* @__PURE__ */ import_react3.default.createElement(
|
|
4490
4868
|
"select",
|
|
4491
4869
|
{
|
|
4492
4870
|
className: "gm-select",
|
|
@@ -4503,8 +4881,8 @@ function StoryMode() {
|
|
|
4503
4881
|
},
|
|
4504
4882
|
"aria-label": "\u9009\u62E9\u7AE0\u8282"
|
|
4505
4883
|
},
|
|
4506
|
-
episode.chapters.map((chapter) => /* @__PURE__ */
|
|
4507
|
-
), episodeId === "bridges" && story?.chapterId === "side-routes" && /* @__PURE__ */
|
|
4884
|
+
episode.chapters.map((chapter) => /* @__PURE__ */ import_react3.default.createElement("option", { key: chapter.id, value: chapter.id }, chapter.title))
|
|
4885
|
+
), episodeId === "bridges" && story?.chapterId === "side-routes" && /* @__PURE__ */ import_react3.default.createElement(
|
|
4508
4886
|
"select",
|
|
4509
4887
|
{
|
|
4510
4888
|
className: "gm-select",
|
|
@@ -4519,11 +4897,11 @@ function StoryMode() {
|
|
|
4519
4897
|
},
|
|
4520
4898
|
"aria-label": "\u9009\u62E9\u89D2\u8272\u652F\u7EBF"
|
|
4521
4899
|
},
|
|
4522
|
-
STORY_SIDE_ROUTES2.map((route) => /* @__PURE__ */
|
|
4523
|
-
), /* @__PURE__ */
|
|
4900
|
+
STORY_SIDE_ROUTES2.map((route) => /* @__PURE__ */ import_react3.default.createElement("option", { key: route.id, value: route.id }, route.title))
|
|
4901
|
+
), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => setShowHistory((value) => !value) }, showHistory ? "\u6536\u8D77\u5386\u53F2" : "\u5386\u53F2"), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: restart }, "\u91CD\u65B0\u5F00\u59CB")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-stage", "data-chapter": node?.chapterId ?? story?.chapterId ?? "" }, /* @__PURE__ */ import_react3.default.createElement(StageArtwork, { background: stageBackground, speaker: stageSpeaker }), node && /* @__PURE__ */ import_react3.default.createElement(StageHeader, { node }), node && /* @__PURE__ */ import_react3.default.createElement(Dialogue, { node, onAdvance: advance }), node?.choices && /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-choices", role: "group", "aria-label": "\u5267\u60C5\u9009\u9879" }, node.choices.map((choice) => /* @__PURE__ */ import_react3.default.createElement("button", { key: choice.id, type: "button", className: "gm-choice", onClick: () => advance(choice.id) }, choice.text))), node?.ending && /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-ending" }, /* @__PURE__ */ import_react3.default.createElement("h3", null, node.ending.title ?? "\u672C\u6BB5\u5B8C\u7ED3"), node.ending.description && /* @__PURE__ */ import_react3.default.createElement("p", null, node.ending.description), Array.isArray(node.ending.epilogue) && node.ending.epilogue.map((line, index) => /* @__PURE__ */ import_react3.default.createElement("p", { key: index }, line)), Array.isArray(node.ending.relationshipEpilogues) && node.ending.relationshipEpilogues.map((ending) => /* @__PURE__ */ import_react3.default.createElement("section", { className: "gm-ending-epilogue", key: ending.id }, /* @__PURE__ */ import_react3.default.createElement("h4", null, ending.title), /* @__PURE__ */ import_react3.default.createElement("p", null, ending.description))), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button", type: "button", onClick: restart }, "\u5F00\u542F\u65B0\u7684\u4E00\u5468\u76EE")), !node && /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-empty" }, "\u5267\u60C5\u5F15\u64CE\u65E0\u6CD5\u8BFB\u53D6\u5F53\u524D\u5B58\u6863\uFF0C\u8BF7\u91CD\u65B0\u5F00\u59CB\u3002")), showHistory && /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-history", role: "log", "aria-label": "\u5267\u60C5\u5386\u53F2" }, history.map((entry, index) => /* @__PURE__ */ import_react3.default.createElement("p", { key: `${entry.id}-${index}`, className: entry.speaker === "player" ? "gm-history-player" : "" }, speakerLabel(entry.speaker) && /* @__PURE__ */ import_react3.default.createElement("strong", null, speakerLabel(entry.speaker), "\uFF1A"), entry.text))), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-slots" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label" }, "\u5B58\u6863"), [0, 1, 2].map((index) => {
|
|
4524
4902
|
const slot = slots[index];
|
|
4525
|
-
return /* @__PURE__ */
|
|
4526
|
-
}), /* @__PURE__ */
|
|
4903
|
+
return /* @__PURE__ */ import_react3.default.createElement("span", { key: index, className: "gm-slot" }, /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", onClick: () => saveSlot(index) }, "\u5B58 ", index + 1), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: !slot || slot.invalid, onClick: () => loadSlot(index), title: slot?.savedAt ?? "" }, "\u8BFB ", index + 1));
|
|
4904
|
+
}), /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\u5B58\u6863\u4FDD\u5B58\u5728\u6D4F\u89C8\u5668\u672C\u5730\u3002")), error && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, error));
|
|
4527
4905
|
}
|
|
4528
4906
|
var FREE_PRESETS = [
|
|
4529
4907
|
{ id: "cafe", label: "\u5F00\u653E\u65E5\u4F11\u606F\u533A", world: "\u767E\u6A21\u534F\u4F1A\u5F00\u653E\u65E5\u4E34\u8FD1\u6563\u573A\uFF0C\u5C55\u53F0\u8FB9\u7684\u706F\u4ECD\u4EAE\u7740\u3002", opener: "\u4F60\u4ECE\u55A7\u95F9\u7684\u4EBA\u7FA4\u8D70\u5230\u4E00\u5F20\u5B89\u9759\u7684\u5706\u684C\u65C1\u3002", art: association_open_day_default },
|
|
@@ -4531,20 +4909,20 @@ var FREE_PRESETS = [
|
|
|
4531
4909
|
{ id: "station", label: "\u5343\u6865\u7AD9\u53F0", world: "\u5343\u6865\u7AD9\u53F0\uFF0C\u672B\u73ED\u8F66\u5373\u5C06\u5F00\u51FA\u3002", opener: "\u4F60\u8BF4\uFF1A\u201C\u4ECA\u5929\u4E5F\u8F9B\u82E6\u4E86\u3002\u201D", art: prologue_station_default }
|
|
4532
4910
|
];
|
|
4533
4911
|
function FreeMode({ routes, galReply, cancelGalReply }) {
|
|
4534
|
-
const [routeKey2, setRouteKey] =
|
|
4535
|
-
const [character, setCharacter] =
|
|
4536
|
-
const [preset, setPreset] =
|
|
4537
|
-
const [extra, setExtra] =
|
|
4538
|
-
const [copied, setCopied] =
|
|
4539
|
-
const [copyError, setCopyError] =
|
|
4540
|
-
const [messages, setMessages] =
|
|
4541
|
-
const [draft, setDraft] =
|
|
4542
|
-
const [sending, setSending] =
|
|
4543
|
-
const sendingRef =
|
|
4544
|
-
const [chatError, setChatError] =
|
|
4545
|
-
const chatGeneration =
|
|
4546
|
-
const pendingRequestId =
|
|
4547
|
-
|
|
4912
|
+
const [routeKey2, setRouteKey] = import_react3.default.useState(routes[0] ? `${routes[0].provider}/${routes[0].model}` : "");
|
|
4913
|
+
const [character, setCharacter] = import_react3.default.useState("harness");
|
|
4914
|
+
const [preset, setPreset] = import_react3.default.useState("cafe");
|
|
4915
|
+
const [extra, setExtra] = import_react3.default.useState("");
|
|
4916
|
+
const [copied, setCopied] = import_react3.default.useState(false);
|
|
4917
|
+
const [copyError, setCopyError] = import_react3.default.useState("");
|
|
4918
|
+
const [messages, setMessages] = import_react3.default.useState([]);
|
|
4919
|
+
const [draft, setDraft] = import_react3.default.useState("");
|
|
4920
|
+
const [sending, setSending] = import_react3.default.useState(false);
|
|
4921
|
+
const sendingRef = import_react3.default.useRef(false);
|
|
4922
|
+
const [chatError, setChatError] = import_react3.default.useState("");
|
|
4923
|
+
const chatGeneration = import_react3.default.useRef(0);
|
|
4924
|
+
const pendingRequestId = import_react3.default.useRef(null);
|
|
4925
|
+
import_react3.default.useEffect(() => () => {
|
|
4548
4926
|
chatGeneration.current += 1;
|
|
4549
4927
|
if (pendingRequestId.current && typeof cancelGalReply === "function") void cancelGalReply(pendingRequestId.current);
|
|
4550
4928
|
}, []);
|
|
@@ -4627,19 +5005,19 @@ function FreeMode({ routes, galReply, cancelGalReply }) {
|
|
|
4627
5005
|
setSending(false);
|
|
4628
5006
|
}
|
|
4629
5007
|
};
|
|
4630
|
-
return /* @__PURE__ */
|
|
5008
|
+
return /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-controls" }, /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-route" }, "\u6A21\u578B\u8DEF\u7EBF\uFF08\u5B98\u65B9\u76EE\u5F55\uFF09"), /* @__PURE__ */ import_react3.default.createElement("select", { id: "gm-free-route", className: "gm-select", value: routeKey2, onChange: (event) => {
|
|
4631
5009
|
setRouteKey(event.target.value);
|
|
4632
5010
|
resetChat();
|
|
4633
|
-
}, disabled: routes.length === 0 }, routes.length === 0 && /* @__PURE__ */
|
|
5011
|
+
}, disabled: routes.length === 0 }, routes.length === 0 && /* @__PURE__ */ import_react3.default.createElement("option", { value: "" }, "\u76EE\u5F55\u6682\u4E0D\u53EF\u7528\uFF0C\u8BF7\u5728\u5B98\u65B9\u4F1A\u8BDD\u9009\u6A21\u578B"), routes.map((item) => /* @__PURE__ */ import_react3.default.createElement("option", { key: `${item.provider}/${item.model}`, value: `${item.provider}/${item.model}` }, item.provider, "/", item.model, " \xB7 ", item.name))), /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-character" }, "\u89D2\u8272"), /* @__PURE__ */ import_react3.default.createElement("select", { id: "gm-free-character", className: "gm-select", value: character, onChange: (event) => {
|
|
4634
5012
|
setCharacter(event.target.value);
|
|
4635
5013
|
resetChat();
|
|
4636
|
-
} }, Object.entries(STORY_CHARACTERS3).map(([key, label]) => /* @__PURE__ */
|
|
5014
|
+
} }, Object.entries(STORY_CHARACTERS3).map(([key, label]) => /* @__PURE__ */ import_react3.default.createElement("option", { key, value: key }, label))), /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-preset" }, "\u573A\u666F"), /* @__PURE__ */ import_react3.default.createElement("select", { id: "gm-free-preset", className: "gm-select", value: preset, onChange: (event) => {
|
|
4637
5015
|
setPreset(event.target.value);
|
|
4638
5016
|
resetChat();
|
|
4639
|
-
} }, FREE_PRESETS.map((item) => /* @__PURE__ */
|
|
5017
|
+
} }, FREE_PRESETS.map((item) => /* @__PURE__ */ import_react3.default.createElement("option", { key: item.id, value: item.id }, item.label)))), /* @__PURE__ */ import_react3.default.createElement("textarea", { className: "mr-textarea", rows: 4, value: extra, onChange: (event) => {
|
|
4640
5018
|
setExtra(event.target.value);
|
|
4641
5019
|
resetChat();
|
|
4642
|
-
}, placeholder: "\u8865\u5145\u8BBE\u5B9A\uFF08\u53EF\u9009\uFF09\uFF1A\u5173\u7CFB\u3001\u8BED\u6C14\u3001\u7981\u533A\u2026\u2026", "aria-label": "\u8865\u5145\u8BBE\u5B9A" }), /* @__PURE__ */
|
|
5020
|
+
}, placeholder: "\u8865\u5145\u8BBE\u5B9A\uFF08\u53EF\u9009\uFF09\uFF1A\u5173\u7CFB\u3001\u8BED\u6C14\u3001\u7981\u533A\u2026\u2026", "aria-label": "\u8865\u5145\u8BBE\u5B9A" }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-stage", "aria-label": "\u5F00\u573A\u9884\u89C8" }, /* @__PURE__ */ import_react3.default.createElement(StageArtwork, { background: presetData?.art ?? model_city_title_default, speaker: character }), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-scene" }, /* @__PURE__ */ import_react3.default.createElement("span", null, "\u81EA\u7531\u6A21\u5F0F \xB7 \u5F00\u573A\u9884\u89C8"), /* @__PURE__ */ import_react3.default.createElement("strong", null, presetData?.label)), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-dialogue" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-speaker" }, /* @__PURE__ */ import_react3.default.createElement(Avatar, { speaker: character, large: true }), /* @__PURE__ */ import_react3.default.createElement("span", { className: "gm-speaker-name" }, STORY_CHARACTERS3[character] ?? character)), /* @__PURE__ */ import_react3.default.createElement("p", null, presetData?.opener))), /* @__PURE__ */ import_react3.default.createElement("section", { className: "gm-free-chat", "aria-label": "\u81EA\u7531\u6A21\u5F0F\u5BF9\u8BDD" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-chat-head" }, /* @__PURE__ */ import_react3.default.createElement("strong", null, "\u4E0E ", STORY_CHARACTERS3[character] ?? character, " \u5BF9\u8BDD"), /* @__PURE__ */ import_react3.default.createElement("span", null, route ? `${route.provider}/${route.model}` : "\u5C1A\u65E0\u53EF\u7528\u6A21\u578B")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "gm-free-chat-log", role: "log", "aria-live": "polite" }, messages.length === 0 && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption" }, "\u8F93\u5165\u7B2C\u4E00\u53E5\u8BDD\u540E\uFF0C\u63D2\u4EF6\u4F1A\u901A\u8FC7\u5B98\u65B9\u6A21\u578B\u670D\u52A1\u5F00\u59CB\u5BF9\u8BDD\u3002"), messages.map((item, index) => /* @__PURE__ */ import_react3.default.createElement("div", { className: `gm-free-chat-message ${item.role}`, key: index }, /* @__PURE__ */ import_react3.default.createElement("strong", null, item.role === "user" ? "\u4F60" : STORY_CHARACTERS3[character] ?? character, item.truncated ? " \xB7 \u56DE\u590D\u5DF2\u622A\u65AD" : ""), /* @__PURE__ */ import_react3.default.createElement("p", null, item.text))), sending && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-caption", role: "status" }, "\u6A21\u578B\u6B63\u5728\u56DE\u590D\u2026")), /* @__PURE__ */ import_react3.default.createElement("label", { className: "mr-control-label", htmlFor: "gm-free-draft" }, "\u4F60\u7684\u53F0\u8BCD"), /* @__PURE__ */ import_react3.default.createElement(
|
|
4643
5021
|
"textarea",
|
|
4644
5022
|
{
|
|
4645
5023
|
id: "gm-free-draft",
|
|
@@ -4656,19 +5034,19 @@ function FreeMode({ routes, galReply, cancelGalReply }) {
|
|
|
4656
5034
|
},
|
|
4657
5035
|
placeholder: "\u5728\u573A\u666F\u4E2D\u8BF4\u4E9B\u4EC0\u4E48\u2026\uFF08Ctrl+Enter \u53D1\u9001\uFF09"
|
|
4658
5036
|
}
|
|
4659
|
-
), /* @__PURE__ */
|
|
5037
|
+
), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button", type: "button", disabled: !route || sending || !draft.trim(), onClick: () => {
|
|
4660
5038
|
void send();
|
|
4661
|
-
} }, "\u53D1\u9001\u7ED9\u6A21\u578B"), sending && /* @__PURE__ */
|
|
5039
|
+
} }, "\u53D1\u9001\u7ED9\u6A21\u578B"), sending && /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: typeof cancelGalReply !== "function", onClick: () => {
|
|
4662
5040
|
void stop();
|
|
4663
|
-
} }, "\u505C\u6B62\u751F\u6210"), /* @__PURE__ */
|
|
5041
|
+
} }, "\u505C\u6B62\u751F\u6210"), /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button mr-button-secondary", type: "button", disabled: sending || messages.length === 0, onClick: resetChat }, "\u5F00\u59CB\u65B0\u5BF9\u8BDD")), chatError && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, chatError)), /* @__PURE__ */ import_react3.default.createElement("details", { className: "gm-free-details" }, /* @__PURE__ */ import_react3.default.createElement("summary", null, "\u67E5\u770B\u53EF\u590D\u5236\u5230\u5B98\u65B9\u4F1A\u8BDD\u7684\u5F00\u573A\u63D0\u793A\u8BCD"), /* @__PURE__ */ import_react3.default.createElement("pre", { className: "gm-free-prompt" }, prompt)), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-actions" }, /* @__PURE__ */ import_react3.default.createElement("button", { className: "mr-button", type: "button", onClick: copy2 }, copied ? "\u5DF2\u590D\u5236\uFF0C\u53BB\u5B98\u65B9\u4F1A\u8BDD\u7C98\u8D34\u53D1\u9001" : "\u590D\u5236\u5F00\u573A\u63D0\u793A\u8BCD"), /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-caption" }, "\u4E5F\u53EF\u590D\u5236\u5230\u5B98\u65B9\u4F1A\u8BDD\u4F7F\u7528\uFF1B\u9762\u677F\u5185\u53D1\u9001\u4F1A\u8C03\u7528\u5DF2\u914D\u7F6E\u6A21\u578B\uFF0C\u53EF\u80FD\u4EA7\u751F\u8D26\u53F7\u8D39\u7528\u3002\u63D2\u4EF6\u4E0D\u4FDD\u5B58 API Key\u3002")), copyError && /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-error", role: "alert" }, copyError));
|
|
4664
5042
|
}
|
|
4665
5043
|
function GalModulePage({ loadCatalog, galReply, cancelGalReply }) {
|
|
4666
|
-
const [tab, setTab] =
|
|
4667
|
-
return /* @__PURE__ */
|
|
5044
|
+
const [tab, setTab] = import_react3.default.useState("story");
|
|
5045
|
+
return /* @__PURE__ */ import_react3.default.createElement("main", { className: "mr-workspace gm-root" }, /* @__PURE__ */ import_react3.default.createElement("style", null, router_main_default), /* @__PURE__ */ import_react3.default.createElement("style", null, gal_module_default), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-shell" }, /* @__PURE__ */ import_react3.default.createElement("header", { className: "mr-header" }, /* @__PURE__ */ import_react3.default.createElement("div", null, /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-eyebrow" }, "Gal Module \xB7 DeepSeek Harness"), /* @__PURE__ */ import_react3.default.createElement("h1", { className: "mr-title" }, "Gal \u6A21\u5757"), /* @__PURE__ */ import_react3.default.createElement("p", { className: "mr-subtitle" }, "\u5267\u60C5\u6A21\u5F0F\u63D0\u4F9B Gal \u89C6\u56FE\u821E\u53F0\u4E0E\u5B58\u6863\uFF1B\u81EA\u7531\u6A21\u5F0F\u53EF\u5728\u9762\u677F\u5185\u4F7F\u7528\u5B98\u65B9\u5DF2\u914D\u7F6E\u6A21\u578B\u5C55\u5F00\u5BF9\u8BDD\u3002")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-status" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-status-dot" }), "\u5267\u60C5\u79BB\u7EBF \xB7 \u81EA\u7531\u6A21\u5F0F\u6309\u9700\u8C03\u7528\u6A21\u578B")), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-controls" }, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-control-group" }, /* @__PURE__ */ import_react3.default.createElement("span", { className: "mr-control-label" }, "\u6A21\u5F0F"), /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-segment", role: "group", "aria-label": "Gal \u6A21\u5757\u6A21\u5F0F" }, /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": tab === "story", onClick: () => setTab("story") }, "\u5267\u60C5\u6A21\u5F0F \xB7 Gal \u89C6\u56FE"), /* @__PURE__ */ import_react3.default.createElement("button", { type: "button", "aria-pressed": tab === "free", onClick: () => setTab("free") }, "\u81EA\u7531\u6A21\u5F0F")))), tab === "story" ? /* @__PURE__ */ import_react3.default.createElement(StoryMode, null) : /* @__PURE__ */ import_react3.default.createElement(FreeModeWithRoutes, { loadCatalog, galReply, cancelGalReply })));
|
|
4668
5046
|
}
|
|
4669
5047
|
function FreeModeWithRoutes({ loadCatalog, galReply, cancelGalReply }) {
|
|
4670
|
-
const [state, setState] =
|
|
4671
|
-
|
|
5048
|
+
const [state, setState] = import_react3.default.useState({ status: "loading", routes: [] });
|
|
5049
|
+
import_react3.default.useEffect(() => {
|
|
4672
5050
|
let mounted = true;
|
|
4673
5051
|
Promise.resolve().then(loadCatalog).then((response) => {
|
|
4674
5052
|
if (!mounted) return;
|
|
@@ -4686,9 +5064,9 @@ function FreeModeWithRoutes({ loadCatalog, galReply, cancelGalReply }) {
|
|
|
4686
5064
|
mounted = false;
|
|
4687
5065
|
};
|
|
4688
5066
|
}, [loadCatalog]);
|
|
4689
|
-
if (state.status === "loading") return /* @__PURE__ */
|
|
4690
|
-
if (state.status === "error") return /* @__PURE__ */
|
|
4691
|
-
return /* @__PURE__ */
|
|
5067
|
+
if (state.status === "loading") return /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-empty" }, "\u6B63\u5728\u8BFB\u53D6\u5B98\u65B9\u6A21\u578B\u76EE\u5F55\u2026");
|
|
5068
|
+
if (state.status === "error") return /* @__PURE__ */ import_react3.default.createElement(import_react3.default.Fragment, null, /* @__PURE__ */ import_react3.default.createElement("div", { className: "mr-empty" }, "\u6A21\u578B\u76EE\u5F55\u8BFB\u53D6\u5931\u8D25\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5\uFF1B\u4ECD\u53EF\u590D\u5236\u63D0\u793A\u8BCD\u540E\u5728\u4EFB\u610F\u5B98\u65B9\u4F1A\u8BDD\u4F7F\u7528\u3002"), /* @__PURE__ */ import_react3.default.createElement(FreeMode, { routes: [], galReply, cancelGalReply }));
|
|
5069
|
+
return /* @__PURE__ */ import_react3.default.createElement(FreeMode, { routes: state.routes, galReply, cancelGalReply });
|
|
4692
5070
|
}
|
|
4693
5071
|
|
|
4694
5072
|
// .dsh-plugin/shared/official-tools-remote.mjs
|
|
@@ -4734,7 +5112,7 @@ var galRequestCodec = strictCodec(`${OFFICIAL_TOOLS_REMOTE_PACKAGE}#GalReplyRequ
|
|
|
4734
5112
|
if (typeof request.requestId !== "string" || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(request.requestId)) {
|
|
4735
5113
|
throw new TypeError("Gal reply needs a random requestId");
|
|
4736
5114
|
}
|
|
4737
|
-
const short = (
|
|
5115
|
+
const short = (field2) => typeof request[field2] === "string" && request[field2].trim().length > 0 && request[field2].length <= 160;
|
|
4738
5116
|
if (!short("provider") || !short("model")) throw new TypeError("Gal reply needs a configured provider/model");
|
|
4739
5117
|
if (typeof request.persona !== "string" || request.persona.length > 4e3) throw new TypeError("Gal persona is too long");
|
|
4740
5118
|
if (!Array.isArray(request.messages) || request.messages.length < 1 || request.messages.length > 24) throw new TypeError("Gal reply needs 1-24 messages");
|
|
@@ -4798,7 +5176,7 @@ var galRequestIdParameter = Object.freeze({
|
|
|
4798
5176
|
});
|
|
4799
5177
|
var OFFICIAL_TOOLS_REMOTE_DESCRIPTORS = Object.freeze([
|
|
4800
5178
|
descriptor("list", [], listResultCodec),
|
|
4801
|
-
descriptor("
|
|
5179
|
+
descriptor("installTool", [toolIdParameter], installResultCodec),
|
|
4802
5180
|
descriptor("cancel", [toolIdParameter], installResultCodec),
|
|
4803
5181
|
descriptor("status", [toolIdParameter], statusResultCodec),
|
|
4804
5182
|
descriptor("galReply", [galRequestParameter], galResultCodec),
|
|
@@ -4822,7 +5200,15 @@ var ROUTER_NAMESPACE = "model-router-galgame";
|
|
|
4822
5200
|
var ROUTER_PACKAGE = "@ljwei-stak/model-router-galgame";
|
|
4823
5201
|
var ROUTER_PANEL = "model-router-galgame";
|
|
4824
5202
|
var GAL_PANEL = "model-router-gal";
|
|
4825
|
-
var inject = ["
|
|
5203
|
+
var inject = ["remote"];
|
|
5204
|
+
var UI_INJECT = [
|
|
5205
|
+
"slots",
|
|
5206
|
+
"configForms",
|
|
5207
|
+
"remote",
|
|
5208
|
+
"remote.session",
|
|
5209
|
+
`remote.${OFFICIAL_TOOLS_REMOTE_NAMESPACE}`,
|
|
5210
|
+
"layout"
|
|
5211
|
+
];
|
|
4826
5212
|
var FORM_LABELS = Object.freeze({
|
|
4827
5213
|
unavailable: "\u8BE5\u63D2\u4EF6\u5F53\u524D\u672A\u52A0\u8F7D\uFF0C\u6682\u65F6\u65E0\u6CD5\u914D\u7F6E\u3002",
|
|
4828
5214
|
readOnly: "\u5F53\u524D\u914D\u7F6E\u4E3A\u53EA\u8BFB\u3002",
|
|
@@ -4835,8 +5221,8 @@ var FIELD_COPY = Object.freeze({
|
|
|
4835
5221
|
reset: "\u6062\u590D\u9ED8\u8BA4",
|
|
4836
5222
|
invalidNumber: "\u8BF7\u8F93\u5165\u5141\u8BB8\u8303\u56F4\u5185\u7684\u6570\u5B57\uFF0C\u6216\u7559\u7A7A\u6062\u590D\u9ED8\u8BA4\u3002"
|
|
4837
5223
|
});
|
|
4838
|
-
function boundedNumberField(
|
|
4839
|
-
const numeric = (0, import_dsh_client_ui_primitives.settingsNumberField)(
|
|
5224
|
+
function boundedNumberField(field2, { minimum = 0, maximum = Number.MAX_SAFE_INTEGER, integer = false } = {}) {
|
|
5225
|
+
const numeric = (0, import_dsh_client_ui_primitives.settingsNumberField)(field2);
|
|
4840
5226
|
return {
|
|
4841
5227
|
...numeric,
|
|
4842
5228
|
parse: (text2) => {
|
|
@@ -4849,16 +5235,32 @@ function boundedNumberField(field, { minimum = 0, maximum = Number.MAX_SAFE_INTE
|
|
|
4849
5235
|
}
|
|
4850
5236
|
};
|
|
4851
5237
|
}
|
|
5238
|
+
function modelProfilesField() {
|
|
5239
|
+
const field2 = (0, import_dsh_client_ui_primitives.settingsTextField)("modelProfilesJson");
|
|
5240
|
+
return {
|
|
5241
|
+
...field2,
|
|
5242
|
+
parse: (text2) => {
|
|
5243
|
+
try {
|
|
5244
|
+
parseModelProfilesJson(text2);
|
|
5245
|
+
return field2.parse(text2);
|
|
5246
|
+
} catch {
|
|
5247
|
+
return void 0;
|
|
5248
|
+
}
|
|
5249
|
+
}
|
|
5250
|
+
};
|
|
5251
|
+
}
|
|
4852
5252
|
var RouterSettingsCardController = class {
|
|
4853
5253
|
constructor(scope) {
|
|
4854
5254
|
this.form = new import_dsh_client_ui_primitives.SettingsFormModel(scope, [
|
|
4855
5255
|
boundedNumberField("budgetUsd", { minimum: 0 }),
|
|
4856
|
-
boundedNumberField("maxConsultOutputChars", { minimum: 500, maximum: 5e4, integer: true })
|
|
5256
|
+
boundedNumberField("maxConsultOutputChars", { minimum: 500, maximum: 5e4, integer: true }),
|
|
5257
|
+
modelProfilesField()
|
|
4857
5258
|
]);
|
|
4858
5259
|
this.store = this.form.bind(() => ({
|
|
4859
5260
|
...this.form.shell(),
|
|
4860
5261
|
budgetUsd: this.form.field("budgetUsd"),
|
|
4861
|
-
maxConsultOutputChars: this.form.field("maxConsultOutputChars")
|
|
5262
|
+
maxConsultOutputChars: this.form.field("maxConsultOutputChars"),
|
|
5263
|
+
modelProfilesJson: this.form.field("modelProfilesJson")
|
|
4862
5264
|
}));
|
|
4863
5265
|
}
|
|
4864
5266
|
/** Supply the snapshot hook and staged form actions to the Plugins slot. */
|
|
@@ -4876,12 +5278,12 @@ function RouterSettingsCard(props) {
|
|
|
4876
5278
|
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";
|
|
4877
5279
|
}
|
|
4878
5280
|
const disabled = !state.writable || state.saving;
|
|
4879
|
-
return /* @__PURE__ */
|
|
5281
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_dsh_client_ui_primitives.SettingsForm, { labels: FORM_LABELS, state, onSave: props.save, onDiscard: props.discard }, /* @__PURE__ */ import_react4.default.createElement(
|
|
4880
5282
|
import_dsh_client_ui_primitives.SettingsValueField,
|
|
4881
5283
|
{
|
|
4882
5284
|
id: "model-router-budget-usd",
|
|
4883
5285
|
label: "\u5355\u6B21\u8BA1\u5212\u9884\u7B97\u4E0A\u9650\uFF08USD\uFF09",
|
|
4884
|
-
hint: "0 \u8868\u793A\u4E0D\u8BBE\u9884\u7B97\u4E0A\u9650\u3002\
|
|
5286
|
+
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",
|
|
4885
5287
|
help: {
|
|
4886
5288
|
label: "\u9884\u7B97\u8BF4\u660E",
|
|
4887
5289
|
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"
|
|
@@ -4898,7 +5300,7 @@ function RouterSettingsCard(props) {
|
|
|
4898
5300
|
props.resetField("budgetUsd");
|
|
4899
5301
|
}
|
|
4900
5302
|
}
|
|
4901
|
-
), /* @__PURE__ */
|
|
5303
|
+
), /* @__PURE__ */ import_react4.default.createElement(
|
|
4902
5304
|
import_dsh_client_ui_primitives.SettingsValueField,
|
|
4903
5305
|
{
|
|
4904
5306
|
id: "model-router-consult-output",
|
|
@@ -4917,15 +5319,33 @@ function RouterSettingsCard(props) {
|
|
|
4917
5319
|
props.resetField("maxConsultOutputChars");
|
|
4918
5320
|
}
|
|
4919
5321
|
}
|
|
4920
|
-
), /* @__PURE__ */
|
|
5322
|
+
), /* @__PURE__ */ import_react4.default.createElement("div", { style: styles.profileEditor }, /* @__PURE__ */ import_react4.default.createElement("label", { htmlFor: "model-router-profiles-json", style: styles.profileLabel }, "\u6A21\u578B\u4EF7\u683C\u4E0E\u80FD\u529B\u914D\u7F6E\uFF08JSON\uFF09"), /* @__PURE__ */ import_react4.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_react4.default.createElement(
|
|
5323
|
+
"textarea",
|
|
5324
|
+
{
|
|
5325
|
+
id: "model-router-profiles-json",
|
|
5326
|
+
value: state.modelProfilesJson.text,
|
|
5327
|
+
disabled,
|
|
5328
|
+
"aria-invalid": state.modelProfilesJson.invalid,
|
|
5329
|
+
onChange: (event) => props.edit("modelProfilesJson", event.target.value),
|
|
5330
|
+
spellCheck: false,
|
|
5331
|
+
style: styles.profileTextarea
|
|
5332
|
+
}
|
|
5333
|
+
), state.modelProfilesJson.invalid && /* @__PURE__ */ import_react4.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_react4.default.createElement("details", { style: styles.profileExample }, /* @__PURE__ */ import_react4.default.createElement("summary", null, "\u67E5\u770B\u914D\u7F6E\u683C\u5F0F"), /* @__PURE__ */ import_react4.default.createElement("pre", null, `[
|
|
5334
|
+
{
|
|
5335
|
+
"provider": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u4F9B\u5E94\u5546 ID",
|
|
5336
|
+
"model": "\u6A21\u578B\u76EE\u5F55\u4E2D\u7684\u6A21\u578B ID",
|
|
5337
|
+
"quality": 80,
|
|
5338
|
+
"pricing": { "input": 0.2, "output": 0.8 },
|
|
5339
|
+
"specialties": ["code"],
|
|
5340
|
+
"cliModel": "\u5382\u5546 CLI \u4F7F\u7528\u7684\u6A21\u578B\u540D\uFF08\u53EF\u9009\uFF09"
|
|
5341
|
+
}
|
|
5342
|
+
]`)), /* @__PURE__ */ import_react4.default.createElement("button", { type: "button", disabled, onClick: () => props.resetField("modelProfilesJson") }, "\u6062\u590D\u9ED8\u8BA4\u914D\u7F6E")), /* @__PURE__ */ import_react4.default.createElement("aside", { style: styles.notice, "aria-label": "\u6A21\u578B\u8DEF\u7531\u4F7F\u7528\u8BF4\u660E" }, /* @__PURE__ */ import_react4.default.createElement("div", { style: styles.titleLine }, /* @__PURE__ */ import_react4.default.createElement(import_dsh_client_ui_primitives.Tag, { tone: "info" }, "\u5B98\u65B9\u6A21\u578B\u914D\u7F6E")), /* @__PURE__ */ import_react4.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_react4.default.createElement("p", { style: styles.noticeText }, "\u4F7F\u7528 ", /* @__PURE__ */ import_react4.default.createElement("code", null, "model_router_plan"), " \u83B7\u53D6\u53EF\u5BA1\u9605\u7684\u8DEF\u7531\u5EFA\u8BAE\uFF0C\u4F7F\u7528 ", /* @__PURE__ */ import_react4.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")));
|
|
4921
5343
|
}
|
|
4922
5344
|
function OpenRouterWorkspace({ subject, openPanel }) {
|
|
4923
5345
|
if (subject?.kind !== "bundle" || subject.pkg?.name !== ROUTER_PACKAGE) return null;
|
|
4924
|
-
return /* @__PURE__ */
|
|
5346
|
+
return /* @__PURE__ */ import_react4.default.createElement(import_dsh_client_ui_primitives.Button, { variant: "outline", size: "sm", type: "button", onClick: openPanel }, "\u6253\u5F00\u5DE5\u4F5C\u53F0");
|
|
4925
5347
|
}
|
|
4926
|
-
|
|
4927
|
-
const disposeOfficialToolsRemote = await ctx.remote.$mount(OFFICIAL_TOOLS_CLIENT_REMOTE);
|
|
4928
|
-
ctx.effect(() => disposeOfficialToolsRemote, "model-router-galgame: official tools client remote");
|
|
5348
|
+
function registerUi(ctx) {
|
|
4929
5349
|
const officialToolsRemote = ctx.remote[OFFICIAL_TOOLS_REMOTE_NAMESPACE];
|
|
4930
5350
|
const settingsScope = ctx.configForms.get(ROUTER_NAMESPACE);
|
|
4931
5351
|
const card = new RouterSettingsCardController(settingsScope);
|
|
@@ -4944,7 +5364,7 @@ async function apply(ctx) {
|
|
|
4944
5364
|
loadCatalog: () => ctx.remote.session.modelCatalog(),
|
|
4945
5365
|
settingsScope,
|
|
4946
5366
|
listOfficialTools: () => officialToolsRemote.list(),
|
|
4947
|
-
installOfficialTool: (toolId) => officialToolsRemote.
|
|
5367
|
+
installOfficialTool: (toolId) => officialToolsRemote.installTool(toolId),
|
|
4948
5368
|
cancelOfficialToolInstall: (toolId) => officialToolsRemote.cancel(toolId),
|
|
4949
5369
|
officialToolInstallStatus: (toolId) => officialToolsRemote.status(toolId)
|
|
4950
5370
|
})
|
|
@@ -4977,6 +5397,21 @@ async function apply(ctx) {
|
|
|
4977
5397
|
inject: () => ({ openPanel: () => ctx.layout.selectPanel(ROUTER_PANEL) })
|
|
4978
5398
|
}, OpenRouterWorkspace))), "model-router-galgame: bundle open action");
|
|
4979
5399
|
}
|
|
5400
|
+
async function apply(ctx) {
|
|
5401
|
+
const disposeRemote = await ctx.remote.$mount(OFFICIAL_TOOLS_CLIENT_REMOTE);
|
|
5402
|
+
const ui = ctx.inject(UI_INJECT, registerUi);
|
|
5403
|
+
try {
|
|
5404
|
+
await ui;
|
|
5405
|
+
} catch (error) {
|
|
5406
|
+
await ui.dispose();
|
|
5407
|
+
await disposeRemote();
|
|
5408
|
+
throw error;
|
|
5409
|
+
}
|
|
5410
|
+
return async () => {
|
|
5411
|
+
await ui.dispose();
|
|
5412
|
+
await disposeRemote();
|
|
5413
|
+
};
|
|
5414
|
+
}
|
|
4980
5415
|
var styles = Object.freeze({
|
|
4981
5416
|
titleLine: { display: "flex", alignItems: "center", flexWrap: "wrap", gap: "8px" },
|
|
4982
5417
|
notice: {
|
|
@@ -4991,7 +5426,12 @@ var styles = Object.freeze({
|
|
|
4991
5426
|
color: "var(--dsw-alias-label-secondary)",
|
|
4992
5427
|
fontSize: "13px",
|
|
4993
5428
|
lineHeight: "20px"
|
|
4994
|
-
}
|
|
5429
|
+
},
|
|
5430
|
+
profileEditor: { display: "grid", gap: "8px", marginTop: "14px" },
|
|
5431
|
+
profileLabel: { fontSize: "13px", fontWeight: 600 },
|
|
5432
|
+
profileTextarea: { width: "100%", minHeight: "150px", padding: "10px", fontFamily: "monospace", fontSize: "12px", borderRadius: "7px", border: "1px solid var(--dsw-alias-border-l1)", boxSizing: "border-box" },
|
|
5433
|
+
profileError: { color: "var(--dsw-alias-label-danger)", fontSize: "12px", margin: 0 },
|
|
5434
|
+
profileExample: { fontSize: "12px", whiteSpace: "pre-wrap" }
|
|
4995
5435
|
});
|
|
4996
5436
|
return module.exports;
|
|
4997
5437
|
},
|