dsh-plugin-effort-declare 0.1.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/CONTRIBUTING.en.md +57 -0
- package/CONTRIBUTING.md +57 -0
- package/INSTALL.en.md +62 -0
- package/INSTALL.md +62 -0
- package/LICENSE +21 -0
- package/README.en.md +64 -0
- package/README.md +64 -0
- package/cordis.patch.yml +5 -0
- package/lib/client.js +1237 -0
- package/lib/client.js.map +1 -0
- package/lib/index.js +6 -0
- package/lib/types/client/EffortDeclareSection.d.ts +24 -0
- package/lib/types/client/EffortDeclareSection.d.ts.map +1 -0
- package/lib/types/client/index.d.ts +15 -0
- package/lib/types/client/index.d.ts.map +1 -0
- package/lib/types/client/load-drafts.d.ts +20 -0
- package/lib/types/client/load-drafts.d.ts.map +1 -0
- package/lib/types/client/locales.d.ts +8 -0
- package/lib/types/client/locales.d.ts.map +1 -0
- package/lib/types/client/schema-ops.d.ts +13 -0
- package/lib/types/client/schema-ops.d.ts.map +1 -0
- package/lib/types/core/catalog.d.ts +31 -0
- package/lib/types/core/catalog.d.ts.map +1 -0
- package/lib/types/core/drafts.d.ts +53 -0
- package/lib/types/core/drafts.d.ts.map +1 -0
- package/lib/types/core/efforts.d.ts +32 -0
- package/lib/types/core/efforts.d.ts.map +1 -0
- package/lib/types/core/filter.d.ts +28 -0
- package/lib/types/core/filter.d.ts.map +1 -0
- package/lib/types/core/path-ops.d.ts +40 -0
- package/lib/types/core/path-ops.d.ts.map +1 -0
- package/lib/types/core/paths.d.ts +16 -0
- package/lib/types/core/paths.d.ts.map +1 -0
- package/lib/types/core/presets.d.ts +31 -0
- package/lib/types/core/presets.d.ts.map +1 -0
- package/lib/types/core/validate.d.ts +3 -0
- package/lib/types/core/validate.d.ts.map +1 -0
- package/lib/types/index.d.ts +10 -0
- package/lib/types/index.d.ts.map +1 -0
- package/package.json +87 -0
- package/src/README.en.md +23 -0
- package/src/README.md +23 -0
- package/src/client/EffortDeclareSection.tsx +496 -0
- package/src/client/README.en.md +27 -0
- package/src/client/README.md +27 -0
- package/src/client/effort-declare.module.css +278 -0
- package/src/client/index.ts +97 -0
- package/src/client/load-drafts.ts +83 -0
- package/src/client/locales.ts +133 -0
- package/src/client/schema-ops.ts +20 -0
- package/src/core/README.en.md +27 -0
- package/src/core/README.md +27 -0
- package/src/core/catalog.ts +61 -0
- package/src/core/drafts.ts +137 -0
- package/src/core/efforts.ts +119 -0
- package/src/core/filter.ts +82 -0
- package/src/core/path-ops.ts +65 -0
- package/src/core/paths.ts +45 -0
- package/src/core/presets.ts +104 -0
- package/src/core/validate.ts +7 -0
- package/src/css-modules.d.ts +6 -0
- package/src/index.ts +11 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,1237 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "dsh-plugin-effort-declare",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
7
|
+
let react = require("react");
|
|
8
|
+
let react_jsx_runtime = require("react/jsx-runtime");
|
|
9
|
+
//#region src/core/catalog.ts
|
|
10
|
+
/**
|
|
11
|
+
* Canonical thinking levels and openai-completions thinkingFormat names.
|
|
12
|
+
*
|
|
13
|
+
* Levels match `@deepseek-ai/dsh-llm-pi-ai` catalog.ts `THINKING_LEVELS`.
|
|
14
|
+
* Formats match `SUPPORTED_THINKING_FORMATS` in the same file (rc.8).
|
|
15
|
+
* Runtime UI prefers the live settings schema union; these lists are the
|
|
16
|
+
* fallback plus a test pin so a silent drift is a failing test, not a
|
|
17
|
+
* second hand-maintained copy nobody notices.
|
|
18
|
+
*/
|
|
19
|
+
/** Selectable reasoning levels, in pi-ai escalation order. */
|
|
20
|
+
const THINKING_LEVELS = [
|
|
21
|
+
"off",
|
|
22
|
+
"minimal",
|
|
23
|
+
"low",
|
|
24
|
+
"medium",
|
|
25
|
+
"high",
|
|
26
|
+
"xhigh",
|
|
27
|
+
"max"
|
|
28
|
+
];
|
|
29
|
+
/** Thinking levels other than Off; Off has its own tri-state control. */
|
|
30
|
+
const THINKING_LEVELS_WITHOUT_OFF = THINKING_LEVELS.filter((level) => level !== "off");
|
|
31
|
+
/**
|
|
32
|
+
* openai-completions thinkingFormat values from llm-pi-ai catalog.ts rc.8.
|
|
33
|
+
* Tests pin this list; the settings page prefers schema union choices.
|
|
34
|
+
*/
|
|
35
|
+
const FALLBACK_THINKING_FORMATS = [
|
|
36
|
+
"openai",
|
|
37
|
+
"deepseek",
|
|
38
|
+
"openrouter",
|
|
39
|
+
"together",
|
|
40
|
+
"zai",
|
|
41
|
+
"qwen",
|
|
42
|
+
"chat-template",
|
|
43
|
+
"qwen-chat-template",
|
|
44
|
+
"string-thinking",
|
|
45
|
+
"ant-ling"
|
|
46
|
+
];
|
|
47
|
+
/** Wire protocol this plugin's v1 editor supports. */
|
|
48
|
+
const OPENAI_COMPLETIONS = "openai-completions";
|
|
49
|
+
/** Settings namespace this plugin writes. */
|
|
50
|
+
const LLM_PI_AI_NS = "llm-pi-ai";
|
|
51
|
+
/** Any route key walks a dict schema to the same profile node. */
|
|
52
|
+
const SCHEMA_PROBE_ROUTE = "\0probe";
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/core/paths.ts
|
|
55
|
+
/**
|
|
56
|
+
* Nested get/has over plain JSON, matching ui-settings SettingsSchemaService
|
|
57
|
+
* for the paths this plugin needs. Kept in-repo so tests do not import a
|
|
58
|
+
* Cordis service.
|
|
59
|
+
*/
|
|
60
|
+
/** Read a nested value by string keys / array indexes. */
|
|
61
|
+
function getPath(value, path) {
|
|
62
|
+
let current = value;
|
|
63
|
+
for (const key of path) {
|
|
64
|
+
if (Array.isArray(current)) {
|
|
65
|
+
current = current[Number(key)];
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (typeof current !== "object" || current === null) return void 0;
|
|
69
|
+
current = current[key];
|
|
70
|
+
}
|
|
71
|
+
return current;
|
|
72
|
+
}
|
|
73
|
+
/** Plain object (not array, not null). */
|
|
74
|
+
function isPlainObject(value) {
|
|
75
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
76
|
+
}
|
|
77
|
+
/** Structured clone of a JSON object, or `{}` when the source is not one. */
|
|
78
|
+
function cloneObject(value) {
|
|
79
|
+
return isPlainObject(value) ? structuredClone(value) : {};
|
|
80
|
+
}
|
|
81
|
+
/** Structured clone of a models array; non-arrays and non-object rows are skipped. */
|
|
82
|
+
function cloneModels(value) {
|
|
83
|
+
if (!Array.isArray(value)) return [];
|
|
84
|
+
return value.flatMap((row) => isPlainObject(row) ? [structuredClone(row)] : []);
|
|
85
|
+
}
|
|
86
|
+
//#endregion
|
|
87
|
+
//#region src/core/filter.ts
|
|
88
|
+
/**
|
|
89
|
+
* Resolve the wire protocol for a route.
|
|
90
|
+
* User/effective `api` wins; then schema default; then openai-completions.
|
|
91
|
+
*/
|
|
92
|
+
function resolveRouteApi(profile, schemaDefault) {
|
|
93
|
+
if (isPlainObject(profile) && typeof profile.api === "string" && profile.api.length > 0) return profile.api;
|
|
94
|
+
if (schemaDefault !== void 0 && schemaDefault.length > 0) return schemaDefault;
|
|
95
|
+
return OPENAI_COMPLETIONS;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* v1 editable routes: hand-declared llm-pi-ai openai-completions.
|
|
99
|
+
* Catalog routes are excluded (writing `models` replaces the whole catalog).
|
|
100
|
+
*/
|
|
101
|
+
function classifyRoute(entry, profile, schemaDefaultApi) {
|
|
102
|
+
if (entry.provider === "deepseek-official" || entry.settingsNs === "llm-deepseek") return {
|
|
103
|
+
editable: false,
|
|
104
|
+
reason: "official-deepseek"
|
|
105
|
+
};
|
|
106
|
+
if (entry.settingsNs !== "llm-pi-ai") return {
|
|
107
|
+
editable: false,
|
|
108
|
+
reason: "not-pi-ai"
|
|
109
|
+
};
|
|
110
|
+
if (entry.declared !== true) return {
|
|
111
|
+
editable: false,
|
|
112
|
+
reason: "catalog"
|
|
113
|
+
};
|
|
114
|
+
if (resolveRouteApi(profile, schemaDefaultApi) !== "openai-completions") return {
|
|
115
|
+
editable: false,
|
|
116
|
+
reason: "not-completions"
|
|
117
|
+
};
|
|
118
|
+
return { editable: true };
|
|
119
|
+
}
|
|
120
|
+
/** Profile object at `providers.<route>` from an llm-pi-ai section value. */
|
|
121
|
+
function profileAt(section, settingsPath, provider) {
|
|
122
|
+
return getPath(section, settingsPath !== void 0 && settingsPath.length > 0 ? settingsPath : ["providers", provider]);
|
|
123
|
+
}
|
|
124
|
+
/** String choices from a schemastery union node (`type: 'union'`). */
|
|
125
|
+
function unionStringChoices(node) {
|
|
126
|
+
if (!isPlainObject(node) || node.type !== "union" || !Array.isArray(node.list)) return [];
|
|
127
|
+
return node.list.map((entry) => isPlainObject(entry) ? entry.value : void 0).filter((value) => typeof value === "string");
|
|
128
|
+
}
|
|
129
|
+
//#endregion
|
|
130
|
+
//#region src/core/path-ops.ts
|
|
131
|
+
/**
|
|
132
|
+
* The minimal path ops carrying `after` over `before`.
|
|
133
|
+
* @param base - path of the edited subtree.
|
|
134
|
+
* @param before - subtree as loaded, or undefined when new.
|
|
135
|
+
* @param after - subtree as edited.
|
|
136
|
+
*/
|
|
137
|
+
function pathOps(base, before, after) {
|
|
138
|
+
const previous = typeof before === "object" && before !== null && !Array.isArray(before) ? before : {};
|
|
139
|
+
const ops = [];
|
|
140
|
+
for (const [key, value] of Object.entries(after)) {
|
|
141
|
+
if (JSON.stringify(previous[key]) === JSON.stringify(value)) continue;
|
|
142
|
+
ops.push({
|
|
143
|
+
op: "set",
|
|
144
|
+
path: [...base, key],
|
|
145
|
+
value
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
for (const key of Object.keys(previous)) if (!(key in after)) ops.push({
|
|
149
|
+
op: "unset",
|
|
150
|
+
path: [...base, key]
|
|
151
|
+
});
|
|
152
|
+
return ops;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Ops for one route: whole-array `models` replace when the table changed,
|
|
156
|
+
* plus one-level compat path ops. Never replace the `llm-pi-ai` section.
|
|
157
|
+
*/
|
|
158
|
+
function buildSaveOps(slices) {
|
|
159
|
+
const base = [...slices.settingsPath];
|
|
160
|
+
const ops = [];
|
|
161
|
+
if (JSON.stringify(slices.beforeModels) !== JSON.stringify(slices.afterModels)) ops.push({
|
|
162
|
+
op: "set",
|
|
163
|
+
path: [...base, "models"],
|
|
164
|
+
value: slices.afterModels
|
|
165
|
+
});
|
|
166
|
+
if (slices.afterCompat !== void 0) ops.push(...pathOps([...base, "compat"], slices.beforeCompat, slices.afterCompat));
|
|
167
|
+
return ops;
|
|
168
|
+
}
|
|
169
|
+
//#endregion
|
|
170
|
+
//#region src/core/drafts.ts
|
|
171
|
+
/**
|
|
172
|
+
* Route-card draft merge: user-layer slices, namespace revision, dirty preserve.
|
|
173
|
+
* No React — settings UI and tests share these helpers.
|
|
174
|
+
*/
|
|
175
|
+
/** Build a draft from the stored user subtree (never from effective `value`). */
|
|
176
|
+
function routeDraftFromUserProfile(args) {
|
|
177
|
+
const { provider, displayName, settingsPath, revision, userProfile } = args;
|
|
178
|
+
const models = cloneModels(isPlainObject(userProfile) ? userProfile.models : []);
|
|
179
|
+
const compatPresent = isPlainObject(userProfile) && "compat" in userProfile && isPlainObject(userProfile.compat);
|
|
180
|
+
const compat = cloneObject(isPlainObject(userProfile) ? userProfile.compat : {});
|
|
181
|
+
return {
|
|
182
|
+
provider,
|
|
183
|
+
displayName,
|
|
184
|
+
settingsPath: [...settingsPath],
|
|
185
|
+
revision,
|
|
186
|
+
models,
|
|
187
|
+
originalModels: cloneModels(models),
|
|
188
|
+
compat,
|
|
189
|
+
originalCompat: cloneObject(compat),
|
|
190
|
+
compatPresent
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
/** Dirty iff pathOps would emit something (same comparison as save). */
|
|
194
|
+
function draftDirty(draft) {
|
|
195
|
+
return buildSaveOps({
|
|
196
|
+
settingsPath: draft.settingsPath,
|
|
197
|
+
beforeModels: draft.originalModels,
|
|
198
|
+
afterModels: draft.models,
|
|
199
|
+
beforeCompat: draft.compatPresent ? draft.originalCompat : void 0,
|
|
200
|
+
afterCompat: draft.compat
|
|
201
|
+
}).length > 0;
|
|
202
|
+
}
|
|
203
|
+
/** Treat the current models/compat as the committed originals (no-op save). */
|
|
204
|
+
function alignDraft(draft) {
|
|
205
|
+
return {
|
|
206
|
+
...draft,
|
|
207
|
+
originalModels: cloneModels(draft.models),
|
|
208
|
+
originalCompat: cloneObject(draft.compat),
|
|
209
|
+
compatPresent: Object.keys(draft.compat).length > 0 ? true : draft.compatPresent
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/** Fold a successful mutate view: saved card realigns; every card gets the new revision. */
|
|
213
|
+
function applySaveSuccess(drafts, savedProvider, slice) {
|
|
214
|
+
return drafts.map((draft) => {
|
|
215
|
+
if (draft.provider !== savedProvider) return {
|
|
216
|
+
...draft,
|
|
217
|
+
revision: slice.revision
|
|
218
|
+
};
|
|
219
|
+
const userProfile = profileAt(slice.user, draft.settingsPath, draft.provider);
|
|
220
|
+
return routeDraftFromUserProfile({
|
|
221
|
+
provider: draft.provider,
|
|
222
|
+
displayName: draft.displayName,
|
|
223
|
+
settingsPath: draft.settingsPath,
|
|
224
|
+
revision: slice.revision,
|
|
225
|
+
userProfile
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Apply a freshly loaded table. Dirty cards keep models/compat; originals and
|
|
231
|
+
* revision follow the incoming snapshot so a later save is against the new user layer.
|
|
232
|
+
*/
|
|
233
|
+
function mergeLoadedDrafts(current, incoming, options) {
|
|
234
|
+
const currentByProvider = new Map(current.map((draft) => [draft.provider, draft]));
|
|
235
|
+
const conflicted = [];
|
|
236
|
+
return {
|
|
237
|
+
drafts: incoming.map((next) => {
|
|
238
|
+
const prev = currentByProvider.get(next.provider);
|
|
239
|
+
if (prev === void 0 || !options.preserveDirty || !draftDirty(prev)) return next;
|
|
240
|
+
conflicted.push(next.provider);
|
|
241
|
+
return {
|
|
242
|
+
...prev,
|
|
243
|
+
displayName: next.displayName,
|
|
244
|
+
settingsPath: next.settingsPath,
|
|
245
|
+
revision: next.revision,
|
|
246
|
+
originalModels: cloneModels(next.originalModels),
|
|
247
|
+
originalCompat: cloneObject(next.originalCompat),
|
|
248
|
+
compatPresent: next.compatPresent
|
|
249
|
+
};
|
|
250
|
+
}),
|
|
251
|
+
conflicted
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
/** Increment a generation counter; callers discard stale async settlements. */
|
|
255
|
+
function nextGeneration(holder) {
|
|
256
|
+
holder.current += 1;
|
|
257
|
+
return holder.current;
|
|
258
|
+
}
|
|
259
|
+
/** Whether `generation` is still the latest issued token. */
|
|
260
|
+
function generationIsCurrent(holder, generation) {
|
|
261
|
+
return holder.current === generation;
|
|
262
|
+
}
|
|
263
|
+
/** Keep a stored thinkingFormat visible even if the schema union omitted it. */
|
|
264
|
+
function thinkingFormatChoices(formats, current) {
|
|
265
|
+
if (typeof current === "string" && current.length > 0 && !formats.includes(current)) return [current, ...formats];
|
|
266
|
+
return [...formats];
|
|
267
|
+
}
|
|
268
|
+
//#endregion
|
|
269
|
+
//#region src/core/efforts.ts
|
|
270
|
+
/** Read Off's three states from a stored dict. */
|
|
271
|
+
function readOff(efforts) {
|
|
272
|
+
if (efforts === void 0 || !("off" in efforts) || efforts.off === void 0) return {
|
|
273
|
+
mode: "absent",
|
|
274
|
+
value: "none"
|
|
275
|
+
};
|
|
276
|
+
if (efforts.off === null) return {
|
|
277
|
+
mode: "empty",
|
|
278
|
+
value: "none"
|
|
279
|
+
};
|
|
280
|
+
return {
|
|
281
|
+
mode: "value",
|
|
282
|
+
value: efforts.off
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
/** Write Off's three states onto a dict (does not mutate the input). */
|
|
286
|
+
function writeOff(efforts, mode, value) {
|
|
287
|
+
const next = { ...efforts };
|
|
288
|
+
delete next.off;
|
|
289
|
+
if (mode === "empty") next.off = null;
|
|
290
|
+
else if (mode === "value") next.off = value.trim().length > 0 ? value : "none";
|
|
291
|
+
return next;
|
|
292
|
+
}
|
|
293
|
+
/** Whether a thinking level (other than Off) is currently declared. */
|
|
294
|
+
function hasLevel(efforts, level) {
|
|
295
|
+
return efforts !== void 0 && efforts[level] !== void 0 && efforts[level] !== null;
|
|
296
|
+
}
|
|
297
|
+
/** Toggle a non-off level. New levels default the wire spelling to the key. */
|
|
298
|
+
function toggleLevel(efforts, level, enabled, wire) {
|
|
299
|
+
const next = { ...efforts };
|
|
300
|
+
if (enabled) next[level] = wire !== void 0 && wire.length > 0 ? wire : level;
|
|
301
|
+
else delete next[level];
|
|
302
|
+
return next;
|
|
303
|
+
}
|
|
304
|
+
/** Set the wire spelling for a declared non-off level. */
|
|
305
|
+
function setWireSpelling(efforts, level, wire) {
|
|
306
|
+
if (!hasLevel(efforts, level)) return efforts;
|
|
307
|
+
return {
|
|
308
|
+
...efforts,
|
|
309
|
+
[level]: wire
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
/**
|
|
313
|
+
* Validate a reasoningEfforts field. Returns an error code; never throws.
|
|
314
|
+
* Absence / `false` are valid (default-off / catalog strip). Empty dict and
|
|
315
|
+
* off-only are rejected by the official resolver.
|
|
316
|
+
*/
|
|
317
|
+
function validateReasoningEfforts(efforts) {
|
|
318
|
+
if (efforts === void 0 || efforts === false) return void 0;
|
|
319
|
+
if (efforts === null || typeof efforts === "object" && !Array.isArray(efforts) && Object.keys(efforts).length === 0) return "empty";
|
|
320
|
+
if (typeof efforts !== "object" || Array.isArray(efforts)) return "empty";
|
|
321
|
+
const record = efforts;
|
|
322
|
+
const known = new Set(THINKING_LEVELS);
|
|
323
|
+
for (const key of Object.keys(record)) if (!known.has(key)) return "bad-wire";
|
|
324
|
+
const declared = THINKING_LEVELS.flatMap((level) => {
|
|
325
|
+
if (!(level in record) || record[level] === void 0) return [];
|
|
326
|
+
return [[level, record[level]]];
|
|
327
|
+
});
|
|
328
|
+
for (const [level, wire] of declared) if (wire === null) {
|
|
329
|
+
if (level !== "off") return "bad-wire";
|
|
330
|
+
} else if (typeof wire !== "string" || wire.length === 0) return "bad-wire";
|
|
331
|
+
if (!declared.some(([level]) => level !== "off")) return "off-only";
|
|
332
|
+
}
|
|
333
|
+
/** Drop reasoningEfforts from a model row; keep every other field. */
|
|
334
|
+
function clearReasoningEfforts(row) {
|
|
335
|
+
const next = { ...row };
|
|
336
|
+
delete next.reasoningEfforts;
|
|
337
|
+
return next;
|
|
338
|
+
}
|
|
339
|
+
/** Read reasoningEfforts from a model row. `false` is treated as absent for the editor. */
|
|
340
|
+
function readEfforts(row) {
|
|
341
|
+
const value = row.reasoningEfforts;
|
|
342
|
+
if (value === void 0 || value === false || value === null) return void 0;
|
|
343
|
+
if (typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
344
|
+
return value;
|
|
345
|
+
}
|
|
346
|
+
/** Put a dict (or omit the field) onto a spread row. */
|
|
347
|
+
function writeEfforts(row, efforts) {
|
|
348
|
+
const next = { ...row };
|
|
349
|
+
if (efforts === void 0 || Object.keys(efforts).length === 0) {
|
|
350
|
+
delete next.reasoningEfforts;
|
|
351
|
+
return next;
|
|
352
|
+
}
|
|
353
|
+
next.reasoningEfforts = efforts;
|
|
354
|
+
return next;
|
|
355
|
+
}
|
|
356
|
+
const PRESETS = {
|
|
357
|
+
deepseek: {
|
|
358
|
+
id: "deepseek",
|
|
359
|
+
efforts: {
|
|
360
|
+
off: null,
|
|
361
|
+
low: "low",
|
|
362
|
+
high: "high",
|
|
363
|
+
max: "max"
|
|
364
|
+
},
|
|
365
|
+
compat: {
|
|
366
|
+
thinkingFormat: "set",
|
|
367
|
+
thinkingFormatValue: "deepseek",
|
|
368
|
+
supportsDeveloperRole: "set-false",
|
|
369
|
+
supportsReasoningEffort: "unset"
|
|
370
|
+
},
|
|
371
|
+
warnSameWire: false
|
|
372
|
+
},
|
|
373
|
+
openai: {
|
|
374
|
+
id: "openai",
|
|
375
|
+
efforts: {
|
|
376
|
+
minimal: "minimal",
|
|
377
|
+
low: "low",
|
|
378
|
+
medium: "medium",
|
|
379
|
+
high: "high"
|
|
380
|
+
},
|
|
381
|
+
compat: {
|
|
382
|
+
thinkingFormat: "unset",
|
|
383
|
+
supportsDeveloperRole: "unset",
|
|
384
|
+
supportsReasoningEffort: "unset"
|
|
385
|
+
},
|
|
386
|
+
warnSameWire: false
|
|
387
|
+
},
|
|
388
|
+
toggle: {
|
|
389
|
+
id: "toggle",
|
|
390
|
+
efforts: {
|
|
391
|
+
off: null,
|
|
392
|
+
high: "high"
|
|
393
|
+
},
|
|
394
|
+
compat: {
|
|
395
|
+
thinkingFormat: "unset",
|
|
396
|
+
supportsDeveloperRole: "unset",
|
|
397
|
+
supportsReasoningEffort: "set-false"
|
|
398
|
+
},
|
|
399
|
+
warnSameWire: true
|
|
400
|
+
}
|
|
401
|
+
};
|
|
402
|
+
/** Apply a preset's efforts onto every model row (spread, keep other fields). */
|
|
403
|
+
function applyPresetEfforts(models, preset) {
|
|
404
|
+
return models.map((row) => ({
|
|
405
|
+
...row,
|
|
406
|
+
reasoningEfforts: { ...preset.efforts }
|
|
407
|
+
}));
|
|
408
|
+
}
|
|
409
|
+
/** Merge a preset's compat patch onto the current route-level compat object. */
|
|
410
|
+
function applyPresetCompat(compat, preset) {
|
|
411
|
+
const next = { ...compat };
|
|
412
|
+
const { compat: patch } = preset;
|
|
413
|
+
if (patch.thinkingFormat === "unset") delete next.thinkingFormat;
|
|
414
|
+
else if (patch.thinkingFormat === "set" && patch.thinkingFormatValue !== void 0) next.thinkingFormat = patch.thinkingFormatValue;
|
|
415
|
+
if (patch.supportsDeveloperRole === "unset") delete next.supportsDeveloperRole;
|
|
416
|
+
else if (patch.supportsDeveloperRole === "set-false") next.supportsDeveloperRole = false;
|
|
417
|
+
if (patch.supportsReasoningEffort === "unset") delete next.supportsReasoningEffort;
|
|
418
|
+
else if (patch.supportsReasoningEffort === "set-false") next.supportsReasoningEffort = false;
|
|
419
|
+
return next;
|
|
420
|
+
}
|
|
421
|
+
//#endregion
|
|
422
|
+
//#region src/core/validate.ts
|
|
423
|
+
/** Per-model client-side check used to show errors without throwing. */
|
|
424
|
+
function modelEffortError(row) {
|
|
425
|
+
if (!("reasoningEfforts" in row) || row.reasoningEfforts === void 0) return void 0;
|
|
426
|
+
return validateReasoningEfforts(row.reasoningEfforts);
|
|
427
|
+
}
|
|
428
|
+
//#endregion
|
|
429
|
+
//#region src/client/load-drafts.ts
|
|
430
|
+
function schemaDefaultString(node) {
|
|
431
|
+
if (!isPlainObject(node) || !isPlainObject(node.meta)) return void 0;
|
|
432
|
+
return typeof node.meta.default === "string" ? node.meta.default : void 0;
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* First paint: `ensure()` (reads only from idle). Never treat ensure as refresh.
|
|
436
|
+
* Callers that must not apply a stale settlement compare generation themselves.
|
|
437
|
+
*/
|
|
438
|
+
async function loadDrafts(api, describe, schema) {
|
|
439
|
+
await describe.ensure();
|
|
440
|
+
const mirrored = describe.getSnapshot();
|
|
441
|
+
if (mirrored.view === void 0) return {
|
|
442
|
+
writable: false,
|
|
443
|
+
formats: [...FALLBACK_THINKING_FORMATS],
|
|
444
|
+
drafts: [],
|
|
445
|
+
error: mirrored.error ?? void 0
|
|
446
|
+
};
|
|
447
|
+
const providersResponse = await api.llm.providers({});
|
|
448
|
+
if (!providersResponse.result.ok) return {
|
|
449
|
+
writable: mirrored.view.writable,
|
|
450
|
+
formats: [...FALLBACK_THINKING_FORMATS],
|
|
451
|
+
drafts: [],
|
|
452
|
+
error: providersResponse.result.error.message
|
|
453
|
+
};
|
|
454
|
+
const pi = new Map(mirrored.view.namespaces.map((view) => [view.ns, view])).get(LLM_PI_AI_NS);
|
|
455
|
+
let formats = [...FALLBACK_THINKING_FORMATS];
|
|
456
|
+
let schemaDefaultApi;
|
|
457
|
+
if (pi !== void 0) try {
|
|
458
|
+
const root = schema.rehydrate(pi.schema);
|
|
459
|
+
const fromSchema = unionStringChoices(schema.nodeAtPath(root, [
|
|
460
|
+
"providers",
|
|
461
|
+
SCHEMA_PROBE_ROUTE,
|
|
462
|
+
"compat",
|
|
463
|
+
"thinkingFormat"
|
|
464
|
+
]));
|
|
465
|
+
if (fromSchema.length > 0) formats = fromSchema;
|
|
466
|
+
schemaDefaultApi = schemaDefaultString(schema.nodeAtPath(root, [
|
|
467
|
+
"providers",
|
|
468
|
+
SCHEMA_PROBE_ROUTE,
|
|
469
|
+
"api"
|
|
470
|
+
]));
|
|
471
|
+
} catch {}
|
|
472
|
+
const drafts = [];
|
|
473
|
+
for (const entry of providersResponse.result.value.providers) {
|
|
474
|
+
const settingsPath = entry.settingsPath !== void 0 && entry.settingsPath.length > 0 ? [...entry.settingsPath] : ["providers", entry.provider];
|
|
475
|
+
if (!classifyRoute(entry, profileAt(pi?.value, settingsPath, entry.provider), schemaDefaultApi).editable) continue;
|
|
476
|
+
drafts.push(routeDraftFromUserProfile({
|
|
477
|
+
provider: entry.provider,
|
|
478
|
+
displayName: entry.displayName ?? entry.provider,
|
|
479
|
+
settingsPath,
|
|
480
|
+
revision: pi?.revision ?? 0,
|
|
481
|
+
userProfile: profileAt(pi?.user, settingsPath, entry.provider)
|
|
482
|
+
}));
|
|
483
|
+
}
|
|
484
|
+
return {
|
|
485
|
+
writable: mirrored.view.writable,
|
|
486
|
+
formats,
|
|
487
|
+
drafts
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
//#endregion
|
|
491
|
+
//#region \0dsh-css:C:\Users\zimo\AppData\Roaming\io.github.hairyf.deepseek-harness-desktop\data\dsh\临时目录\dsh-plugin-effort-declare\src\client\effort-declare.module.css.mjs
|
|
492
|
+
const cssText = ".hYEBUa_section{max-width:720px;color:var(--dsw-alias-label-primary);flex-direction:column;gap:12px;display:flex}.hYEBUa_title{color:var(--dsw-alias-label-primary);margin:0;font-size:16px;font-weight:500;line-height:24px}.hYEBUa_intro{color:var(--dsw-alias-label-tertiary);margin:0;font-size:14px;line-height:22px}.hYEBUa_notice{color:var(--dsw-alias-state-warn-label);margin:0;font-size:12px;line-height:18px}.hYEBUa_savedNotice{color:var(--dsw-alias-state-success-primary);margin:0;font-size:12px;line-height:18px}.hYEBUa_error{color:var(--dsw-alias-state-error-primary);margin:0;font-size:12px;line-height:18px}.hYEBUa_rows{flex-direction:column;gap:8px;margin:12px 0 0;padding:0;list-style:none;display:flex}.hYEBUa_rowCard{border:1px solid var(--dsw-alias-border-l2);border-radius:12px;flex-direction:column;gap:12px;padding:12px 14px;display:flex}.hYEBUa_rowHead{align-items:baseline;gap:8px;display:flex}.hYEBUa_rowName{color:var(--dsw-alias-label-primary);font-size:14px;font-weight:500;line-height:22px}.hYEBUa_rowTag{border:1px solid var(--dsw-alias-border-l3);color:var(--dsw-alias-label-secondary);border-radius:4px;flex:none;padding:1px 6px;font-size:11px;line-height:16px}.hYEBUa_compatSummary{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:18px}.hYEBUa_presetRow,.hYEBUa_actions{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.hYEBUa_fieldLabel{color:var(--dsw-alias-label-secondary);font-size:12px;font-weight:500;line-height:18px}.hYEBUa_primaryButton,.hYEBUa_secondaryButton{box-sizing:border-box;height:36px;font:inherit;cursor:pointer;border-radius:18px;justify-content:center;align-items:center;padding:0 14px;font-size:14px;line-height:22px;display:inline-flex}.hYEBUa_primaryButton{background:var(--dsw-alias-button-primary-fill);color:var(--dsw-alias-label-primary-foreground);border:none}.hYEBUa_primaryButton:hover:not(:disabled){background:var(--dsw-alias-button-primary-hover)}.hYEBUa_secondaryButton{border:1px solid var(--dsw-alias-border-l2);color:var(--dsw-alias-label-primary);background:0 0}.hYEBUa_secondaryButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover-solid)}.hYEBUa_primaryButton:disabled,.hYEBUa_secondaryButton:disabled,.hYEBUa_linkButton:disabled,.hYEBUa_input:disabled{opacity:.4;cursor:default}.hYEBUa_primaryButton:focus-visible,.hYEBUa_secondaryButton:focus-visible,.hYEBUa_linkButton:focus-visible,.hYEBUa_input:focus-visible{box-shadow:0 0 0 2px var(--dsw-alias-border-l3);outline:none}.hYEBUa_linkButton{box-sizing:border-box;height:28px;color:var(--dsw-alias-label-tertiary);font:inherit;cursor:pointer;background:0 0;border:none;border-radius:14px;align-items:center;padding:0 10px;font-size:12px;line-height:18px;display:inline-flex}.hYEBUa_linkButton:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover);color:var(--dsw-alias-label-secondary)}.hYEBUa_modelEntry{border-top:1px solid var(--dsw-alias-border-l2);flex-direction:column;gap:8px;padding:10px 0;display:flex}.hYEBUa_modelHead{flex-wrap:wrap;align-items:baseline;gap:8px;display:flex}.hYEBUa_modelId{font-size:13px;font-weight:500;line-height:20px}.hYEBUa_modelName{color:var(--dsw-alias-label-tertiary);font-size:12px;line-height:18px}.hYEBUa_levels{flex-wrap:wrap;gap:10px 14px;display:flex}.hYEBUa_level{align-items:center;gap:6px;font-size:12px;line-height:18px;display:inline-flex}.hYEBUa_wireRow{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.hYEBUa_input,.hYEBUa_selectInput{box-sizing:border-box;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-module-platform);height:32px;color:var(--dsw-alias-label-primary);font:inherit;border-radius:8px;padding:0 10px;font-size:13px}.hYEBUa_wireInput{width:7em}.hYEBUa_offGroup{flex-direction:column;gap:6px;display:flex}.hYEBUa_advanced{border-top:1px solid var(--dsw-alias-border-l2);padding-top:8px}.hYEBUa_advanced summary{cursor:pointer;color:var(--dsw-alias-label-secondary);font-size:12px;line-height:18px}.hYEBUa_advancedBody{flex-direction:column;gap:10px;padding-top:10px;display:flex}.hYEBUa_check{align-items:flex-start;gap:8px;font-size:12px;line-height:18px;display:flex}";
|
|
493
|
+
const cssTagId = "dsh-plugin-effort-declare/effort-declare.module.css";
|
|
494
|
+
var effort_declare_module_css_default = {
|
|
495
|
+
"actions": "hYEBUa_actions",
|
|
496
|
+
"advanced": "hYEBUa_advanced",
|
|
497
|
+
"advancedBody": "hYEBUa_advancedBody",
|
|
498
|
+
"check": "hYEBUa_check",
|
|
499
|
+
"compatSummary": "hYEBUa_compatSummary",
|
|
500
|
+
"error": "hYEBUa_error",
|
|
501
|
+
"fieldLabel": "hYEBUa_fieldLabel",
|
|
502
|
+
"input": "hYEBUa_input",
|
|
503
|
+
"intro": "hYEBUa_intro",
|
|
504
|
+
"level": "hYEBUa_level",
|
|
505
|
+
"levels": "hYEBUa_levels",
|
|
506
|
+
"linkButton": "hYEBUa_linkButton",
|
|
507
|
+
"modelEntry": "hYEBUa_modelEntry",
|
|
508
|
+
"modelHead": "hYEBUa_modelHead",
|
|
509
|
+
"modelId": "hYEBUa_modelId",
|
|
510
|
+
"modelName": "hYEBUa_modelName",
|
|
511
|
+
"notice": "hYEBUa_notice",
|
|
512
|
+
"offGroup": "hYEBUa_offGroup",
|
|
513
|
+
"presetRow": "hYEBUa_presetRow",
|
|
514
|
+
"primaryButton": "hYEBUa_primaryButton",
|
|
515
|
+
"rowCard": "hYEBUa_rowCard",
|
|
516
|
+
"rowHead": "hYEBUa_rowHead",
|
|
517
|
+
"rowName": "hYEBUa_rowName",
|
|
518
|
+
"rowTag": "hYEBUa_rowTag",
|
|
519
|
+
"rows": "hYEBUa_rows",
|
|
520
|
+
"savedNotice": "hYEBUa_savedNotice",
|
|
521
|
+
"secondaryButton": "hYEBUa_secondaryButton",
|
|
522
|
+
"section": "hYEBUa_section",
|
|
523
|
+
"selectInput": "hYEBUa_selectInput",
|
|
524
|
+
"title": "hYEBUa_title",
|
|
525
|
+
"wireInput": "hYEBUa_wireInput",
|
|
526
|
+
"wireRow": "hYEBUa_wireRow"
|
|
527
|
+
};
|
|
528
|
+
//#endregion
|
|
529
|
+
//#region src/client/EffortDeclareSection.tsx
|
|
530
|
+
/**
|
|
531
|
+
* Settings section: per-model reasoningEfforts + openai-completions compat
|
|
532
|
+
* for hand-declared llm-pi-ai routes.
|
|
533
|
+
*/
|
|
534
|
+
function compatSummary(compat) {
|
|
535
|
+
const parts = [];
|
|
536
|
+
if (typeof compat.thinkingFormat === "string") parts.push(`thinkingFormat=${compat.thinkingFormat}`);
|
|
537
|
+
if (compat.supportsDeveloperRole === false) parts.push("supportsDeveloperRole=false");
|
|
538
|
+
if (compat.supportsReasoningEffort === false) parts.push("supportsReasoningEffort=false");
|
|
539
|
+
return parts.join(" · ");
|
|
540
|
+
}
|
|
541
|
+
function errorText(code, t) {
|
|
542
|
+
if (code === "empty") return t("errorEmpty");
|
|
543
|
+
if (code === "off-only") return t("errorOffOnly");
|
|
544
|
+
if (code === "bad-wire") return t("errorBadWire");
|
|
545
|
+
}
|
|
546
|
+
function ModelRowEditor(props) {
|
|
547
|
+
const { row, disabled, t, onChange } = props;
|
|
548
|
+
const efforts = readEfforts(row) ?? {};
|
|
549
|
+
const off = readOff(readEfforts(row));
|
|
550
|
+
const id = typeof row.id === "string" ? row.id : "";
|
|
551
|
+
const name = typeof row.name === "string" ? row.name : "";
|
|
552
|
+
const patchEfforts = (next) => {
|
|
553
|
+
onChange(writeEfforts(row, Object.keys(next).length === 0 ? void 0 : next));
|
|
554
|
+
};
|
|
555
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
556
|
+
className: effort_declare_module_css_default.modelEntry,
|
|
557
|
+
children: [
|
|
558
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
559
|
+
className: effort_declare_module_css_default.modelHead,
|
|
560
|
+
children: [
|
|
561
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
562
|
+
className: effort_declare_module_css_default.modelId,
|
|
563
|
+
children: id || t("model")
|
|
564
|
+
}),
|
|
565
|
+
name !== "" && name !== id ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
566
|
+
className: effort_declare_module_css_default.modelName,
|
|
567
|
+
children: name
|
|
568
|
+
}) : null,
|
|
569
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
570
|
+
type: "button",
|
|
571
|
+
className: effort_declare_module_css_default.linkButton,
|
|
572
|
+
disabled: disabled || !("reasoningEfforts" in row),
|
|
573
|
+
onClick: () => {
|
|
574
|
+
onChange(clearReasoningEfforts(row));
|
|
575
|
+
},
|
|
576
|
+
children: t("clear")
|
|
577
|
+
})
|
|
578
|
+
]
|
|
579
|
+
}),
|
|
580
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
581
|
+
className: effort_declare_module_css_default.fieldLabel,
|
|
582
|
+
children: t("levels")
|
|
583
|
+
}),
|
|
584
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
585
|
+
className: effort_declare_module_css_default.levels,
|
|
586
|
+
children: THINKING_LEVELS_WITHOUT_OFF.map((level) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
587
|
+
className: effort_declare_module_css_default.level,
|
|
588
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
589
|
+
type: "checkbox",
|
|
590
|
+
checked: hasLevel(efforts, level),
|
|
591
|
+
disabled,
|
|
592
|
+
onChange: (event) => {
|
|
593
|
+
patchEfforts(toggleLevel(efforts, level, event.target.checked));
|
|
594
|
+
}
|
|
595
|
+
}), level]
|
|
596
|
+
}, level))
|
|
597
|
+
}),
|
|
598
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
599
|
+
className: effort_declare_module_css_default.wireRow,
|
|
600
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
601
|
+
className: effort_declare_module_css_default.fieldLabel,
|
|
602
|
+
children: t("wire")
|
|
603
|
+
}), THINKING_LEVELS_WITHOUT_OFF.filter((level) => hasLevel(efforts, level)).map((level) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
604
|
+
className: effort_declare_module_css_default.level,
|
|
605
|
+
children: [level, /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
606
|
+
className: `${effort_declare_module_css_default.input} ${effort_declare_module_css_default.wireInput}`,
|
|
607
|
+
type: "text",
|
|
608
|
+
value: efforts[level] ?? level,
|
|
609
|
+
disabled,
|
|
610
|
+
"aria-label": `${t("wire")} ${level}`,
|
|
611
|
+
onChange: (event) => {
|
|
612
|
+
patchEfforts(setWireSpelling(efforts, level, event.target.value));
|
|
613
|
+
}
|
|
614
|
+
})]
|
|
615
|
+
}, level))]
|
|
616
|
+
}),
|
|
617
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
618
|
+
className: effort_declare_module_css_default.offGroup,
|
|
619
|
+
children: [
|
|
620
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
621
|
+
className: effort_declare_module_css_default.fieldLabel,
|
|
622
|
+
children: t("offMode")
|
|
623
|
+
}),
|
|
624
|
+
[
|
|
625
|
+
"absent",
|
|
626
|
+
"empty",
|
|
627
|
+
"value"
|
|
628
|
+
].map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
629
|
+
className: effort_declare_module_css_default.level,
|
|
630
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
631
|
+
type: "radio",
|
|
632
|
+
name: props.radioName,
|
|
633
|
+
checked: off.mode === mode,
|
|
634
|
+
disabled,
|
|
635
|
+
onChange: () => {
|
|
636
|
+
patchEfforts(writeOff(efforts, mode, off.value));
|
|
637
|
+
}
|
|
638
|
+
}), mode === "absent" ? t("offAbsent") : mode === "empty" ? t("offEmpty") : t("offValue")]
|
|
639
|
+
}, mode)),
|
|
640
|
+
off.mode === "value" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
641
|
+
className: `${effort_declare_module_css_default.input} ${effort_declare_module_css_default.wireInput}`,
|
|
642
|
+
type: "text",
|
|
643
|
+
value: off.value,
|
|
644
|
+
placeholder: t("offValuePlaceholder"),
|
|
645
|
+
disabled,
|
|
646
|
+
onChange: (event) => {
|
|
647
|
+
patchEfforts(writeOff(efforts, "value", event.target.value));
|
|
648
|
+
}
|
|
649
|
+
}) : null
|
|
650
|
+
]
|
|
651
|
+
})
|
|
652
|
+
]
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
function RouteCard(props) {
|
|
656
|
+
const { draft, writable, busy, reloading, t, onChange } = props;
|
|
657
|
+
const noModels = draft.models.length === 0;
|
|
658
|
+
const editDisabled = !writable || busy || noModels;
|
|
659
|
+
const saveLocked = editDisabled || reloading;
|
|
660
|
+
const formats = thinkingFormatChoices(props.formats, draft.compat.thinkingFormat);
|
|
661
|
+
const summary = compatSummary(draft.compat);
|
|
662
|
+
const sameWire = draft.compat.supportsReasoningEffort === false;
|
|
663
|
+
const clientError = draft.models.map((row) => errorText(modelEffortError(row), t)).find((text) => text !== void 0);
|
|
664
|
+
const dirty = draftDirty(draft);
|
|
665
|
+
const applyPreset = (id) => {
|
|
666
|
+
const preset = PRESETS[id];
|
|
667
|
+
onChange({
|
|
668
|
+
...draft,
|
|
669
|
+
models: applyPresetEfforts(draft.models, preset),
|
|
670
|
+
compat: applyPresetCompat(draft.compat, preset)
|
|
671
|
+
});
|
|
672
|
+
};
|
|
673
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("li", {
|
|
674
|
+
className: effort_declare_module_css_default.rowCard,
|
|
675
|
+
children: [
|
|
676
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
677
|
+
className: effort_declare_module_css_default.rowHead,
|
|
678
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
679
|
+
className: effort_declare_module_css_default.rowName,
|
|
680
|
+
children: draft.displayName
|
|
681
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
682
|
+
className: effort_declare_module_css_default.rowTag,
|
|
683
|
+
children: draft.provider
|
|
684
|
+
})]
|
|
685
|
+
}),
|
|
686
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", {
|
|
687
|
+
className: effort_declare_module_css_default.compatSummary,
|
|
688
|
+
children: [t("compatSummary"), summary === "" ? "" : `: ${summary}`]
|
|
689
|
+
}),
|
|
690
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
691
|
+
className: effort_declare_module_css_default.presetRow,
|
|
692
|
+
children: [
|
|
693
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
694
|
+
className: effort_declare_module_css_default.fieldLabel,
|
|
695
|
+
children: t("presets")
|
|
696
|
+
}),
|
|
697
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
698
|
+
type: "button",
|
|
699
|
+
className: effort_declare_module_css_default.secondaryButton,
|
|
700
|
+
disabled: editDisabled,
|
|
701
|
+
onClick: () => {
|
|
702
|
+
applyPreset("deepseek");
|
|
703
|
+
},
|
|
704
|
+
children: t("presetDeepSeek")
|
|
705
|
+
}),
|
|
706
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
707
|
+
type: "button",
|
|
708
|
+
className: effort_declare_module_css_default.secondaryButton,
|
|
709
|
+
disabled: editDisabled,
|
|
710
|
+
onClick: () => {
|
|
711
|
+
applyPreset("openai");
|
|
712
|
+
},
|
|
713
|
+
children: t("presetOpenAI")
|
|
714
|
+
}),
|
|
715
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
716
|
+
type: "button",
|
|
717
|
+
className: effort_declare_module_css_default.secondaryButton,
|
|
718
|
+
disabled: editDisabled,
|
|
719
|
+
onClick: () => {
|
|
720
|
+
applyPreset("toggle");
|
|
721
|
+
},
|
|
722
|
+
children: t("presetToggle")
|
|
723
|
+
})
|
|
724
|
+
]
|
|
725
|
+
}),
|
|
726
|
+
sameWire ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
727
|
+
className: effort_declare_module_css_default.notice,
|
|
728
|
+
children: t("presetToggleWarn")
|
|
729
|
+
}) : null,
|
|
730
|
+
noModels ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
731
|
+
className: effort_declare_module_css_default.notice,
|
|
732
|
+
children: t("noModels")
|
|
733
|
+
}) : null,
|
|
734
|
+
draft.models.map((row, index) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModelRowEditor, {
|
|
735
|
+
row,
|
|
736
|
+
index,
|
|
737
|
+
radioName: `off-${draft.provider}-${String(index)}`,
|
|
738
|
+
disabled: editDisabled,
|
|
739
|
+
t,
|
|
740
|
+
onChange: (next) => {
|
|
741
|
+
const models = draft.models.map((candidate, at) => at === index ? next : candidate);
|
|
742
|
+
onChange({
|
|
743
|
+
...draft,
|
|
744
|
+
models
|
|
745
|
+
});
|
|
746
|
+
}
|
|
747
|
+
}, `${String(row.id)}-${String(index)}`)),
|
|
748
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("details", {
|
|
749
|
+
className: effort_declare_module_css_default.advanced,
|
|
750
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("summary", { children: t("advanced") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
751
|
+
className: effort_declare_module_css_default.advancedBody,
|
|
752
|
+
children: [
|
|
753
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
754
|
+
className: effort_declare_module_css_default.fieldLabel,
|
|
755
|
+
children: [t("thinkingFormat"), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
|
|
756
|
+
className: `${effort_declare_module_css_default.input} ${effort_declare_module_css_default.selectInput}`,
|
|
757
|
+
value: typeof draft.compat.thinkingFormat === "string" ? draft.compat.thinkingFormat : "",
|
|
758
|
+
disabled: editDisabled,
|
|
759
|
+
onChange: (event) => {
|
|
760
|
+
const compat = { ...draft.compat };
|
|
761
|
+
if (event.target.value === "") delete compat.thinkingFormat;
|
|
762
|
+
else compat.thinkingFormat = event.target.value;
|
|
763
|
+
onChange({
|
|
764
|
+
...draft,
|
|
765
|
+
compat
|
|
766
|
+
});
|
|
767
|
+
},
|
|
768
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
769
|
+
value: "",
|
|
770
|
+
children: t("thinkingFormatDefault")
|
|
771
|
+
}), formats.map((format) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
|
|
772
|
+
value: format,
|
|
773
|
+
children: format
|
|
774
|
+
}, format))]
|
|
775
|
+
})]
|
|
776
|
+
}),
|
|
777
|
+
draft.compat.supportsDeveloperRole === true ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
778
|
+
className: effort_declare_module_css_default.notice,
|
|
779
|
+
children: t("developerTrueHint")
|
|
780
|
+
}) : null,
|
|
781
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
782
|
+
className: effort_declare_module_css_default.check,
|
|
783
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
784
|
+
type: "checkbox",
|
|
785
|
+
checked: draft.compat.supportsDeveloperRole === false,
|
|
786
|
+
disabled: editDisabled,
|
|
787
|
+
onChange: (event) => {
|
|
788
|
+
const compat = { ...draft.compat };
|
|
789
|
+
if (event.target.checked) compat.supportsDeveloperRole = false;
|
|
790
|
+
else delete compat.supportsDeveloperRole;
|
|
791
|
+
onChange({
|
|
792
|
+
...draft,
|
|
793
|
+
compat
|
|
794
|
+
});
|
|
795
|
+
}
|
|
796
|
+
}), t("supportsDeveloperRole")]
|
|
797
|
+
}),
|
|
798
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
|
|
799
|
+
className: effort_declare_module_css_default.check,
|
|
800
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
801
|
+
type: "checkbox",
|
|
802
|
+
checked: draft.compat.supportsReasoningEffort === false,
|
|
803
|
+
disabled: editDisabled,
|
|
804
|
+
onChange: (event) => {
|
|
805
|
+
const compat = { ...draft.compat };
|
|
806
|
+
if (event.target.checked) compat.supportsReasoningEffort = false;
|
|
807
|
+
else delete compat.supportsReasoningEffort;
|
|
808
|
+
onChange({
|
|
809
|
+
...draft,
|
|
810
|
+
compat
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
}), t("supportsReasoningEffort")]
|
|
814
|
+
})
|
|
815
|
+
]
|
|
816
|
+
})]
|
|
817
|
+
}),
|
|
818
|
+
clientError !== void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
819
|
+
className: effort_declare_module_css_default.error,
|
|
820
|
+
children: clientError
|
|
821
|
+
}) : null,
|
|
822
|
+
props.notice?.kind === "saved" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
823
|
+
className: effort_declare_module_css_default.savedNotice,
|
|
824
|
+
children: props.notice.text
|
|
825
|
+
}) : null,
|
|
826
|
+
props.notice?.kind === "conflict" || props.notice?.kind === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
827
|
+
className: effort_declare_module_css_default.error,
|
|
828
|
+
children: props.notice.text
|
|
829
|
+
}) : null,
|
|
830
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
831
|
+
className: effort_declare_module_css_default.actions,
|
|
832
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
833
|
+
type: "button",
|
|
834
|
+
className: effort_declare_module_css_default.secondaryButton,
|
|
835
|
+
disabled: busy || !dirty,
|
|
836
|
+
onClick: () => {
|
|
837
|
+
props.onCancel(draft);
|
|
838
|
+
},
|
|
839
|
+
children: t("cancel")
|
|
840
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
841
|
+
type: "button",
|
|
842
|
+
className: effort_declare_module_css_default.primaryButton,
|
|
843
|
+
disabled: saveLocked || !dirty || clientError !== void 0,
|
|
844
|
+
onClick: () => {
|
|
845
|
+
props.onSave(draft);
|
|
846
|
+
},
|
|
847
|
+
children: busy ? t("saving") : t("save")
|
|
848
|
+
})]
|
|
849
|
+
})
|
|
850
|
+
]
|
|
851
|
+
});
|
|
852
|
+
}
|
|
853
|
+
function EffortDeclareSection(props) {
|
|
854
|
+
const t = props.t;
|
|
855
|
+
const api = props.api;
|
|
856
|
+
const describe = props.describe;
|
|
857
|
+
const schema = props.schema;
|
|
858
|
+
const [status, setStatus] = (0, react.useState)("loading");
|
|
859
|
+
const [error, setError] = (0, react.useState)("");
|
|
860
|
+
const [writable, setWritable] = (0, react.useState)(false);
|
|
861
|
+
const [formats, setFormats] = (0, react.useState)([]);
|
|
862
|
+
const [drafts, setDrafts] = (0, react.useState)([]);
|
|
863
|
+
const [busyRoute, setBusyRoute] = (0, react.useState)(null);
|
|
864
|
+
const [notices, setNotices] = (0, react.useState)({});
|
|
865
|
+
const generationRef = (0, react.useRef)(0);
|
|
866
|
+
const draftsRef = (0, react.useRef)(drafts);
|
|
867
|
+
draftsRef.current = drafts;
|
|
868
|
+
const reload = (0, react.useCallback)((preserveDirty) => {
|
|
869
|
+
if (api === void 0 || describe === void 0 || schema === void 0) {
|
|
870
|
+
setStatus("error");
|
|
871
|
+
setError(t("loadError"));
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
const generation = nextGeneration(generationRef);
|
|
875
|
+
setStatus("loading");
|
|
876
|
+
setError("");
|
|
877
|
+
loadDrafts(api, describe, schema).then((result) => {
|
|
878
|
+
if (!generationIsCurrent(generationRef, generation)) return;
|
|
879
|
+
setWritable(result.writable);
|
|
880
|
+
if (result.formats.length > 0) setFormats(result.formats);
|
|
881
|
+
if (result.error !== void 0) {
|
|
882
|
+
setStatus("error");
|
|
883
|
+
setError(result.error);
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
const merged = mergeLoadedDrafts(draftsRef.current, result.drafts, { preserveDirty });
|
|
887
|
+
setDrafts(merged.drafts);
|
|
888
|
+
if (merged.conflicted.length > 0) setNotices(Object.fromEntries(merged.conflicted.map((provider) => [provider, {
|
|
889
|
+
kind: "conflict",
|
|
890
|
+
text: t("dirtyConflict")
|
|
891
|
+
}])));
|
|
892
|
+
setStatus("ready");
|
|
893
|
+
}, (failure) => {
|
|
894
|
+
if (!generationIsCurrent(generationRef, generation)) return;
|
|
895
|
+
setStatus("error");
|
|
896
|
+
setError(failure instanceof Error ? failure.message : t("loadError"));
|
|
897
|
+
});
|
|
898
|
+
}, [
|
|
899
|
+
api,
|
|
900
|
+
describe,
|
|
901
|
+
schema,
|
|
902
|
+
t
|
|
903
|
+
]);
|
|
904
|
+
(0, react.useEffect)(() => {
|
|
905
|
+
reload(false);
|
|
906
|
+
}, [reload]);
|
|
907
|
+
(0, react.useEffect)(() => {
|
|
908
|
+
if (props.subscribeInvalidate === void 0) return void 0;
|
|
909
|
+
return props.subscribeInvalidate(() => {
|
|
910
|
+
reload(true);
|
|
911
|
+
});
|
|
912
|
+
}, [props.subscribeInvalidate, reload]);
|
|
913
|
+
const save = async (draft) => {
|
|
914
|
+
if (api === void 0 || describe === void 0) return;
|
|
915
|
+
if (status === "loading" || busyRoute !== null) return;
|
|
916
|
+
const blocking = draft.models.map((row) => errorText(modelEffortError(row), t)).find((text) => text !== void 0);
|
|
917
|
+
if (blocking !== void 0) {
|
|
918
|
+
setNotices({ [draft.provider]: {
|
|
919
|
+
kind: "error",
|
|
920
|
+
text: blocking
|
|
921
|
+
} });
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
924
|
+
setBusyRoute(draft.provider);
|
|
925
|
+
setNotices({});
|
|
926
|
+
try {
|
|
927
|
+
const ops = buildSaveOps({
|
|
928
|
+
settingsPath: draft.settingsPath,
|
|
929
|
+
beforeModels: draft.originalModels,
|
|
930
|
+
afterModels: draft.models,
|
|
931
|
+
beforeCompat: draft.compatPresent ? draft.originalCompat : void 0,
|
|
932
|
+
afterCompat: draft.compat
|
|
933
|
+
});
|
|
934
|
+
if (ops.length === 0) {
|
|
935
|
+
setDrafts((current) => current.map((row) => row.provider === draft.provider ? alignDraft(row) : row));
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
const response = await api.settings.mutate({
|
|
939
|
+
ns: LLM_PI_AI_NS,
|
|
940
|
+
ops,
|
|
941
|
+
expectedRevision: draft.revision
|
|
942
|
+
});
|
|
943
|
+
if (!response.result.ok) {
|
|
944
|
+
setNotices({ [draft.provider]: {
|
|
945
|
+
kind: "error",
|
|
946
|
+
text: response.result.error.code === "settings-conflict" ? t("conflict") : response.result.error.message
|
|
947
|
+
} });
|
|
948
|
+
return;
|
|
949
|
+
}
|
|
950
|
+
const view = response.result.value;
|
|
951
|
+
describe.acceptView(view);
|
|
952
|
+
setDrafts((current) => applySaveSuccess(current, draft.provider, {
|
|
953
|
+
user: view.user ?? {},
|
|
954
|
+
revision: view.revision
|
|
955
|
+
}));
|
|
956
|
+
setNotices({ [draft.provider]: {
|
|
957
|
+
kind: "saved",
|
|
958
|
+
text: t("saved")
|
|
959
|
+
} });
|
|
960
|
+
} catch (failure) {
|
|
961
|
+
setNotices({ [draft.provider]: {
|
|
962
|
+
kind: "error",
|
|
963
|
+
text: failure instanceof Error ? failure.message : t("loadError")
|
|
964
|
+
} });
|
|
965
|
+
} finally {
|
|
966
|
+
setBusyRoute(null);
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
const showLoading = status === "loading" && drafts.length === 0;
|
|
970
|
+
const showEmpty = status === "ready" && drafts.length === 0;
|
|
971
|
+
const showList = drafts.length > 0;
|
|
972
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
973
|
+
className: effort_declare_module_css_default.section,
|
|
974
|
+
children: [
|
|
975
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
976
|
+
className: effort_declare_module_css_default.title,
|
|
977
|
+
children: t("title")
|
|
978
|
+
}),
|
|
979
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
980
|
+
className: effort_declare_module_css_default.intro,
|
|
981
|
+
children: t("intro")
|
|
982
|
+
}),
|
|
983
|
+
!writable && status === "ready" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
984
|
+
className: effort_declare_module_css_default.notice,
|
|
985
|
+
children: t("readOnly")
|
|
986
|
+
}) : null,
|
|
987
|
+
showLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
988
|
+
className: effort_declare_module_css_default.intro,
|
|
989
|
+
children: t("loading")
|
|
990
|
+
}) : null,
|
|
991
|
+
status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
992
|
+
className: effort_declare_module_css_default.error,
|
|
993
|
+
children: error
|
|
994
|
+
}) : null,
|
|
995
|
+
status === "error" ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
996
|
+
type: "button",
|
|
997
|
+
className: effort_declare_module_css_default.secondaryButton,
|
|
998
|
+
onClick: () => {
|
|
999
|
+
reload(true);
|
|
1000
|
+
},
|
|
1001
|
+
children: t("reload")
|
|
1002
|
+
}) : null,
|
|
1003
|
+
showEmpty ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1004
|
+
className: effort_declare_module_css_default.notice,
|
|
1005
|
+
children: t("empty")
|
|
1006
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1007
|
+
className: effort_declare_module_css_default.intro,
|
|
1008
|
+
children: t("emptyHint")
|
|
1009
|
+
})] }) : null,
|
|
1010
|
+
showList ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("ul", {
|
|
1011
|
+
className: effort_declare_module_css_default.rows,
|
|
1012
|
+
children: drafts.map((draft) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1013
|
+
draft,
|
|
1014
|
+
formats: formats.length > 0 ? formats : [],
|
|
1015
|
+
writable,
|
|
1016
|
+
busy: busyRoute === draft.provider,
|
|
1017
|
+
reloading: status === "loading",
|
|
1018
|
+
notice: notices[draft.provider],
|
|
1019
|
+
t,
|
|
1020
|
+
onChange: (next) => {
|
|
1021
|
+
setNotices((current) => {
|
|
1022
|
+
const copy = { ...current };
|
|
1023
|
+
delete copy[next.provider];
|
|
1024
|
+
return copy;
|
|
1025
|
+
});
|
|
1026
|
+
setDrafts((current) => current.map((row) => row.provider === next.provider ? next : row));
|
|
1027
|
+
},
|
|
1028
|
+
onSave: (next) => {
|
|
1029
|
+
save(next);
|
|
1030
|
+
},
|
|
1031
|
+
onCancel: (next) => {
|
|
1032
|
+
setNotices((current) => {
|
|
1033
|
+
const copy = { ...current };
|
|
1034
|
+
delete copy[next.provider];
|
|
1035
|
+
return copy;
|
|
1036
|
+
});
|
|
1037
|
+
setDrafts((current) => current.map((row) => row.provider === next.provider ? {
|
|
1038
|
+
...row,
|
|
1039
|
+
models: cloneModels(row.originalModels),
|
|
1040
|
+
compat: cloneObject(row.originalCompat)
|
|
1041
|
+
} : row));
|
|
1042
|
+
}
|
|
1043
|
+
}, draft.provider))
|
|
1044
|
+
}) : null
|
|
1045
|
+
]
|
|
1046
|
+
});
|
|
1047
|
+
}
|
|
1048
|
+
//#endregion
|
|
1049
|
+
//#region src/client/schema-ops.ts
|
|
1050
|
+
/** Wrap a live settingsSchema service as plain callbacks. */
|
|
1051
|
+
function bindSchema(service) {
|
|
1052
|
+
return {
|
|
1053
|
+
rehydrate: (serialized) => service.rehydrate(serialized),
|
|
1054
|
+
nodeAtPath: (root, path) => service.nodeAtPath(root, path),
|
|
1055
|
+
getPath: (value, path) => service.getPath(value, path),
|
|
1056
|
+
hasPath: (value, path) => service.hasPath(value, path)
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
//#endregion
|
|
1060
|
+
//#region src/client/locales.ts
|
|
1061
|
+
const NS = "plugin-effort-declare";
|
|
1062
|
+
const zh = {
|
|
1063
|
+
nav: "推理档位",
|
|
1064
|
+
title: "推理档位声明",
|
|
1065
|
+
intro: "本页声明「这模型能选哪些推理档」。对话里的档位选择仍在输入框模型菜单。没声明 = 和现在一样没有 Effort 行。不会改密钥、目录或当次选择。",
|
|
1066
|
+
empty: "没有可编辑的手工 openai-completions 路由。",
|
|
1067
|
+
emptyHint: "请先到「模型」页添加提供方(llm-pi-ai 手工路由)。官方 DeepSeek 与 catalog 路由不在本页编辑。需要 DSH ≥ 0.1.0-rc.8(providers.declared)。",
|
|
1068
|
+
loadError: "无法加载提供方或设置。",
|
|
1069
|
+
loading: "正在加载…",
|
|
1070
|
+
readOnly: "当前设置为只读,无法保存。",
|
|
1071
|
+
save: "保存",
|
|
1072
|
+
saving: "保存中…",
|
|
1073
|
+
cancel: "取消",
|
|
1074
|
+
saved: "已保存。对话选择器会按新的能力声明显示 Effort 行。",
|
|
1075
|
+
conflict: "设置已被其他地方改过,请重新加载后再保存。",
|
|
1076
|
+
dirtyConflict: "此路由有未保存修改,设置已在其他地方更新。保存会写到最新文档上,或先取消再改。",
|
|
1077
|
+
presets: "预设",
|
|
1078
|
+
presetDeepSeek: "DeepSeek 兼容",
|
|
1079
|
+
presetOpenAI: "OpenAI 兼容",
|
|
1080
|
+
presetToggle: "仅开/关",
|
|
1081
|
+
presetToggleWarn: "选择器里多档在线上没有区别:除 Off 外请求体相同。",
|
|
1082
|
+
model: "模型",
|
|
1083
|
+
levels: "可选档位",
|
|
1084
|
+
wire: "线上拼写",
|
|
1085
|
+
offMode: "Off",
|
|
1086
|
+
offAbsent: "无 Off",
|
|
1087
|
+
offEmpty: "Off 且不发参数",
|
|
1088
|
+
offValue: "Off 且发字符串",
|
|
1089
|
+
offValuePlaceholder: "none",
|
|
1090
|
+
clear: "清除本模型声明",
|
|
1091
|
+
advanced: "高级:协议方言",
|
|
1092
|
+
thinkingFormat: "thinkingFormat",
|
|
1093
|
+
thinkingFormatDefault: "默认(openai)",
|
|
1094
|
+
supportsDeveloperRole: "系统提示走 system 而不是 developer(supportsDeveloperRole: false)",
|
|
1095
|
+
supportsReasoningEffort: "不发 reasoning_effort,只发开关(supportsReasoningEffort: false)",
|
|
1096
|
+
developerTrueHint: "当前文档是 supportsDeveloperRole: true。v1 只能强制 false 或缺席;勾选会写成 false,取消勾选会删除该键。",
|
|
1097
|
+
compatSummary: "协议",
|
|
1098
|
+
errorEmpty: "不能保存空的 reasoningEfforts。请选择档位,或清除声明。",
|
|
1099
|
+
errorOffOnly: "不能只开 Off,必须再开一档思考档。",
|
|
1100
|
+
errorBadWire: "思考档必须填写线上拼写;只有 Off 可以留空。不能使用未知档位键。",
|
|
1101
|
+
noModels: "这条路由还没有 models 列表。请先到「模型」页添加模型。",
|
|
1102
|
+
reload: "重新加载"
|
|
1103
|
+
};
|
|
1104
|
+
const en = {
|
|
1105
|
+
nav: "Reasoning efforts",
|
|
1106
|
+
title: "Reasoning effort declarations",
|
|
1107
|
+
intro: "This page declares which reasoning levels a model can offer. The per-turn choice stays in the composer model menu. Undeclared models keep having no Effort row. Keys, catalogs, and the current selection are not edited here.",
|
|
1108
|
+
empty: "No editable hand-declared openai-completions routes.",
|
|
1109
|
+
emptyHint: "Add a provider on the Models page first (an llm-pi-ai hand-declared route). Official DeepSeek and catalog routes are not edited here. Requires DSH ≥ 0.1.0-rc.8 (providers.declared).",
|
|
1110
|
+
loadError: "Could not load providers or settings.",
|
|
1111
|
+
loading: "Loading…",
|
|
1112
|
+
readOnly: "Settings are read-only; saving is disabled.",
|
|
1113
|
+
save: "Save",
|
|
1114
|
+
saving: "Saving…",
|
|
1115
|
+
cancel: "Cancel",
|
|
1116
|
+
saved: "Saved. The composer Effort row follows this capability declaration.",
|
|
1117
|
+
conflict: "Settings changed elsewhere. Reload, then save again.",
|
|
1118
|
+
dirtyConflict: "This route has unsaved edits and settings changed elsewhere. Save writes onto the latest document, or cancel first.",
|
|
1119
|
+
presets: "Presets",
|
|
1120
|
+
presetDeepSeek: "DeepSeek compatible",
|
|
1121
|
+
presetOpenAI: "OpenAI compatible",
|
|
1122
|
+
presetToggle: "On/off only",
|
|
1123
|
+
presetToggleWarn: "Multiple selector levels are identical on the wire except Off.",
|
|
1124
|
+
model: "Model",
|
|
1125
|
+
levels: "Offered levels",
|
|
1126
|
+
wire: "Wire spelling",
|
|
1127
|
+
offMode: "Off",
|
|
1128
|
+
offAbsent: "No Off",
|
|
1129
|
+
offEmpty: "Off, send nothing",
|
|
1130
|
+
offValue: "Off, send a string",
|
|
1131
|
+
offValuePlaceholder: "none",
|
|
1132
|
+
clear: "Clear this model’s declaration",
|
|
1133
|
+
advanced: "Advanced: protocol dialect",
|
|
1134
|
+
thinkingFormat: "thinkingFormat",
|
|
1135
|
+
thinkingFormatDefault: "Default (openai)",
|
|
1136
|
+
supportsDeveloperRole: "Send system prompts as system, not developer (supportsDeveloperRole: false)",
|
|
1137
|
+
supportsReasoningEffort: "Do not send reasoning_effort; switch only (supportsReasoningEffort: false)",
|
|
1138
|
+
developerTrueHint: "The document has supportsDeveloperRole: true. v1 can only force false or omit the key; checking writes false, unchecking deletes the key.",
|
|
1139
|
+
compatSummary: "Protocol",
|
|
1140
|
+
errorEmpty: "Empty reasoningEfforts cannot be saved. Pick levels, or clear the declaration.",
|
|
1141
|
+
errorOffOnly: "Off alone is not enough; declare at least one thinking level.",
|
|
1142
|
+
errorBadWire: "Thinking levels need a wire spelling; only Off may be empty. Unknown effort keys are rejected.",
|
|
1143
|
+
noModels: "This route has no models list yet. Add models on the Models page first.",
|
|
1144
|
+
reload: "Reload"
|
|
1145
|
+
};
|
|
1146
|
+
//#endregion
|
|
1147
|
+
//#region src/client/index.ts
|
|
1148
|
+
const inject = [
|
|
1149
|
+
"slots",
|
|
1150
|
+
"locale",
|
|
1151
|
+
"connection",
|
|
1152
|
+
"remote",
|
|
1153
|
+
"settingsScope",
|
|
1154
|
+
"settingsSchema"
|
|
1155
|
+
];
|
|
1156
|
+
const PLUGIN_ID = "dsh-plugin-effort-declare";
|
|
1157
|
+
function mountPluginCss() {
|
|
1158
|
+
if (typeof document === "undefined") return () => {};
|
|
1159
|
+
const selector = `style[data-plugin-css=${JSON.stringify(cssTagId)}]`;
|
|
1160
|
+
let tag = document.querySelector(selector);
|
|
1161
|
+
if (tag === null) {
|
|
1162
|
+
tag = document.createElement("style");
|
|
1163
|
+
tag.dataset.plugin = PLUGIN_ID;
|
|
1164
|
+
tag.dataset.pluginCss = cssTagId;
|
|
1165
|
+
document.head.appendChild(tag);
|
|
1166
|
+
}
|
|
1167
|
+
tag.textContent = cssText;
|
|
1168
|
+
return () => {
|
|
1169
|
+
tag?.remove();
|
|
1170
|
+
};
|
|
1171
|
+
}
|
|
1172
|
+
function apply(ctx) {
|
|
1173
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
1174
|
+
zh,
|
|
1175
|
+
en
|
|
1176
|
+
}), `${PLUGIN_ID}: dictionaries`);
|
|
1177
|
+
ctx.effect(() => mountPluginCss(), `${PLUGIN_ID}: css`);
|
|
1178
|
+
const connection = ctx.get("connection");
|
|
1179
|
+
const settingsSchema = ctx.settingsSchema;
|
|
1180
|
+
const schema = bindSchema({
|
|
1181
|
+
rehydrate: (serialized) => settingsSchema.rehydrate(serialized),
|
|
1182
|
+
nodeAtPath: (root, path) => settingsSchema.nodeAtPath(root, path),
|
|
1183
|
+
getPath: (value, path) => settingsSchema.getPath(value, path),
|
|
1184
|
+
hasPath: (value, path) => settingsSchema.hasPath(value, path)
|
|
1185
|
+
});
|
|
1186
|
+
const t = ctx.locale.bind(NS);
|
|
1187
|
+
const describe = ctx.settingsScope.describe();
|
|
1188
|
+
const invalidation = /* @__PURE__ */ new Set();
|
|
1189
|
+
ctx.effect(() => {
|
|
1190
|
+
const emit = (source) => {
|
|
1191
|
+
for (const listener of invalidation) listener(source);
|
|
1192
|
+
};
|
|
1193
|
+
const disposers = [
|
|
1194
|
+
ctx.remote.$on("settings/document-updated", (ns) => {
|
|
1195
|
+
if (ns !== "llm-pi-ai") return;
|
|
1196
|
+
emit("settings");
|
|
1197
|
+
}),
|
|
1198
|
+
ctx.remote.$on("llm/adapters-updated", () => {
|
|
1199
|
+
emit("directory");
|
|
1200
|
+
}),
|
|
1201
|
+
ctx.on("connection/reset", () => {
|
|
1202
|
+
emit("directory");
|
|
1203
|
+
})
|
|
1204
|
+
];
|
|
1205
|
+
return () => {
|
|
1206
|
+
for (const dispose of disposers) dispose();
|
|
1207
|
+
};
|
|
1208
|
+
}, `${PLUGIN_ID}: invalidations`);
|
|
1209
|
+
const subscribeInvalidate = (listener) => {
|
|
1210
|
+
invalidation.add(listener);
|
|
1211
|
+
return () => {
|
|
1212
|
+
invalidation.delete(listener);
|
|
1213
|
+
};
|
|
1214
|
+
};
|
|
1215
|
+
ctx.slots.inject("settings.section", () => ctx.slots.register({
|
|
1216
|
+
name: "settings.section",
|
|
1217
|
+
id: "effort-declare",
|
|
1218
|
+
order: 15,
|
|
1219
|
+
label: () => t("nav"),
|
|
1220
|
+
locale: NS,
|
|
1221
|
+
inject: () => ({
|
|
1222
|
+
api: connection.api,
|
|
1223
|
+
describe,
|
|
1224
|
+
schema,
|
|
1225
|
+
t,
|
|
1226
|
+
subscribeInvalidate
|
|
1227
|
+
})
|
|
1228
|
+
}, EffortDeclareSection));
|
|
1229
|
+
}
|
|
1230
|
+
//#endregion
|
|
1231
|
+
exports.apply = apply;
|
|
1232
|
+
exports.inject = inject;
|
|
1233
|
+
return module.exports;
|
|
1234
|
+
}
|
|
1235
|
+
});
|
|
1236
|
+
|
|
1237
|
+
//# sourceMappingURL=client.js.map
|