dsh-model-switch 0.4.21 → 0.4.23
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/CHANGELOG.md +17 -0
- package/PRODUCT.md +4 -4
- package/README.md +17 -12
- package/README.zh.md +17 -12
- package/lib/client.js +820 -512
- package/lib/index.js +93 -23
- package/lib/types/client/ModelSwitchSettings.d.ts +4 -3
- package/lib/types/client/locales.d.ts +9 -5
- package/lib/types/client/main-row-controller.d.ts +16 -35
- package/lib/types/client/picker/ComposerPicker.d.ts +1 -3
- package/lib/types/client/picker/PlanReviewCard.d.ts +0 -6
- package/lib/types/client/picker/locales.d.ts +0 -8
- package/lib/types/client/picker/provider-group-watch.d.ts +48 -0
- package/lib/types/client-contract.d.ts +3 -9
- package/lib/types/host-runtime.d.ts +3 -41
- package/lib/types/host-settings.d.ts +11 -29
- package/lib/types/index.d.ts +1 -1
- package/lib/types/picker/plan-review.d.ts +2 -10
- package/lib/types/provider-group.d.ts +78 -0
- package/package.json +1 -1
package/lib/client.js
CHANGED
|
@@ -10,11 +10,7 @@ window.__ModuleLoader__.load({
|
|
|
10
10
|
let _deepseek_ai_dsh_client_ui_primitives = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
11
11
|
//#region src/client-contract.ts
|
|
12
12
|
const MODEL_SWITCH_CONFIG_ID = "model-switch";
|
|
13
|
-
const MAIN_DEFAULT_CONFIG_ID = "agent-default-model";
|
|
14
13
|
const PROVIDERS_CONFIG_ID = "llm-providers-ui";
|
|
15
|
-
var MainSettingsConflictError = class extends Error {
|
|
16
|
-
name = "MainSettingsConflictError";
|
|
17
|
-
};
|
|
18
14
|
const SUBAGENT_SETTINGS_FIELDS = Object.freeze({
|
|
19
15
|
mode: "subagentMode",
|
|
20
16
|
provider: "subagentProvider",
|
|
@@ -80,6 +76,174 @@ window.__ModuleLoader__.load({
|
|
|
80
76
|
return route !== void 0 && (route.provider ?? "").trim() !== "" && (route.model ?? "").trim() !== "";
|
|
81
77
|
}
|
|
82
78
|
//#endregion
|
|
79
|
+
//#region src/provider-group.ts
|
|
80
|
+
const ROUTE_FIELDS = Object.freeze([
|
|
81
|
+
"compactProvider",
|
|
82
|
+
"compactModel",
|
|
83
|
+
"autoReviewProvider",
|
|
84
|
+
"autoReviewModel",
|
|
85
|
+
"subagentProvider",
|
|
86
|
+
"subagentModel",
|
|
87
|
+
"subagentReasoningEffort"
|
|
88
|
+
]);
|
|
89
|
+
/** Own data property, so a literal '__proto__' provider id can never touch a prototype. */
|
|
90
|
+
function setKey(target, key, value) {
|
|
91
|
+
Object.defineProperty(target, key, {
|
|
92
|
+
value,
|
|
93
|
+
enumerable: true,
|
|
94
|
+
writable: true,
|
|
95
|
+
configurable: true
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
/** A non-blank string. Blank values behave exactly like an absent field. */
|
|
99
|
+
function present(value) {
|
|
100
|
+
return typeof value === "string" && value.trim() !== "";
|
|
101
|
+
}
|
|
102
|
+
/** Normalize one raw entry to its owning provider and route view; undefined for non-object junk. */
|
|
103
|
+
function normalizeEntry(value) {
|
|
104
|
+
if (value === null || typeof value !== "object" || Array.isArray(value)) return void 0;
|
|
105
|
+
const source = value;
|
|
106
|
+
const group = {};
|
|
107
|
+
for (const field of ROUTE_FIELDS) {
|
|
108
|
+
const item = source[field];
|
|
109
|
+
if (present(item)) group[field] = item;
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
provider: present(source.provider) ? source.provider : "",
|
|
113
|
+
group
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Strip a raw stored value to well-formed groups; never throws. Both storage
|
|
118
|
+
* shapes are accepted: the stored entry array (keys live in each entry's
|
|
119
|
+
* `provider` field) and a hand-written provider → routes map (keys are the
|
|
120
|
+
* providers). All-empty entries stay: they pin a provider to every default.
|
|
121
|
+
*/
|
|
122
|
+
function normalizeProviderGroups(raw) {
|
|
123
|
+
const groups = {};
|
|
124
|
+
if (Array.isArray(raw)) {
|
|
125
|
+
for (const entry of raw) {
|
|
126
|
+
const view = normalizeEntry(entry);
|
|
127
|
+
if (view === void 0 || view.provider === "") continue;
|
|
128
|
+
setKey(groups, view.provider, view.group);
|
|
129
|
+
}
|
|
130
|
+
return groups;
|
|
131
|
+
}
|
|
132
|
+
if (raw !== null && typeof raw === "object") for (const [key, value] of Object.entries(raw)) {
|
|
133
|
+
if (key.trim() === "") continue;
|
|
134
|
+
const view = normalizeEntry(value);
|
|
135
|
+
if (view === void 0) continue;
|
|
136
|
+
setKey(groups, key, view.group);
|
|
137
|
+
}
|
|
138
|
+
return groups;
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* The saved group for a provider.
|
|
142
|
+
* @returns the normalized group, or undefined when the provider has no group —
|
|
143
|
+
* which is what leaves the live routes untouched during a switch.
|
|
144
|
+
*/
|
|
145
|
+
function groupOf(groups, provider) {
|
|
146
|
+
if (groups === void 0 || provider.trim() === "") return void 0;
|
|
147
|
+
if (!Object.prototype.hasOwnProperty.call(groups, provider)) return void 0;
|
|
148
|
+
return groups[provider];
|
|
149
|
+
}
|
|
150
|
+
/** A complete pinned pair; either half missing leaves the capability at its default. */
|
|
151
|
+
function pair(group, providerField, modelField) {
|
|
152
|
+
const provider = group[providerField];
|
|
153
|
+
const model = group[modelField];
|
|
154
|
+
return present(provider) && present(model) ? {
|
|
155
|
+
provider,
|
|
156
|
+
model
|
|
157
|
+
} : void 0;
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Ordered writes applying one group with full-replacement semantics: pinned
|
|
161
|
+
* capabilities land as pairs, everything else reverts to its default state
|
|
162
|
+
* (compaction/review follow the session model; Subagent follows official
|
|
163
|
+
* inherit). The mode write comes last so a fixed route never publishes with
|
|
164
|
+
* the previous provider's fields still half-written.
|
|
165
|
+
*/
|
|
166
|
+
function providerGroupWrites(group) {
|
|
167
|
+
const compact = pair(group, "compactProvider", "compactModel");
|
|
168
|
+
const review = pair(group, "autoReviewProvider", "autoReviewModel");
|
|
169
|
+
const subagent = pair(group, "subagentProvider", "subagentModel");
|
|
170
|
+
const effort = subagent !== void 0 && present(group.subagentReasoningEffort) ? group.subagentReasoningEffort : void 0;
|
|
171
|
+
return [
|
|
172
|
+
{
|
|
173
|
+
field: "compactProvider",
|
|
174
|
+
value: compact?.provider
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
field: "compactModel",
|
|
178
|
+
value: compact?.model
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
field: "autoReviewProvider",
|
|
182
|
+
value: review?.provider
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
field: "autoReviewModel",
|
|
186
|
+
value: review?.model
|
|
187
|
+
},
|
|
188
|
+
{
|
|
189
|
+
field: "subagentProvider",
|
|
190
|
+
value: subagent?.provider
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
field: "subagentModel",
|
|
194
|
+
value: subagent?.model
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
field: "subagentReasoningEffort",
|
|
198
|
+
value: effort
|
|
199
|
+
},
|
|
200
|
+
{
|
|
201
|
+
field: "subagentMode",
|
|
202
|
+
mode: subagent !== void 0 ? "fixed" : "follow-main"
|
|
203
|
+
}
|
|
204
|
+
];
|
|
205
|
+
}
|
|
206
|
+
/** Storage entry for one provider's group; absent or blank route fields become ''. */
|
|
207
|
+
function entryOf(provider, group) {
|
|
208
|
+
return {
|
|
209
|
+
provider,
|
|
210
|
+
compactProvider: group.compactProvider ?? "",
|
|
211
|
+
compactModel: group.compactModel ?? "",
|
|
212
|
+
autoReviewProvider: group.autoReviewProvider ?? "",
|
|
213
|
+
autoReviewModel: group.autoReviewModel ?? "",
|
|
214
|
+
subagentProvider: group.subagentProvider ?? "",
|
|
215
|
+
subagentModel: group.subagentModel ?? "",
|
|
216
|
+
subagentReasoningEffort: group.subagentReasoningEffort ?? ""
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* The stored entry list with one provider's group replaced (an entry) or
|
|
221
|
+
* removed (undefined). Every entry is re-normalized and de-duplicated so a
|
|
222
|
+
* save can never write junk back; the input is never mutated. A blank
|
|
223
|
+
* provider only normalizes.
|
|
224
|
+
*/
|
|
225
|
+
function withProviderGroup(storage, provider, group) {
|
|
226
|
+
const groups = { ...normalizeProviderGroups(storage) };
|
|
227
|
+
if (provider.trim() !== "") {
|
|
228
|
+
if (group === void 0) delete groups[provider];
|
|
229
|
+
else setKey(groups, provider, group);
|
|
230
|
+
}
|
|
231
|
+
return Object.entries(groups).map(([key, value]) => entryOf(key, value));
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Apply a saved group's routes to the live flat settings.
|
|
235
|
+
* @returns true when a group existed and every write settled; false when the
|
|
236
|
+
* provider has no group (nothing was written). A rejected write propagates.
|
|
237
|
+
*/
|
|
238
|
+
async function applyProviderGroupTo(sink, groups, provider) {
|
|
239
|
+
const group = groupOf(groups, provider);
|
|
240
|
+
if (group === void 0) return false;
|
|
241
|
+
for (const write of providerGroupWrites(group)) if (write.field === "subagentMode") await sink.set("subagentMode", write.mode);
|
|
242
|
+
else if (write.value === void 0) await sink.unset(write.field);
|
|
243
|
+
else await sink.set(write.field, write.value);
|
|
244
|
+
return true;
|
|
245
|
+
}
|
|
246
|
+
//#endregion
|
|
83
247
|
//#region src/client/derived-config-form.ts
|
|
84
248
|
function deriveConfigForm(source, project, fields) {
|
|
85
249
|
let sourceSnapshot;
|
|
@@ -436,65 +600,20 @@ window.__ModuleLoader__.load({
|
|
|
436
600
|
models
|
|
437
601
|
};
|
|
438
602
|
}
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
442
|
-
function acceptedRevisionAfterFailure(accepted, error) {
|
|
443
|
-
return error instanceof MainSettingsConflictError ? void 0 : accepted;
|
|
444
|
-
}
|
|
445
|
-
function deriveMainChoices(groups, draft) {
|
|
446
|
-
const providers = groups.map((group) => ({
|
|
447
|
-
id: group.id,
|
|
448
|
-
name: group.name
|
|
449
|
-
}));
|
|
450
|
-
if (draft !== void 0 && !providers.some((option) => option.id === draft.provider)) providers.push({
|
|
451
|
-
id: draft.provider,
|
|
452
|
-
name: draft.provider
|
|
453
|
-
});
|
|
454
|
-
const group = groups.find((item) => item.id === draft?.provider);
|
|
455
|
-
const models = (group?.models ?? []).map((model) => ({
|
|
456
|
-
id: model.id,
|
|
457
|
-
name: model.name
|
|
458
|
-
}));
|
|
459
|
-
if (draft !== void 0 && !models.some((option) => option.id === draft.model)) models.push({
|
|
460
|
-
id: draft.model,
|
|
461
|
-
name: draft.model
|
|
462
|
-
});
|
|
463
|
-
const efforts = ((group?.models.find((item) => item.id === draft?.model))?.reasoning?.efforts ?? []).map((effort) => ({
|
|
464
|
-
id: effort.id,
|
|
465
|
-
name: effort.name
|
|
466
|
-
}));
|
|
467
|
-
if (draft?.reasoningEffort !== void 0 && !efforts.some((option) => option.id === draft.reasoningEffort)) efforts.push({
|
|
468
|
-
id: draft.reasoningEffort,
|
|
469
|
-
name: draft.reasoningEffort
|
|
470
|
-
});
|
|
471
|
-
return {
|
|
472
|
-
providers,
|
|
473
|
-
models,
|
|
474
|
-
efforts
|
|
475
|
-
};
|
|
476
|
-
}
|
|
477
|
-
function useModelSwitchSettingsController(input) {
|
|
478
|
-
const main = input.useMainSettings((value) => value);
|
|
479
|
-
const subagent = input.useSubagentSettings((value) => value);
|
|
480
|
-
const [draft, setDraft] = (0, react.useState)(main.value);
|
|
603
|
+
/** Load the model catalog backing every route editor, reloading on provider-order changes. */
|
|
604
|
+
function useSettingsCatalog(input) {
|
|
481
605
|
const [groups, setGroups] = (0, react.useState)([]);
|
|
482
|
-
const [
|
|
483
|
-
const [message, setMessage] = (0, react.useState)();
|
|
484
|
-
const locked = (0, react.useRef)(false);
|
|
485
|
-
const acceptedRevision = (0, react.useRef)();
|
|
486
|
-
(0, react.useEffect)(() => {
|
|
487
|
-
setDraft(main.value);
|
|
488
|
-
setMessage(void 0);
|
|
489
|
-
if (main.revision !== void 0 && (acceptedRevision.current === void 0 || main.revision > acceptedRevision.current)) acceptedRevision.current = main.revision;
|
|
490
|
-
}, [main.revision, main.value]);
|
|
606
|
+
const [catalogError, setCatalogError] = (0, react.useState)();
|
|
491
607
|
(0, react.useEffect)(() => {
|
|
492
608
|
let live = true;
|
|
493
609
|
const load = () => {
|
|
494
610
|
input.loadCatalog().then((value) => {
|
|
495
|
-
if (live)
|
|
611
|
+
if (live) {
|
|
612
|
+
setGroups(value);
|
|
613
|
+
setCatalogError(void 0);
|
|
614
|
+
}
|
|
496
615
|
}).catch(() => {
|
|
497
|
-
if (live)
|
|
616
|
+
if (live) setCatalogError(input.t("catalogFailed"));
|
|
498
617
|
});
|
|
499
618
|
};
|
|
500
619
|
load();
|
|
@@ -510,63 +629,9 @@ window.__ModuleLoader__.load({
|
|
|
510
629
|
input.subscribeProviderOrder,
|
|
511
630
|
input.t
|
|
512
631
|
]);
|
|
513
|
-
const { providers, models, efforts } = deriveMainChoices(groups, draft);
|
|
514
|
-
const disabled = main.status !== "ready" || !main.writable || draft === void 0 || busy || draft.provider.trim() === "" || draft.model.trim() === "";
|
|
515
|
-
const setProvider = (provider) => setDraft((current) => {
|
|
516
|
-
if (current === void 0) return current;
|
|
517
|
-
const first = groups.find((item) => item.id === provider)?.models[0];
|
|
518
|
-
return selectRouteModel(groups, provider, first?.id ?? current.model);
|
|
519
|
-
});
|
|
520
|
-
const setModel = (id) => setDraft((current) => current === void 0 ? current : selectRouteModel(groups, current.provider, id));
|
|
521
|
-
const setReasoningEffort = (value) => setDraft((current) => {
|
|
522
|
-
if (current === void 0) return current;
|
|
523
|
-
const next = { ...current };
|
|
524
|
-
if (value === "") delete next.reasoningEffort;
|
|
525
|
-
else next.reasoningEffort = value;
|
|
526
|
-
return next;
|
|
527
|
-
});
|
|
528
|
-
const reset = () => {
|
|
529
|
-
setDraft(main.value);
|
|
530
|
-
setMessage(void 0);
|
|
531
|
-
};
|
|
532
|
-
const save = async () => {
|
|
533
|
-
if (disabled || locked.current || draft === void 0) return;
|
|
534
|
-
const mirrorRevision = main.revision;
|
|
535
|
-
if (mirrorRevision === void 0) {
|
|
536
|
-
setMessage(input.t("requestFailed"));
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
539
|
-
const expectedRevision = expectedMainRevision(mirrorRevision, acceptedRevision.current);
|
|
540
|
-
locked.current = true;
|
|
541
|
-
setBusy(true);
|
|
542
|
-
setMessage(void 0);
|
|
543
|
-
try {
|
|
544
|
-
acceptedRevision.current = await input.saveMain(draft, expectedRevision);
|
|
545
|
-
setMessage(input.t("saved"));
|
|
546
|
-
} catch (error) {
|
|
547
|
-
acceptedRevision.current = acceptedRevisionAfterFailure(acceptedRevision.current, error);
|
|
548
|
-
setMessage(error instanceof Error ? error.message : input.t("requestFailed"));
|
|
549
|
-
} finally {
|
|
550
|
-
locked.current = false;
|
|
551
|
-
setBusy(false);
|
|
552
|
-
}
|
|
553
|
-
};
|
|
554
632
|
return {
|
|
555
|
-
main,
|
|
556
|
-
subagent,
|
|
557
|
-
draft,
|
|
558
633
|
groups,
|
|
559
|
-
|
|
560
|
-
models,
|
|
561
|
-
efforts,
|
|
562
|
-
busy,
|
|
563
|
-
message,
|
|
564
|
-
disabled,
|
|
565
|
-
setProvider,
|
|
566
|
-
setModel,
|
|
567
|
-
setReasoningEffort,
|
|
568
|
-
reset,
|
|
569
|
-
save
|
|
634
|
+
catalogError
|
|
570
635
|
};
|
|
571
636
|
}
|
|
572
637
|
//#endregion
|
|
@@ -628,29 +693,50 @@ window.__ModuleLoader__.load({
|
|
|
628
693
|
function compact(...values) {
|
|
629
694
|
return values.filter((value) => value !== void 0 && value !== "").join(" · ");
|
|
630
695
|
}
|
|
696
|
+
/** A complete pinned pair; either half blank leaves the capability at its default. */
|
|
697
|
+
function draftPair(provider, model) {
|
|
698
|
+
return provider !== void 0 && provider.trim() !== "" && model !== void 0 && model.trim() !== "" ? {
|
|
699
|
+
provider,
|
|
700
|
+
model
|
|
701
|
+
} : void 0;
|
|
702
|
+
}
|
|
631
703
|
function RouteIcon({ kind }) {
|
|
632
|
-
if (kind === "
|
|
704
|
+
if (kind === "providerGroups") return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("svg", {
|
|
633
705
|
width: "18",
|
|
634
706
|
height: "18",
|
|
635
707
|
viewBox: "0 0 18 18",
|
|
636
708
|
fill: "none",
|
|
637
709
|
"aria-hidden": "true",
|
|
638
|
-
children:
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
710
|
+
children: [
|
|
711
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
712
|
+
d: "M3 5.5h12M3 12.5h12",
|
|
713
|
+
stroke: "currentColor",
|
|
714
|
+
strokeLinecap: "round"
|
|
715
|
+
}),
|
|
716
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
717
|
+
cx: "6.5",
|
|
718
|
+
cy: "5.5",
|
|
719
|
+
r: "2",
|
|
720
|
+
stroke: "currentColor"
|
|
721
|
+
}),
|
|
722
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("circle", {
|
|
723
|
+
cx: "11.5",
|
|
724
|
+
cy: "12.5",
|
|
725
|
+
r: "2",
|
|
726
|
+
stroke: "currentColor"
|
|
727
|
+
})
|
|
728
|
+
]
|
|
644
729
|
});
|
|
645
|
-
if (kind === "
|
|
730
|
+
if (kind === "compaction") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
|
|
646
731
|
width: "18",
|
|
647
732
|
height: "18",
|
|
648
733
|
viewBox: "0 0 18 18",
|
|
649
734
|
fill: "none",
|
|
650
735
|
"aria-hidden": "true",
|
|
651
736
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", {
|
|
652
|
-
d: "
|
|
737
|
+
d: "M5 3.5 9 7.5l4-4M5 14.5 9 10.5l4 4",
|
|
653
738
|
stroke: "currentColor",
|
|
739
|
+
strokeLinecap: "round",
|
|
654
740
|
strokeLinejoin: "round"
|
|
655
741
|
})
|
|
656
742
|
});
|
|
@@ -840,7 +926,7 @@ window.__ModuleLoader__.load({
|
|
|
840
926
|
})]
|
|
841
927
|
});
|
|
842
928
|
}
|
|
843
|
-
function Actions({ t, busy, disabled, message, onCancel, onSave }) {
|
|
929
|
+
function Actions({ t, busy, disabled, message, extra, onCancel, onSave }) {
|
|
844
930
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
845
931
|
className: ModelSwitchSettings_module_css_default.cardFooter,
|
|
846
932
|
children: [
|
|
@@ -848,6 +934,7 @@ window.__ModuleLoader__.load({
|
|
|
848
934
|
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
849
935
|
children: message
|
|
850
936
|
}),
|
|
937
|
+
extra,
|
|
851
938
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
852
939
|
type: "button",
|
|
853
940
|
className: cx(ModelSwitchSettings_module_css_default.button, ModelSwitchSettings_module_css_default.secondaryButton),
|
|
@@ -889,6 +976,22 @@ window.__ModuleLoader__.load({
|
|
|
889
976
|
const matched = matchCatalogModel(groups.find((group) => group.id === route.provider)?.models ?? [], route.model);
|
|
890
977
|
return matched?.effort ?? matched?.model.reasoning?.defaultEffort;
|
|
891
978
|
}
|
|
979
|
+
/**
|
|
980
|
+
* The effort caption for the subagent summary: a stored effort is the pinned
|
|
981
|
+
* one ("已固定 · High"); an unset effort follows the provider default and says
|
|
982
|
+
* so ("Provider 默认 · High"), using the same effort-name lookup the picker does.
|
|
983
|
+
*/
|
|
984
|
+
function subagentEffortSegment(t, matched, pinned, defaultEffort) {
|
|
985
|
+
const effortName = (id) => matched?.model.reasoning?.efforts.find((level) => level.id === id)?.name ?? id;
|
|
986
|
+
if (pinned !== "") return compact(t("effortPinned"), effortName(pinned));
|
|
987
|
+
return defaultEffort === void 0 ? t("providerDefaultShort") : compact(t("providerDefaultShort"), effortName(defaultEffort));
|
|
988
|
+
}
|
|
989
|
+
/** The catalog head route; capability switches pin here when turned On. */
|
|
990
|
+
function firstCatalogRoute(groups) {
|
|
991
|
+
const provider = groups[0];
|
|
992
|
+
const model = provider?.models[0];
|
|
993
|
+
return provider === void 0 || model === void 0 ? void 0 : selectRouteModel(groups, provider.id, model.id);
|
|
994
|
+
}
|
|
892
995
|
function capabilityChoices(groups, route, providers, kind) {
|
|
893
996
|
const choices = deriveRouteChoices(groups, route, providers);
|
|
894
997
|
if (kind === "image" && route?.provider === "grok") {
|
|
@@ -909,8 +1012,8 @@ window.__ModuleLoader__.load({
|
|
|
909
1012
|
return choices;
|
|
910
1013
|
}
|
|
911
1014
|
function ModelSwitchSettings(props) {
|
|
912
|
-
const
|
|
913
|
-
const
|
|
1015
|
+
const { groups, catalogError } = useSettingsCatalog(props);
|
|
1016
|
+
const subagent = props.useSubagentSettings((value) => value);
|
|
914
1017
|
const search = props.useSearchSettings((value) => value);
|
|
915
1018
|
const image = props.useImageSettings((value) => value);
|
|
916
1019
|
const autoReview = props.useAutoReviewSettings((value) => value);
|
|
@@ -926,6 +1029,8 @@ window.__ModuleLoader__.load({
|
|
|
926
1029
|
const [message, setMessage] = (0, react.useState)();
|
|
927
1030
|
const [compactBusy, setCompactBusy] = (0, react.useState)(false);
|
|
928
1031
|
const [compactError, setCompactError] = (0, react.useState)();
|
|
1032
|
+
const [groupProvider, setGroupProvider] = (0, react.useState)("");
|
|
1033
|
+
const [groupDraft, setGroupDraft] = (0, react.useState)(void 0);
|
|
929
1034
|
const loadSearchCapabilities = props.loadCapabilities;
|
|
930
1035
|
const [searchSnapshot, setSearchSnapshot] = (0, react.useState)(void 0);
|
|
931
1036
|
const [searchError, setSearchError] = (0, react.useState)(void 0);
|
|
@@ -964,9 +1069,67 @@ window.__ModuleLoader__.load({
|
|
|
964
1069
|
if (timer !== void 0) clearTimeout(timer);
|
|
965
1070
|
};
|
|
966
1071
|
}, [loadSearchCapabilities]);
|
|
1072
|
+
const providerGroups = normalizeProviderGroups(switchSettings.value?.providerGroups);
|
|
1073
|
+
/** Seed an unsaved group from the live routes. */
|
|
1074
|
+
const seedGroupFromActive = () => {
|
|
1075
|
+
const compact = compaction.value !== void 0 && routePairConfigured(compaction.value) ? {
|
|
1076
|
+
provider: compaction.value.provider ?? "",
|
|
1077
|
+
model: compaction.value.model ?? ""
|
|
1078
|
+
} : void 0;
|
|
1079
|
+
const review = autoReview.value !== void 0 && routePairConfigured(autoReview.value) ? {
|
|
1080
|
+
provider: autoReview.value.provider ?? "",
|
|
1081
|
+
model: autoReview.value.model ?? ""
|
|
1082
|
+
} : void 0;
|
|
1083
|
+
const fixedSubagent = subagent.value !== void 0 && subagent.value.mode === "fixed" && routePairConfigured(subagent.value) ? {
|
|
1084
|
+
provider: subagent.value.provider ?? "",
|
|
1085
|
+
model: subagent.value.model ?? "",
|
|
1086
|
+
reasoningEffort: subagent.value.reasoningEffort
|
|
1087
|
+
} : void 0;
|
|
1088
|
+
return {
|
|
1089
|
+
...compact === void 0 ? {} : {
|
|
1090
|
+
compactProvider: compact.provider,
|
|
1091
|
+
compactModel: compact.model
|
|
1092
|
+
},
|
|
1093
|
+
...review === void 0 ? {} : {
|
|
1094
|
+
autoReviewProvider: review.provider,
|
|
1095
|
+
autoReviewModel: review.model
|
|
1096
|
+
},
|
|
1097
|
+
...fixedSubagent === void 0 ? {} : {
|
|
1098
|
+
subagentProvider: fixedSubagent.provider,
|
|
1099
|
+
subagentModel: fixedSubagent.model,
|
|
1100
|
+
...fixedSubagent.reasoningEffort === void 0 ? {} : { subagentReasoningEffort: fixedSubagent.reasoningEffort }
|
|
1101
|
+
}
|
|
1102
|
+
};
|
|
1103
|
+
};
|
|
1104
|
+
/** Reload the editor draft: the saved group when one exists, else a seed from the live routes. */
|
|
1105
|
+
const resyncGroupDraft = () => {
|
|
1106
|
+
if (groupProvider === "") {
|
|
1107
|
+
setGroupDraft(void 0);
|
|
1108
|
+
return;
|
|
1109
|
+
}
|
|
1110
|
+
setGroupDraft(groupOf(providerGroups, groupProvider) ?? seedGroupFromActive());
|
|
1111
|
+
};
|
|
1112
|
+
/** Open the editor on the provider in use: the first configured group, else the catalog head. */
|
|
1113
|
+
(0, react.useEffect)(() => {
|
|
1114
|
+
if (groupProvider !== "") return;
|
|
1115
|
+
const initial = Object.keys(providerGroups)[0] ?? groups[0]?.id;
|
|
1116
|
+
if (initial !== void 0 && initial.trim() !== "") setGroupProvider(initial);
|
|
1117
|
+
}, [
|
|
1118
|
+
groupProvider,
|
|
1119
|
+
providerGroups,
|
|
1120
|
+
groups
|
|
1121
|
+
]);
|
|
1122
|
+
(0, react.useEffect)(() => {
|
|
1123
|
+
resyncGroupDraft();
|
|
1124
|
+
}, [
|
|
1125
|
+
groupProvider,
|
|
1126
|
+
switchSettings.value,
|
|
1127
|
+
subagent.value,
|
|
1128
|
+
autoReview.value,
|
|
1129
|
+
compaction.value
|
|
1130
|
+
]);
|
|
967
1131
|
if ([
|
|
968
|
-
|
|
969
|
-
controller.subagent,
|
|
1132
|
+
subagent,
|
|
970
1133
|
search,
|
|
971
1134
|
image,
|
|
972
1135
|
switchSettings
|
|
@@ -1009,8 +1172,7 @@ window.__ModuleLoader__.load({
|
|
|
1009
1172
|
});
|
|
1010
1173
|
return efforts;
|
|
1011
1174
|
})();
|
|
1012
|
-
const defaultEffort =
|
|
1013
|
-
const mainEffectiveEffort = draft?.reasoningEffort ?? routeDefaultEffort(groups, draft);
|
|
1175
|
+
const defaultEffort = routeDefaultEffort(groups, subagentRoute);
|
|
1014
1176
|
const searchLive = searchSnapshot?.capabilities;
|
|
1015
1177
|
const searchGroups = searchGroupsFromCapabilities(searchLive);
|
|
1016
1178
|
const searchChoices = capabilityChoices(searchGroups, searchDraft, void 0, "search");
|
|
@@ -1021,7 +1183,6 @@ window.__ModuleLoader__.load({
|
|
|
1021
1183
|
const compactionChoices = deriveRouteChoices(groups, compactionDraft);
|
|
1022
1184
|
const compactionOn = routePairConfigured(compaction.value);
|
|
1023
1185
|
const synced = [
|
|
1024
|
-
main,
|
|
1025
1186
|
subagent,
|
|
1026
1187
|
search,
|
|
1027
1188
|
image,
|
|
@@ -1101,8 +1262,9 @@ window.__ModuleLoader__.load({
|
|
|
1101
1262
|
};
|
|
1102
1263
|
/**
|
|
1103
1264
|
* The reviewer switch: Off unsets both stored fields (follow the session
|
|
1104
|
-
* model); On
|
|
1105
|
-
* and stays editable below. A failed write reports, never
|
|
1265
|
+
* model); On pins the route to the catalog head so the pin takes effect
|
|
1266
|
+
* immediately and stays editable below. A failed write reports, never
|
|
1267
|
+
* flips silently.
|
|
1106
1268
|
*/
|
|
1107
1269
|
const persistAutoReviewEnabled = (enabled) => {
|
|
1108
1270
|
if (busy === "autoReview" || !autoReview.writable || autoReview.value === void 0) return;
|
|
@@ -1114,8 +1276,8 @@ window.__ModuleLoader__.load({
|
|
|
1114
1276
|
});
|
|
1115
1277
|
return;
|
|
1116
1278
|
}
|
|
1117
|
-
const seed =
|
|
1118
|
-
if (seed === void 0
|
|
1279
|
+
const seed = firstCatalogRoute(groups);
|
|
1280
|
+
if (seed === void 0) return;
|
|
1119
1281
|
setBusy("autoReview");
|
|
1120
1282
|
setMessage(void 0);
|
|
1121
1283
|
(async () => {
|
|
@@ -1138,9 +1300,9 @@ window.__ModuleLoader__.load({
|
|
|
1138
1300
|
};
|
|
1139
1301
|
/**
|
|
1140
1302
|
* The compaction switch: Off unsets both stored fields (official default =
|
|
1141
|
-
* the conversation target); On
|
|
1142
|
-
* effect immediately and stays editable below. A failed write
|
|
1143
|
-
* never flips silently.
|
|
1303
|
+
* the conversation target); On pins the route to the catalog head so the
|
|
1304
|
+
* pin takes effect immediately and stays editable below. A failed write
|
|
1305
|
+
* reports, never flips silently.
|
|
1144
1306
|
*/
|
|
1145
1307
|
const persistCompactionEnabled = (enabled) => {
|
|
1146
1308
|
if (busy === "compaction" || !compaction.writable || compaction.value === void 0) return;
|
|
@@ -1152,8 +1314,8 @@ window.__ModuleLoader__.load({
|
|
|
1152
1314
|
});
|
|
1153
1315
|
return;
|
|
1154
1316
|
}
|
|
1155
|
-
const seed =
|
|
1156
|
-
if (seed === void 0
|
|
1317
|
+
const seed = firstCatalogRoute(groups);
|
|
1318
|
+
if (seed === void 0) return;
|
|
1157
1319
|
setBusy("compaction");
|
|
1158
1320
|
setMessage(void 0);
|
|
1159
1321
|
(async () => {
|
|
@@ -1174,14 +1336,109 @@ window.__ModuleLoader__.load({
|
|
|
1174
1336
|
});
|
|
1175
1337
|
});
|
|
1176
1338
|
};
|
|
1177
|
-
const mainSummary = draft === void 0 ? props.t("loading") : compact(routeName(groups, draft), mainEffectiveEffort);
|
|
1178
1339
|
const subagentRole = subagentDraft?.mode === "fixed" && subagentDraft.provider !== void 0 && subagentDraft.provider !== "" ? props.providerRoleOf?.(subagentDraft.provider) : void 0;
|
|
1179
1340
|
const subagentOn = subagentDraft?.mode === "fixed";
|
|
1180
|
-
const subagentSummary = subagentDraft === void 0 ? props.t("loading") : subagentOn ? compact(routeName(groups, subagentRoute), isAgentRole(subagentRole) ? props.t("agentBadge") : void 0,
|
|
1341
|
+
const subagentSummary = subagentDraft === void 0 ? props.t("loading") : subagentOn ? compact(routeName(groups, subagentRoute), isAgentRole(subagentRole) ? props.t("agentBadge") : void 0, subagentEffortSegment(props.t, subagentCatalogModel, (subagentDraft.reasoningEffort ?? "").trim(), defaultEffort)) : props.t("subagentOff");
|
|
1181
1342
|
const subagentDisabled = subagent.status !== "ready" || !subagent.writable || subagentDraft === void 0 || busy === "subagent" || !subagentOn || (subagentDraft.provider ?? "").trim() === "" || (subagentDraft.model ?? "").trim() === "";
|
|
1182
1343
|
const capabilityDisabled = (route, snapshot, next) => snapshot.status !== "ready" || !snapshot.writable || next === void 0 || busy === route || (next.provider ?? "").trim() === "" || (next.model ?? "").trim() === "";
|
|
1183
1344
|
const autoReviewDisabled = autoReview.status !== "ready" || !autoReview.writable || autoReviewDraft === void 0 || busy === "autoReview" || !autoReviewOn || (autoReviewDraft.provider ?? "").trim() === "" || (autoReviewDraft.model ?? "").trim() === "";
|
|
1184
1345
|
const compactionDisabled = compaction.status !== "ready" || !compaction.writable || compactionDraft === void 0 || busy === "compaction" || !compactionOn || (compactionDraft.provider ?? "").trim() === "" || (compactionDraft.model ?? "").trim() === "";
|
|
1346
|
+
const configuredGroupCount = Object.keys(providerGroups).length;
|
|
1347
|
+
const groupControlsDisabled = busy === "providerGroups" || switchSettings.status !== "ready" || !switchSettings.writable;
|
|
1348
|
+
const groupProviderChoices = deriveRouteChoices(groups, groupProvider === "" ? void 0 : { provider: groupProvider }).providers.map((choice) => groupOf(providerGroups, choice.id) === void 0 ? choice : {
|
|
1349
|
+
...choice,
|
|
1350
|
+
name: choice.name + " ✓"
|
|
1351
|
+
});
|
|
1352
|
+
const groupCompaction = groupDraft === void 0 ? void 0 : draftPair(groupDraft.compactProvider, groupDraft.compactModel);
|
|
1353
|
+
const groupReview = groupDraft === void 0 ? void 0 : draftPair(groupDraft.autoReviewProvider, groupDraft.autoReviewModel);
|
|
1354
|
+
const groupSubagent = groupDraft === void 0 ? void 0 : draftPair(groupDraft.subagentProvider, groupDraft.subagentModel);
|
|
1355
|
+
const groupCompactionChoices = deriveRouteChoices(groups, groupCompaction);
|
|
1356
|
+
const groupReviewChoices = deriveRouteChoices(groups, groupReview);
|
|
1357
|
+
const groupSubagentChoices = deriveRouteChoices(groups, groupSubagent);
|
|
1358
|
+
const groupSubMatched = matchCatalogModel(groups.find((group) => group.id === (groupDraft?.subagentProvider ?? ""))?.models ?? [], groupDraft?.subagentModel ?? "");
|
|
1359
|
+
const groupSubEfforts = (() => {
|
|
1360
|
+
const efforts = (groupSubMatched?.model.reasoning?.efforts ?? []).map((effort) => ({
|
|
1361
|
+
id: effort.id,
|
|
1362
|
+
name: effort.name
|
|
1363
|
+
}));
|
|
1364
|
+
const current = groupDraft?.subagentReasoningEffort;
|
|
1365
|
+
if (current !== void 0 && current !== "" && !efforts.some((option) => option.id === current)) efforts.push({
|
|
1366
|
+
id: current,
|
|
1367
|
+
name: current
|
|
1368
|
+
});
|
|
1369
|
+
return efforts;
|
|
1370
|
+
})();
|
|
1371
|
+
/**
|
|
1372
|
+
* Enable a capability row by pinning it: to that capability's live route
|
|
1373
|
+
* when one is configured, else to the catalog head (same sources the
|
|
1374
|
+
* editor's first-edit seed uses).
|
|
1375
|
+
*/
|
|
1376
|
+
const enableGroupCapability = (kind) => {
|
|
1377
|
+
const active = seedGroupFromActive();
|
|
1378
|
+
const seed = firstCatalogRoute(groups);
|
|
1379
|
+
setGroupDraft((draft) => {
|
|
1380
|
+
if (draft === void 0) return draft;
|
|
1381
|
+
if (kind === "compaction") {
|
|
1382
|
+
const provider = active.compactProvider ?? seed?.provider;
|
|
1383
|
+
const model = active.compactModel ?? seed?.model;
|
|
1384
|
+
if (provider === void 0 || model === void 0) return draft;
|
|
1385
|
+
return {
|
|
1386
|
+
...draft,
|
|
1387
|
+
compactProvider: provider,
|
|
1388
|
+
compactModel: model
|
|
1389
|
+
};
|
|
1390
|
+
}
|
|
1391
|
+
if (kind === "autoReview") {
|
|
1392
|
+
const provider = active.autoReviewProvider ?? seed?.provider;
|
|
1393
|
+
const model = active.autoReviewModel ?? seed?.model;
|
|
1394
|
+
if (provider === void 0 || model === void 0) return draft;
|
|
1395
|
+
return {
|
|
1396
|
+
...draft,
|
|
1397
|
+
autoReviewProvider: provider,
|
|
1398
|
+
autoReviewModel: model
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
const provider = active.subagentProvider ?? seed?.provider;
|
|
1402
|
+
const model = active.subagentModel ?? seed?.model;
|
|
1403
|
+
if (provider === void 0 || model === void 0) return draft;
|
|
1404
|
+
const effort = active.subagentProvider !== void 0 ? active.subagentReasoningEffort : seed?.reasoningEffort;
|
|
1405
|
+
return {
|
|
1406
|
+
...draft,
|
|
1407
|
+
subagentProvider: provider,
|
|
1408
|
+
subagentModel: model,
|
|
1409
|
+
...effort === void 0 || effort === "" ? {} : { subagentReasoningEffort: effort }
|
|
1410
|
+
};
|
|
1411
|
+
});
|
|
1412
|
+
};
|
|
1413
|
+
const disableGroupCapability = (kind) => {
|
|
1414
|
+
setGroupDraft((draft) => {
|
|
1415
|
+
if (draft === void 0) return draft;
|
|
1416
|
+
if (kind === "compaction") return {
|
|
1417
|
+
...draft,
|
|
1418
|
+
compactProvider: "",
|
|
1419
|
+
compactModel: ""
|
|
1420
|
+
};
|
|
1421
|
+
if (kind === "autoReview") return {
|
|
1422
|
+
...draft,
|
|
1423
|
+
autoReviewProvider: "",
|
|
1424
|
+
autoReviewModel: ""
|
|
1425
|
+
};
|
|
1426
|
+
return {
|
|
1427
|
+
...draft,
|
|
1428
|
+
subagentProvider: "",
|
|
1429
|
+
subagentModel: "",
|
|
1430
|
+
subagentReasoningEffort: ""
|
|
1431
|
+
};
|
|
1432
|
+
});
|
|
1433
|
+
};
|
|
1434
|
+
const saveProviderGroupCard = () => {
|
|
1435
|
+
if (groupProvider === "" || groupDraft === void 0) return;
|
|
1436
|
+
run("providerGroups", () => props.saveProviderGroup(groupProvider, groupDraft));
|
|
1437
|
+
};
|
|
1438
|
+
const deleteProviderGroupCard = () => {
|
|
1439
|
+
if (groupProvider === "") return;
|
|
1440
|
+
run("providerGroups", () => props.saveProviderGroup(groupProvider, void 0));
|
|
1441
|
+
};
|
|
1185
1442
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("main", {
|
|
1186
1443
|
className: ModelSwitchSettings_module_css_default.section,
|
|
1187
1444
|
children: [
|
|
@@ -1197,6 +1454,10 @@ window.__ModuleLoader__.load({
|
|
|
1197
1454
|
className: ModelSwitchSettings_module_css_default.saved,
|
|
1198
1455
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("i", { className: ModelSwitchSettings_module_css_default.savedDot }), synced ? props.t("settingsSynced") : props.t("loading")]
|
|
1199
1456
|
}),
|
|
1457
|
+
catalogError === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1458
|
+
className: cx(ModelSwitchSettings_module_css_default.hint, ModelSwitchSettings_module_css_default.message),
|
|
1459
|
+
children: catalogError
|
|
1460
|
+
}),
|
|
1200
1461
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1201
1462
|
className: ModelSwitchSettings_module_css_default.group,
|
|
1202
1463
|
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
@@ -1262,150 +1523,92 @@ window.__ModuleLoader__.load({
|
|
|
1262
1523
|
}),
|
|
1263
1524
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1264
1525
|
className: ModelSwitchSettings_module_css_default.group,
|
|
1265
|
-
children: [
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1526
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
1527
|
+
className: ModelSwitchSettings_module_css_default.groupLabel,
|
|
1528
|
+
children: props.t("conversationRoutes")
|
|
1529
|
+
}), props.capabilities.centralSubagentRouting.available ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1530
|
+
title: props.t("subagent"),
|
|
1531
|
+
summary: subagentSummary,
|
|
1532
|
+
hint: props.t("subagentHelp"),
|
|
1533
|
+
icon: "subagent",
|
|
1534
|
+
open: open === "subagent" && subagentOn,
|
|
1535
|
+
onToggle: () => {
|
|
1536
|
+
if (subagentOn) toggle("subagent");
|
|
1537
|
+
},
|
|
1538
|
+
expandable: subagentOn,
|
|
1539
|
+
trailing: subagentDraft === void 0 ? void 0 : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1540
|
+
checked: subagentOn,
|
|
1541
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1542
|
+
label: props.t("subagent"),
|
|
1543
|
+
onChange: persistSubagentEnabled
|
|
1269
1544
|
}),
|
|
1270
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
value: draft.provider,
|
|
1289
|
-
disabled: controller.busy || !main.writable,
|
|
1290
|
-
choices: controller.providers,
|
|
1291
|
-
onChange: controller.setProvider
|
|
1292
|
-
}),
|
|
1293
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1294
|
-
label: props.t("model"),
|
|
1295
|
-
value: draft.model,
|
|
1296
|
-
disabled: controller.busy || !main.writable,
|
|
1297
|
-
choices: controller.models,
|
|
1298
|
-
onChange: controller.setModel
|
|
1299
|
-
}),
|
|
1300
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1301
|
-
label: props.t("effort"),
|
|
1302
|
-
value: draft.reasoningEffort ?? "",
|
|
1303
|
-
disabled: controller.busy || !main.writable,
|
|
1304
|
-
choices: controller.efforts,
|
|
1305
|
-
onChange: controller.setReasoningEffort
|
|
1306
|
-
})
|
|
1307
|
-
]
|
|
1545
|
+
children: subagentDraft === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1546
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1547
|
+
children: props.t("loading")
|
|
1548
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1549
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
1550
|
+
children: [
|
|
1551
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1552
|
+
label: props.t("provider"),
|
|
1553
|
+
value: subagentDraft.provider ?? "",
|
|
1554
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1555
|
+
choices: subagentChoices.providers,
|
|
1556
|
+
onChange: (provider) => {
|
|
1557
|
+
const first = groups.find((group) => group.id === provider)?.models[0];
|
|
1558
|
+
setSubagentDraft({
|
|
1559
|
+
mode: "fixed",
|
|
1560
|
+
...selectRouteModel(groups, provider, first?.id ?? subagentDraft.model ?? "")
|
|
1561
|
+
});
|
|
1562
|
+
}
|
|
1308
1563
|
}),
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1564
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1565
|
+
label: props.t("model"),
|
|
1566
|
+
value: subagentCatalogModel?.model.id ?? subagentDraft.model ?? "",
|
|
1567
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1568
|
+
choices: subagentChoices.models,
|
|
1569
|
+
onChange: (model) => {
|
|
1570
|
+
setSubagentDraft({
|
|
1571
|
+
mode: "fixed",
|
|
1572
|
+
...selectRouteModel(groups, subagentDraft.provider ?? "", model)
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1575
|
+
}),
|
|
1576
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1577
|
+
label: props.t("effort"),
|
|
1578
|
+
value: subagentDraft.reasoningEffort || subagentCatalogModel?.effort || "",
|
|
1579
|
+
disabled: busy === "subagent" || !subagent.writable,
|
|
1580
|
+
choices: subagentEfforts,
|
|
1581
|
+
onChange: (effort) => {
|
|
1582
|
+
const matched = subagentCatalogModel;
|
|
1583
|
+
setSubagentDraft({
|
|
1584
|
+
...subagentDraft,
|
|
1585
|
+
...matched?.model.id === void 0 ? {} : { model: matched.model.id },
|
|
1586
|
+
reasoningEffort: effort
|
|
1587
|
+
});
|
|
1321
1588
|
}
|
|
1322
1589
|
})
|
|
1323
|
-
]
|
|
1324
|
-
}),
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
if (subagentOn) toggle("subagent");
|
|
1590
|
+
]
|
|
1591
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
1592
|
+
t: props.t,
|
|
1593
|
+
busy: busy === "subagent",
|
|
1594
|
+
disabled: subagentDisabled,
|
|
1595
|
+
...message?.route === "subagent" ? { message: message.text } : {},
|
|
1596
|
+
onCancel: () => {
|
|
1597
|
+
resetSubagent();
|
|
1598
|
+
setMessage(void 0);
|
|
1333
1599
|
},
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
children: [
|
|
1347
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1348
|
-
label: props.t("provider"),
|
|
1349
|
-
value: subagentDraft.provider ?? "",
|
|
1350
|
-
disabled: busy === "subagent" || !subagent.writable,
|
|
1351
|
-
choices: subagentChoices.providers,
|
|
1352
|
-
onChange: (provider) => {
|
|
1353
|
-
const first = groups.find((group) => group.id === provider)?.models[0];
|
|
1354
|
-
setSubagentDraft({
|
|
1355
|
-
mode: "fixed",
|
|
1356
|
-
...selectRouteModel(groups, provider, first?.id ?? subagentDraft.model ?? "")
|
|
1357
|
-
});
|
|
1358
|
-
}
|
|
1359
|
-
}),
|
|
1360
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1361
|
-
label: props.t("model"),
|
|
1362
|
-
value: subagentCatalogModel?.model.id ?? subagentDraft.model ?? "",
|
|
1363
|
-
disabled: busy === "subagent" || !subagent.writable,
|
|
1364
|
-
choices: subagentChoices.models,
|
|
1365
|
-
onChange: (model) => {
|
|
1366
|
-
setSubagentDraft({
|
|
1367
|
-
mode: "fixed",
|
|
1368
|
-
...selectRouteModel(groups, subagentDraft.provider ?? "", model)
|
|
1369
|
-
});
|
|
1370
|
-
}
|
|
1371
|
-
}),
|
|
1372
|
-
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1373
|
-
label: props.t("effort"),
|
|
1374
|
-
value: subagentDraft.reasoningEffort || subagentCatalogModel?.effort || "",
|
|
1375
|
-
disabled: busy === "subagent" || !subagent.writable,
|
|
1376
|
-
choices: subagentEfforts,
|
|
1377
|
-
onChange: (effort) => {
|
|
1378
|
-
const matched = subagentCatalogModel;
|
|
1379
|
-
setSubagentDraft({
|
|
1380
|
-
...subagentDraft,
|
|
1381
|
-
...matched?.model.id === void 0 ? {} : { model: matched.model.id },
|
|
1382
|
-
reasoningEffort: effort
|
|
1383
|
-
});
|
|
1384
|
-
}
|
|
1385
|
-
})
|
|
1386
|
-
]
|
|
1387
|
-
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
1388
|
-
t: props.t,
|
|
1389
|
-
busy: busy === "subagent",
|
|
1390
|
-
disabled: subagentDisabled,
|
|
1391
|
-
...message?.route === "subagent" ? { message: message.text } : {},
|
|
1392
|
-
onCancel: () => {
|
|
1393
|
-
resetSubagent();
|
|
1394
|
-
setMessage(void 0);
|
|
1395
|
-
},
|
|
1396
|
-
onSave: saveSubagent
|
|
1397
|
-
})] })
|
|
1398
|
-
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1399
|
-
title: props.t("subagent"),
|
|
1400
|
-
summary: unavailable("centralSubagentRouting"),
|
|
1401
|
-
icon: "subagent",
|
|
1402
|
-
open: false,
|
|
1403
|
-
onToggle: () => {},
|
|
1404
|
-
disabled: true,
|
|
1405
|
-
badge: props.t("unavailable"),
|
|
1406
|
-
badgeWarn: true
|
|
1407
|
-
})
|
|
1408
|
-
]
|
|
1600
|
+
onSave: saveSubagent
|
|
1601
|
+
})] })
|
|
1602
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)(RouteCard, {
|
|
1603
|
+
title: props.t("subagent"),
|
|
1604
|
+
summary: unavailable("centralSubagentRouting"),
|
|
1605
|
+
icon: "subagent",
|
|
1606
|
+
open: false,
|
|
1607
|
+
onToggle: () => {},
|
|
1608
|
+
disabled: true,
|
|
1609
|
+
badge: props.t("unavailable"),
|
|
1610
|
+
badgeWarn: true
|
|
1611
|
+
})]
|
|
1409
1612
|
}),
|
|
1410
1613
|
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1411
1614
|
className: ModelSwitchSettings_module_css_default.group,
|
|
@@ -1681,6 +1884,229 @@ window.__ModuleLoader__.load({
|
|
|
1681
1884
|
] })
|
|
1682
1885
|
})
|
|
1683
1886
|
]
|
|
1887
|
+
}),
|
|
1888
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("section", {
|
|
1889
|
+
className: ModelSwitchSettings_module_css_default.group,
|
|
1890
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
|
|
1891
|
+
className: ModelSwitchSettings_module_css_default.groupLabel,
|
|
1892
|
+
children: props.t("providerGroups")
|
|
1893
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(RouteCard, {
|
|
1894
|
+
title: props.t("providerGroupsCard"),
|
|
1895
|
+
summary: props.t("providerGroupsSummary"),
|
|
1896
|
+
hint: props.t("providerGroupsHelp"),
|
|
1897
|
+
icon: "providerGroups",
|
|
1898
|
+
open: open === "providerGroups",
|
|
1899
|
+
onToggle: () => {
|
|
1900
|
+
toggle("providerGroups");
|
|
1901
|
+
},
|
|
1902
|
+
...configuredGroupCount > 0 ? { badge: String(configuredGroupCount) } : {},
|
|
1903
|
+
children: [
|
|
1904
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1905
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1906
|
+
children: props.t("providerGroupsBodyHelp")
|
|
1907
|
+
}),
|
|
1908
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1909
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
1910
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1911
|
+
label: props.t("provider"),
|
|
1912
|
+
value: groupProvider,
|
|
1913
|
+
disabled: groupControlsDisabled,
|
|
1914
|
+
choices: groupProviderChoices,
|
|
1915
|
+
onChange: setGroupProvider
|
|
1916
|
+
})
|
|
1917
|
+
}),
|
|
1918
|
+
groupProvider === "" || groupDraft === void 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1919
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1920
|
+
children: props.t("providerGroupPick")
|
|
1921
|
+
}) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
|
|
1922
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1923
|
+
className: ModelSwitchSettings_module_css_default.rowCard,
|
|
1924
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1925
|
+
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
1926
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1927
|
+
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
1928
|
+
children: props.t("compaction")
|
|
1929
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1930
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1931
|
+
children: groupCompaction === void 0 ? props.t("compactionFollow") : routeName(groups, groupCompaction)
|
|
1932
|
+
})]
|
|
1933
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1934
|
+
checked: groupCompaction !== void 0,
|
|
1935
|
+
disabled: groupControlsDisabled,
|
|
1936
|
+
label: props.t("compaction"),
|
|
1937
|
+
onChange: (value) => {
|
|
1938
|
+
if (value) enableGroupCapability("compaction");
|
|
1939
|
+
else disableGroupCapability("compaction");
|
|
1940
|
+
}
|
|
1941
|
+
})]
|
|
1942
|
+
}),
|
|
1943
|
+
groupCompaction === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1944
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
1945
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1946
|
+
label: props.t("provider"),
|
|
1947
|
+
value: groupCompaction.provider,
|
|
1948
|
+
disabled: groupControlsDisabled,
|
|
1949
|
+
choices: groupCompactionChoices.providers,
|
|
1950
|
+
onChange: (provider) => {
|
|
1951
|
+
const first = groups.find((item) => item.id === provider)?.models[0];
|
|
1952
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
1953
|
+
...draft,
|
|
1954
|
+
compactProvider: provider,
|
|
1955
|
+
compactModel: first?.id ?? ""
|
|
1956
|
+
});
|
|
1957
|
+
}
|
|
1958
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1959
|
+
label: props.t("model"),
|
|
1960
|
+
value: groupCompaction.model,
|
|
1961
|
+
disabled: groupControlsDisabled,
|
|
1962
|
+
choices: groupCompactionChoices.models,
|
|
1963
|
+
onChange: (model) => {
|
|
1964
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
1965
|
+
...draft,
|
|
1966
|
+
compactModel: model
|
|
1967
|
+
});
|
|
1968
|
+
}
|
|
1969
|
+
})]
|
|
1970
|
+
}),
|
|
1971
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1972
|
+
className: ModelSwitchSettings_module_css_default.rowCard,
|
|
1973
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1974
|
+
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
1975
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1976
|
+
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
1977
|
+
children: props.t("autoReview")
|
|
1978
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
1979
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
1980
|
+
children: groupReview === void 0 ? props.t("autoReviewFollow") : routeName(groups, groupReview)
|
|
1981
|
+
})]
|
|
1982
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
1983
|
+
checked: groupReview !== void 0,
|
|
1984
|
+
disabled: groupControlsDisabled,
|
|
1985
|
+
label: props.t("autoReview"),
|
|
1986
|
+
onChange: (value) => {
|
|
1987
|
+
if (value) enableGroupCapability("autoReview");
|
|
1988
|
+
else disableGroupCapability("autoReview");
|
|
1989
|
+
}
|
|
1990
|
+
})]
|
|
1991
|
+
}),
|
|
1992
|
+
groupReview === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1993
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
1994
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
1995
|
+
label: props.t("provider"),
|
|
1996
|
+
value: groupReview.provider,
|
|
1997
|
+
disabled: groupControlsDisabled,
|
|
1998
|
+
choices: groupReviewChoices.providers,
|
|
1999
|
+
onChange: (provider) => {
|
|
2000
|
+
const first = groups.find((item) => item.id === provider)?.models[0];
|
|
2001
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2002
|
+
...draft,
|
|
2003
|
+
autoReviewProvider: provider,
|
|
2004
|
+
autoReviewModel: first?.id ?? ""
|
|
2005
|
+
});
|
|
2006
|
+
}
|
|
2007
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2008
|
+
label: props.t("model"),
|
|
2009
|
+
value: groupReview.model,
|
|
2010
|
+
disabled: groupControlsDisabled,
|
|
2011
|
+
choices: groupReviewChoices.models,
|
|
2012
|
+
onChange: (model) => {
|
|
2013
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2014
|
+
...draft,
|
|
2015
|
+
autoReviewModel: model
|
|
2016
|
+
});
|
|
2017
|
+
}
|
|
2018
|
+
})]
|
|
2019
|
+
}),
|
|
2020
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2021
|
+
className: ModelSwitchSettings_module_css_default.rowCard,
|
|
2022
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2023
|
+
className: ModelSwitchSettings_module_css_default.rowCopy,
|
|
2024
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
2025
|
+
className: ModelSwitchSettings_module_css_default.rowTitle,
|
|
2026
|
+
children: props.t("subagent")
|
|
2027
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
2028
|
+
className: ModelSwitchSettings_module_css_default.hint,
|
|
2029
|
+
children: groupSubagent === void 0 ? props.t("subagentOff") : routeName(groups, groupSubagent)
|
|
2030
|
+
})]
|
|
2031
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(InstantSwitch, {
|
|
2032
|
+
checked: groupSubagent !== void 0,
|
|
2033
|
+
disabled: groupControlsDisabled,
|
|
2034
|
+
label: props.t("subagent"),
|
|
2035
|
+
onChange: (value) => {
|
|
2036
|
+
if (value) enableGroupCapability("subagent");
|
|
2037
|
+
else disableGroupCapability("subagent");
|
|
2038
|
+
}
|
|
2039
|
+
})]
|
|
2040
|
+
}),
|
|
2041
|
+
groupSubagent === void 0 ? null : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2042
|
+
className: ModelSwitchSettings_module_css_default.formGrid,
|
|
2043
|
+
children: [
|
|
2044
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2045
|
+
label: props.t("provider"),
|
|
2046
|
+
value: groupSubagent.provider,
|
|
2047
|
+
disabled: groupControlsDisabled,
|
|
2048
|
+
choices: groupSubagentChoices.providers,
|
|
2049
|
+
onChange: (provider) => {
|
|
2050
|
+
const first = groups.find((item) => item.id === provider)?.models[0];
|
|
2051
|
+
const routed = selectRouteModel(groups, provider, first?.id ?? groupSubagent.model);
|
|
2052
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2053
|
+
...draft,
|
|
2054
|
+
subagentProvider: routed.provider,
|
|
2055
|
+
subagentModel: routed.model,
|
|
2056
|
+
subagentReasoningEffort: routed.reasoningEffort ?? ""
|
|
2057
|
+
});
|
|
2058
|
+
}
|
|
2059
|
+
}),
|
|
2060
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2061
|
+
label: props.t("model"),
|
|
2062
|
+
value: groupSubagent.model,
|
|
2063
|
+
disabled: groupControlsDisabled,
|
|
2064
|
+
choices: groupSubagentChoices.models,
|
|
2065
|
+
onChange: (model) => {
|
|
2066
|
+
const routed = selectRouteModel(groups, groupSubagent.provider, model);
|
|
2067
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2068
|
+
...draft,
|
|
2069
|
+
subagentProvider: routed.provider,
|
|
2070
|
+
subagentModel: routed.model,
|
|
2071
|
+
subagentReasoningEffort: routed.reasoningEffort ?? ""
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
}),
|
|
2075
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Field, {
|
|
2076
|
+
label: props.t("effort"),
|
|
2077
|
+
value: groupDraft.subagentReasoningEffort ?? "",
|
|
2078
|
+
disabled: groupControlsDisabled,
|
|
2079
|
+
choices: groupSubEfforts,
|
|
2080
|
+
onChange: (effort) => {
|
|
2081
|
+
setGroupDraft((draft) => draft === void 0 ? draft : {
|
|
2082
|
+
...draft,
|
|
2083
|
+
subagentReasoningEffort: effort
|
|
2084
|
+
});
|
|
2085
|
+
}
|
|
2086
|
+
})
|
|
2087
|
+
]
|
|
2088
|
+
}),
|
|
2089
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)(Actions, {
|
|
2090
|
+
t: props.t,
|
|
2091
|
+
busy: busy === "providerGroups",
|
|
2092
|
+
disabled: groupControlsDisabled || groupProvider === "",
|
|
2093
|
+
...message?.route === "providerGroups" ? { message: message.text } : {},
|
|
2094
|
+
extra: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
2095
|
+
type: "button",
|
|
2096
|
+
className: cx(ModelSwitchSettings_module_css_default.button, ModelSwitchSettings_module_css_default.secondaryButton),
|
|
2097
|
+
disabled: groupControlsDisabled || groupOf(providerGroups, groupProvider) === void 0,
|
|
2098
|
+
onClick: deleteProviderGroupCard,
|
|
2099
|
+
children: props.t("providerGroupDelete")
|
|
2100
|
+
}),
|
|
2101
|
+
onCancel: () => {
|
|
2102
|
+
resyncGroupDraft();
|
|
2103
|
+
setMessage(void 0);
|
|
2104
|
+
},
|
|
2105
|
+
onSave: saveProviderGroupCard
|
|
2106
|
+
})
|
|
2107
|
+
] })
|
|
2108
|
+
]
|
|
2109
|
+
})]
|
|
1684
2110
|
})
|
|
1685
2111
|
]
|
|
1686
2112
|
});
|
|
@@ -1690,22 +2116,21 @@ window.__ModuleLoader__.load({
|
|
|
1690
2116
|
const zh$1 = {
|
|
1691
2117
|
nav: "模型切换",
|
|
1692
2118
|
title: "模型切换",
|
|
1693
|
-
subtitle: "
|
|
2119
|
+
subtitle: "为子代理与能力路由设置固定模型;当前会话模型用输入框旁的选择器直接切换。",
|
|
1694
2120
|
conversationRoutes: "对话路由",
|
|
1695
2121
|
capabilityRoutes: "能力路由",
|
|
1696
2122
|
sendProtection: "发送保护",
|
|
1697
2123
|
settingsSynced: "设置已同步",
|
|
1698
2124
|
remoteSettingsUnavailable: "官方 DSH 在非本机浏览器禁用配置读取与写入;请在 Host 的 127.0.0.1 页面打开设置。",
|
|
1699
|
-
defaultBadge: "默认",
|
|
1700
2125
|
cancel: "取消",
|
|
1701
2126
|
compactOnSwitch: "切换时压缩",
|
|
1702
2127
|
compactOnSwitchHelp: "发送且换了模型时,先按目标窗口检查上下文;超了就用切换前的模型压一次历史,再继续请求。只选模型、不发送,不会压缩。官方自动压缩和 /compact 不受影响。压缩会产生额外模型用量,摘要可能省略细节。",
|
|
1703
|
-
main: "主模型",
|
|
1704
2128
|
provider: "提供商",
|
|
1705
2129
|
model: "模型",
|
|
1706
2130
|
providerDefault: "使用提供商默认值",
|
|
1707
2131
|
providerDefaultShort: "Provider 默认",
|
|
1708
2132
|
effort: "推理强度",
|
|
2133
|
+
effortPinned: "已固定",
|
|
1709
2134
|
save: "保存",
|
|
1710
2135
|
saving: "保存中…",
|
|
1711
2136
|
saved: "已保存",
|
|
@@ -1719,10 +2144,8 @@ window.__ModuleLoader__.load({
|
|
|
1719
2144
|
imageHelp: "统一 generate_image 会调用所选 Codex 或 Grok Adapter;Provider 原有图片工具仍保留。",
|
|
1720
2145
|
unavailable: "未接入",
|
|
1721
2146
|
loading: "正在加载设置…",
|
|
1722
|
-
readonly: "设置为只读",
|
|
1723
2147
|
requestFailed: "保存失败",
|
|
1724
2148
|
catalogFailed: "无法加载模型目录。",
|
|
1725
|
-
conflict: "设置已在其他位置更改,请检查最新值后重试。",
|
|
1726
2149
|
autoReview: "自动审查模型",
|
|
1727
2150
|
autoReviewFollow: "跟随会话模型",
|
|
1728
2151
|
autoReviewHelp: "为官方 auto-review(Auto 权限预设的逐调用审查)指定审查模型;需启用官方 auto-review 能力包。关闭时审查跟随当前会话模型。",
|
|
@@ -1731,6 +2154,13 @@ window.__ModuleLoader__.load({
|
|
|
1731
2154
|
compactionFollow: "跟随会话模型",
|
|
1732
2155
|
compactionHelp: "为官方压缩(/compact 与官方自动压缩)指定摘要模型;关闭(两个字段都留空)时使用官方默认,即当前会话的目标模型。",
|
|
1733
2156
|
compactionRouteHelp: "摘要请求固定使用这里的 Provider/模型,与会话目标模型无关;保存后对下一次压缩立即生效。保存时若恰有压缩进行中,可能被打断并在下次重试。",
|
|
2157
|
+
providerGroups: "供应商配置组",
|
|
2158
|
+
providerGroupsCard: "按供应商预设路由",
|
|
2159
|
+
providerGroupsSummary: "会话模型切换到该供应商时自动应用对应组",
|
|
2160
|
+
providerGroupsHelp: "为压缩、子代理、审查按供应商分别保存路由。",
|
|
2161
|
+
providerGroupsBodyHelp: "每个供应商保存一组压缩、子代理、审查路由;本会话的模型切换到该供应商时(无论通过插件的模型选择器还是 TUI 等其他界面),这三条路由自动写入上方对应卡片。切换到未配置的供应商时现有路由保持不变;应用会覆盖三张卡片的当前值(含手动修改)。",
|
|
2162
|
+
providerGroupPick: "选择供应商以编辑其配置组。",
|
|
2163
|
+
providerGroupDelete: "删除该组",
|
|
1734
2164
|
"reason.central-subagent-routing": "Alpha.4 没有全局子代理启动路由接口。",
|
|
1735
2165
|
"reason.packaged-preset-roots": "Alpha.4 不支持插件提供额外 preset root。",
|
|
1736
2166
|
"reason.tool-owner-suppression": "Alpha.4 没有工具所有者或来源抑制接口。",
|
|
@@ -1741,22 +2171,21 @@ window.__ModuleLoader__.load({
|
|
|
1741
2171
|
const en$1 = {
|
|
1742
2172
|
nav: "Model Switch",
|
|
1743
2173
|
title: "Model Switch",
|
|
1744
|
-
subtitle: "
|
|
2174
|
+
subtitle: "Pin routes for Subagents and capability tools; switch the active session model through the composer picker.",
|
|
1745
2175
|
conversationRoutes: "Conversation routes",
|
|
1746
2176
|
capabilityRoutes: "Capability routes",
|
|
1747
2177
|
sendProtection: "Send protection",
|
|
1748
2178
|
settingsSynced: "Settings synced",
|
|
1749
2179
|
remoteSettingsUnavailable: "Official DSH disables settings reads and writes in non-loopback browsers. Open Settings on the Host loopback URL to edit model routes.",
|
|
1750
|
-
defaultBadge: "Default",
|
|
1751
2180
|
cancel: "Cancel",
|
|
1752
2181
|
compactOnSwitch: "Compact on switch",
|
|
1753
2182
|
compactOnSwitchHelp: "When a sent message uses a new model, check the target window first. If needed, compact history with the previous model, then continue. Choosing a model does not compact. Official automatic compaction and /compact are unchanged. Compaction uses extra model quota and may omit details.",
|
|
1754
|
-
main: "Main model",
|
|
1755
2183
|
provider: "Provider",
|
|
1756
2184
|
model: "Model",
|
|
1757
2185
|
providerDefault: "Provider default",
|
|
1758
2186
|
providerDefaultShort: "Provider default",
|
|
1759
2187
|
effort: "Reasoning effort",
|
|
2188
|
+
effortPinned: "Pinned",
|
|
1760
2189
|
save: "Save",
|
|
1761
2190
|
saving: "Saving…",
|
|
1762
2191
|
saved: "Saved",
|
|
@@ -1770,10 +2199,8 @@ window.__ModuleLoader__.load({
|
|
|
1770
2199
|
imageHelp: "The stable generate_image tool calls the selected Codex or Grok Adapter; existing provider image tools remain available.",
|
|
1771
2200
|
unavailable: "Unavailable",
|
|
1772
2201
|
loading: "Loading settings…",
|
|
1773
|
-
readonly: "Settings are read-only",
|
|
1774
2202
|
requestFailed: "Save failed",
|
|
1775
2203
|
catalogFailed: "Could not load the model catalog.",
|
|
1776
|
-
conflict: "Settings changed elsewhere. Review the latest values and retry.",
|
|
1777
2204
|
autoReview: "Auto review model",
|
|
1778
2205
|
autoReviewFollow: "Follows the session model",
|
|
1779
2206
|
autoReviewHelp: "Pin the reviewer model for the official auto-review gate (per-call review under the Auto permission preset). Requires the official auto-review bundle. When off, reviews follow the session model.",
|
|
@@ -1782,6 +2209,13 @@ window.__ModuleLoader__.load({
|
|
|
1782
2209
|
compactionFollow: "Follows the conversation model",
|
|
1783
2210
|
compactionHelp: "Pick the summarization model for official compaction (/compact and official automatic compaction). When off (both fields empty), the official default applies: the conversation target model.",
|
|
1784
2211
|
compactionRouteHelp: "Summarization requests use exactly this provider/model regardless of the conversation target; saved changes apply to the very next compaction. Saving while a compaction is running may interrupt it; it retries next time.",
|
|
2212
|
+
providerGroups: "Provider groups",
|
|
2213
|
+
providerGroupsCard: "Per-provider route presets",
|
|
2214
|
+
providerGroupsSummary: "Applied automatically when this session switches to that provider",
|
|
2215
|
+
providerGroupsHelp: "Save compaction, subagent, and review routes per provider.",
|
|
2216
|
+
providerGroupsBodyHelp: "Each provider stores one compaction, subagent, and review route group. When this session switches to that provider — through the plugin picker or any other surface such as the TUI — these three routes are written into the cards above. Switching to a provider without a group leaves the current routes untouched. Applying a group overwrites those three cards, including manual edits.",
|
|
2217
|
+
providerGroupPick: "Pick a provider to edit its group.",
|
|
2218
|
+
providerGroupDelete: "Delete group",
|
|
1785
2219
|
"reason.central-subagent-routing": "Alpha.4 exposes no global Subagent start-routing seam.",
|
|
1786
2220
|
"reason.packaged-preset-roots": "Alpha.4 exposes no plugin-owned preset root.",
|
|
1787
2221
|
"reason.tool-owner-suppression": "Alpha.4 exposes no tool owner or provenance suppression seam.",
|
|
@@ -1842,13 +2276,8 @@ window.__ModuleLoader__.load({
|
|
|
1842
2276
|
}
|
|
1843
2277
|
async function approvePlanReview(args) {
|
|
1844
2278
|
const current = args.current;
|
|
1845
|
-
|
|
1846
|
-
committed: true,
|
|
1847
|
-
mainDefaultRestored: true
|
|
1848
|
-
} : await args.select(args.selection);
|
|
1849
|
-
if (!commit.committed) return false;
|
|
2279
|
+
if (!(current !== void 0 && current !== null && current.provider === args.selection.provider && current.model === args.selection.model && current.reasoningEffort === args.selection.reasoningEffort ? { committed: true } : await args.select(args.selection)).committed) return false;
|
|
1850
2280
|
await args.answer();
|
|
1851
|
-
if (!commit.mainDefaultRestored) args.onNotRestored?.(args.selection.model);
|
|
1852
2281
|
return true;
|
|
1853
2282
|
}
|
|
1854
2283
|
function planActionView(state, available, hasExecution) {
|
|
@@ -2552,7 +2981,7 @@ window.__ModuleLoader__.load({
|
|
|
2552
2981
|
children: mark
|
|
2553
2982
|
});
|
|
2554
2983
|
}
|
|
2555
|
-
function ComposerPicker({ locked, providerLock = null, agentLocked = false, available,
|
|
2984
|
+
function ComposerPicker({ locked, providerLock = null, agentLocked = false, available, directory, t, draft, onDraftChange, embedded, tone, resolveInteractionOperations, roleOf }) {
|
|
2556
2985
|
const { snapshot: state, getDirectorySnapshot, load, select } = directory;
|
|
2557
2986
|
const [pane, setPane] = (0, react.useState)("root");
|
|
2558
2987
|
const [searching, setSearching] = (0, react.useState)(false);
|
|
@@ -2635,16 +3064,7 @@ window.__ModuleLoader__.load({
|
|
|
2635
3064
|
setQuery("");
|
|
2636
3065
|
};
|
|
2637
3066
|
const settleSelection = (outcome) => {
|
|
2638
|
-
if (outcome.committed)
|
|
2639
|
-
if (!outcome.mainDefaultRestored) {
|
|
2640
|
-
toastSeq.current += 1;
|
|
2641
|
-
setToast({
|
|
2642
|
-
seq: toastSeq.current,
|
|
2643
|
-
text: t("plan.notRestored")
|
|
2644
|
-
});
|
|
2645
|
-
}
|
|
2646
|
-
return;
|
|
2647
|
-
}
|
|
3067
|
+
if (outcome.committed) return;
|
|
2648
3068
|
const message = getDirectorySnapshot().error;
|
|
2649
3069
|
if (message !== null) {
|
|
2650
3070
|
toastSeq.current += 1;
|
|
@@ -3055,11 +3475,11 @@ window.__ModuleLoader__.load({
|
|
|
3055
3475
|
ref: triggerRef,
|
|
3056
3476
|
type: "button",
|
|
3057
3477
|
className: ComposerPicker_module_css_default.trigger,
|
|
3058
|
-
"aria-label":
|
|
3478
|
+
"aria-label": triggerAria,
|
|
3059
3479
|
"aria-haspopup": "menu",
|
|
3060
3480
|
"aria-expanded": open,
|
|
3061
3481
|
"aria-controls": open ? `${id}-menu` : void 0,
|
|
3062
|
-
title:
|
|
3482
|
+
title: triggerLabel,
|
|
3063
3483
|
disabled: locked,
|
|
3064
3484
|
onPointerDown: onTriggerPointerDown,
|
|
3065
3485
|
onClick: onTriggerClick,
|
|
@@ -3284,132 +3704,64 @@ window.__ModuleLoader__.load({
|
|
|
3284
3704
|
}, operations);
|
|
3285
3705
|
}
|
|
3286
3706
|
//#endregion
|
|
3287
|
-
//#region src/client/picker/
|
|
3288
|
-
/** Bound on how long the unawaited Host default write is awaited. */
|
|
3289
|
-
const DEFAULT_WRITE_WAIT_MS = 750;
|
|
3290
|
-
function defaultWait(ms) {
|
|
3291
|
-
return new Promise((resolve) => {
|
|
3292
|
-
setTimeout(resolve, ms);
|
|
3293
|
-
});
|
|
3294
|
-
}
|
|
3295
|
-
/** A fenced snapshot: ready, Host-synced, writable, with a concrete revision. */
|
|
3296
|
-
function fenced(snapshot) {
|
|
3297
|
-
if (snapshot.status !== "ready" || snapshot.mode !== "host" || !snapshot.writable) return void 0;
|
|
3298
|
-
if (snapshot.value === void 0 || snapshot.revision === void 0) return void 0;
|
|
3299
|
-
return {
|
|
3300
|
-
revision: snapshot.revision,
|
|
3301
|
-
value: snapshot.value
|
|
3302
|
-
};
|
|
3303
|
-
}
|
|
3304
|
-
/** Field writes restoring one complete Main selection; the Host replaces the section. */
|
|
3305
|
-
function mainDefaultOps(selection) {
|
|
3306
|
-
return [
|
|
3307
|
-
{
|
|
3308
|
-
op: "set",
|
|
3309
|
-
path: ["provider"],
|
|
3310
|
-
value: selection.provider
|
|
3311
|
-
},
|
|
3312
|
-
{
|
|
3313
|
-
op: "set",
|
|
3314
|
-
path: ["model"],
|
|
3315
|
-
value: selection.model
|
|
3316
|
-
},
|
|
3317
|
-
selection.reasoningEffort === void 0 || selection.reasoningEffort === "" ? {
|
|
3318
|
-
op: "unset",
|
|
3319
|
-
path: ["reasoningEffort"]
|
|
3320
|
-
} : {
|
|
3321
|
-
op: "set",
|
|
3322
|
-
path: ["reasoningEffort"],
|
|
3323
|
-
value: selection.reasoningEffort
|
|
3324
|
-
}
|
|
3325
|
-
];
|
|
3326
|
-
}
|
|
3327
|
-
/** An effort never leaks across a comparison: absent and cleared are the same selection. */
|
|
3328
|
-
function sameMainSelection(left, right) {
|
|
3329
|
-
if (left === void 0) return false;
|
|
3330
|
-
const leftEffort = left.reasoningEffort === void 0 || left.reasoningEffort === "" ? void 0 : left.reasoningEffort;
|
|
3331
|
-
const rightEffort = right.reasoningEffort === void 0 || right.reasoningEffort === "" ? void 0 : right.reasoningEffort;
|
|
3332
|
-
return left.provider === right.provider && left.model === right.model && leftEffort === rightEffort;
|
|
3333
|
-
}
|
|
3334
|
-
/** Resolve as soon as the revision leaves `revision`, or after the bounded wait. */
|
|
3335
|
-
function waitForAdvance(form, revision, wait, timeoutMs) {
|
|
3336
|
-
const current = form.getSnapshot();
|
|
3337
|
-
if (current.revision !== revision) return Promise.resolve(current);
|
|
3338
|
-
return new Promise((resolve) => {
|
|
3339
|
-
let settled = false;
|
|
3340
|
-
const finish = (snapshot) => {
|
|
3341
|
-
if (settled) return;
|
|
3342
|
-
settled = true;
|
|
3343
|
-
unsubscribe();
|
|
3344
|
-
resolve(snapshot);
|
|
3345
|
-
};
|
|
3346
|
-
const unsubscribe = form.subscribe(() => {
|
|
3347
|
-
const next = form.getSnapshot();
|
|
3348
|
-
if (next.revision !== revision) finish(next);
|
|
3349
|
-
});
|
|
3350
|
-
wait(timeoutMs).then(() => {
|
|
3351
|
-
finish(form.getSnapshot());
|
|
3352
|
-
});
|
|
3353
|
-
});
|
|
3354
|
-
}
|
|
3707
|
+
//#region src/client/picker/provider-group-watch.ts
|
|
3355
3708
|
/**
|
|
3356
|
-
*
|
|
3357
|
-
*
|
|
3358
|
-
* Never throws and never blocks a Plan answer: a failed compensation is
|
|
3359
|
-
* reported, not enforced, because the Session selection the human asked for is
|
|
3360
|
-
* already installed by the time this runs.
|
|
3709
|
+
* Watch one directory store and apply groups on provider changes.
|
|
3361
3710
|
*
|
|
3362
|
-
*
|
|
3363
|
-
*
|
|
3364
|
-
*
|
|
3365
|
-
*
|
|
3711
|
+
* - The first observed provider (or the one already loaded) is the baseline
|
|
3712
|
+
* and never triggers a write, so mounting and initial load stay silent.
|
|
3713
|
+
* - Applies are serialized: a change arriving mid-apply coalesces to the
|
|
3714
|
+
* latest target and runs once the current apply finishes, so two writes
|
|
3715
|
+
* never interleave pair writes.
|
|
3716
|
+
* - `current` being null (catalog still loading) is ignored, not a change.
|
|
3717
|
+
* @param options - the store to watch and the apply to run.
|
|
3718
|
+
* @returns the watch handle.
|
|
3366
3719
|
*/
|
|
3367
|
-
|
|
3368
|
-
const
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
const
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
const observed = fenced(form.getSnapshot());
|
|
3378
|
-
if (observed === void 0) return {
|
|
3379
|
-
restored: true,
|
|
3380
|
-
conflict: false
|
|
3381
|
-
};
|
|
3382
|
-
if (observed.revision === captured.revision) return {
|
|
3383
|
-
restored: true,
|
|
3384
|
-
conflict: false
|
|
3385
|
-
};
|
|
3386
|
-
if (sameMainSelection(observed.value, captured.value)) return {
|
|
3387
|
-
restored: true,
|
|
3388
|
-
conflict: false
|
|
3389
|
-
};
|
|
3390
|
-
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
3391
|
-
const snapshot = fenced(form.getSnapshot());
|
|
3392
|
-
if (snapshot === void 0) return {
|
|
3393
|
-
restored: false,
|
|
3394
|
-
conflict: false
|
|
3395
|
-
};
|
|
3396
|
-
if (sameMainSelection(snapshot.value, captured.value)) return {
|
|
3397
|
-
restored: true,
|
|
3398
|
-
conflict: false
|
|
3399
|
-
};
|
|
3400
|
-
if (await form.mutate(mainDefaultOps(captured.value), snapshot.revision)) return {
|
|
3401
|
-
restored: true,
|
|
3402
|
-
conflict: false
|
|
3403
|
-
};
|
|
3404
|
-
if (form.getSnapshot().revision === snapshot.revision) return {
|
|
3405
|
-
restored: false,
|
|
3406
|
-
conflict: false
|
|
3407
|
-
};
|
|
3720
|
+
function createProviderGroupWatch(options) {
|
|
3721
|
+
const onFailure = options.onFailure ?? ((error) => {
|
|
3722
|
+
console.warn("dsh-model-switch: provider group apply failed", error);
|
|
3723
|
+
});
|
|
3724
|
+
let baselined = false;
|
|
3725
|
+
let lastSeen;
|
|
3726
|
+
const initial = options.source.getSnapshot().current;
|
|
3727
|
+
if (initial !== null) {
|
|
3728
|
+
baselined = true;
|
|
3729
|
+
lastSeen = initial.provider;
|
|
3408
3730
|
}
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3731
|
+
let running = false;
|
|
3732
|
+
let pending;
|
|
3733
|
+
const run = async (provider) => {
|
|
3734
|
+
running = true;
|
|
3735
|
+
try {
|
|
3736
|
+
await options.apply(provider);
|
|
3737
|
+
} catch (error) {
|
|
3738
|
+
onFailure(error);
|
|
3739
|
+
} finally {
|
|
3740
|
+
running = false;
|
|
3741
|
+
if (pending !== void 0) {
|
|
3742
|
+
const next = pending;
|
|
3743
|
+
pending = void 0;
|
|
3744
|
+
run(next);
|
|
3745
|
+
}
|
|
3746
|
+
}
|
|
3747
|
+
};
|
|
3748
|
+
const check = () => {
|
|
3749
|
+
const current = options.source.getSnapshot().current;
|
|
3750
|
+
if (current === null) return;
|
|
3751
|
+
if (!baselined) {
|
|
3752
|
+
baselined = true;
|
|
3753
|
+
lastSeen = current.provider;
|
|
3754
|
+
return;
|
|
3755
|
+
}
|
|
3756
|
+
if (current.provider === lastSeen) return;
|
|
3757
|
+
lastSeen = current.provider;
|
|
3758
|
+
if (running) {
|
|
3759
|
+
pending = current.provider;
|
|
3760
|
+
return;
|
|
3761
|
+
}
|
|
3762
|
+
run(current.provider);
|
|
3412
3763
|
};
|
|
3764
|
+
return { dispose: options.source.subscribe(check) };
|
|
3413
3765
|
}
|
|
3414
3766
|
//#endregion
|
|
3415
3767
|
//#region src/picker/pending-dismissal.ts
|
|
@@ -3625,12 +3977,6 @@ window.__ModuleLoader__.load({
|
|
|
3625
3977
|
throw cause instanceof PlanDismissalUnsupportedError ? new Error(unsupportedMessage, { cause }) : new Error(rejectedMessage, { cause });
|
|
3626
3978
|
}
|
|
3627
3979
|
}
|
|
3628
|
-
/** Match the model seat's admission guard before letting either picker offer a change. */
|
|
3629
|
-
function mainDefaultsUnavailableReason(snapshot, t) {
|
|
3630
|
-
if (snapshot.mode === "memory") return t("settings.remoteUnavailable");
|
|
3631
|
-
if (snapshot.status === "loading") return t("settings.loading");
|
|
3632
|
-
if (snapshot.status !== "ready" || snapshot.mode !== "host" || !snapshot.writable || snapshot.value === void 0 || snapshot.revision === void 0) return t("settings.unavailable");
|
|
3633
|
-
}
|
|
3634
3980
|
/** Inline failed lock-read status; history and log reading stay unaffected. */
|
|
3635
3981
|
function ProviderLockHint(props) {
|
|
3636
3982
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
@@ -3646,7 +3992,6 @@ window.__ModuleLoader__.load({
|
|
|
3646
3992
|
function PlanReviewCard(props) {
|
|
3647
3993
|
const snapshot = props.useDirectory((value) => value);
|
|
3648
3994
|
const order = props.useProviderOrder((value) => value);
|
|
3649
|
-
const settingsUnavailableReason = mainDefaultsUnavailableReason((0, react.useSyncExternalStore)(props.subscribeMainDefaults, props.getMainDefaultsSnapshot), props.t);
|
|
3650
3995
|
const lock = (0, react.useSyncExternalStore)(props.providerLockStore.subscribe, props.providerLockStore.getSnapshot);
|
|
3651
3996
|
const phase = props.useInput((input) => input.phase);
|
|
3652
3997
|
const blank = props.useSession((session) => session.blank);
|
|
@@ -3682,7 +4027,6 @@ window.__ModuleLoader__.load({
|
|
|
3682
4027
|
providerLock,
|
|
3683
4028
|
agentLocked,
|
|
3684
4029
|
lockFailed: lock.failed,
|
|
3685
|
-
...settingsUnavailableReason === void 0 ? {} : { settingsUnavailableReason },
|
|
3686
4030
|
directory: pickerDirectoryViewOrdered(snapshot, props, order, props.catalogRoutes?.() ?? {}),
|
|
3687
4031
|
t: props.t,
|
|
3688
4032
|
goalRemote: props.goalRemote,
|
|
@@ -3691,7 +4035,7 @@ window.__ModuleLoader__.load({
|
|
|
3691
4035
|
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf }
|
|
3692
4036
|
}, props.matched.key);
|
|
3693
4037
|
}
|
|
3694
|
-
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed,
|
|
4038
|
+
function PlanReviewState({ matched, review, available, providerLock, agentLocked, lockFailed, directory, t, goalRemote, planPreview, resolveInteractionOperations, roleOf }) {
|
|
3695
4039
|
const { snapshot, getDirectorySnapshot, load, select } = directory;
|
|
3696
4040
|
const [execution, setExecution] = (0, react.useState)(snapshot.current ?? void 0);
|
|
3697
4041
|
const [feedback, setFeedback] = (0, react.useState)("");
|
|
@@ -3754,8 +4098,7 @@ window.__ModuleLoader__.load({
|
|
|
3754
4098
|
setNotice(null);
|
|
3755
4099
|
load();
|
|
3756
4100
|
};
|
|
3757
|
-
const
|
|
3758
|
-
const executionAllowed = execution !== void 0 && !settingsBlock && runtimeChoiceAllowed(providerLock, agentLocked, execution.provider, snapshot.current?.provider, isAgentRole(roleOf?.(execution.provider)));
|
|
4101
|
+
const executionAllowed = execution !== void 0 && runtimeChoiceAllowed(providerLock, agentLocked, execution.provider, snapshot.current?.provider, isAgentRole(roleOf?.(execution.provider)));
|
|
3759
4102
|
const action = planActionView({
|
|
3760
4103
|
busy,
|
|
3761
4104
|
blocked,
|
|
@@ -3767,11 +4110,7 @@ window.__ModuleLoader__.load({
|
|
|
3767
4110
|
select,
|
|
3768
4111
|
selection: model,
|
|
3769
4112
|
current: snapshot.current,
|
|
3770
|
-
answer: () => respondAnswer(matched, review.id, review.approve.label, t("plan.responseRejected"), true)
|
|
3771
|
-
onNotRestored: (switched) => {
|
|
3772
|
-
setNotice(t("plan.notRestored", { model: switched }));
|
|
3773
|
-
console.warn("dsh-model-switch: the Session model changed but the Main default was not restored", switched);
|
|
3774
|
-
}
|
|
4113
|
+
answer: () => respondAnswer(matched, review.id, review.approve.label, t("plan.responseRejected"), true)
|
|
3775
4114
|
})) {
|
|
3776
4115
|
const message = getDirectorySnapshot().error;
|
|
3777
4116
|
throw new Error(message === null ? t("plan.modelFailed") : t("error.action", { message }));
|
|
@@ -3843,12 +4182,11 @@ window.__ModuleLoader__.load({
|
|
|
3843
4182
|
errorLabel: (message) => t("plan.pickerCrash", { message }),
|
|
3844
4183
|
retryLabel: t("retry"),
|
|
3845
4184
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerPicker, {
|
|
3846
|
-
locked: busy || blocked
|
|
4185
|
+
locked: busy || blocked,
|
|
3847
4186
|
providerLock,
|
|
3848
4187
|
agentLocked,
|
|
3849
4188
|
...roleOf === void 0 ? {} : { roleOf },
|
|
3850
4189
|
available,
|
|
3851
|
-
...settingsUnavailableReason === void 0 ? {} : { unavailableReason: settingsUnavailableReason },
|
|
3852
4190
|
directory,
|
|
3853
4191
|
t,
|
|
3854
4192
|
...resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations },
|
|
@@ -3882,7 +4220,7 @@ window.__ModuleLoader__.load({
|
|
|
3882
4220
|
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3883
4221
|
className: PlanReviewCard_module_css_default.feedback,
|
|
3884
4222
|
role: "status",
|
|
3885
|
-
children: action.error ?? notice
|
|
4223
|
+
children: action.error ?? notice
|
|
3886
4224
|
}),
|
|
3887
4225
|
decline !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
3888
4226
|
className: PlanReviewCard_module_css_default.feedbackRow,
|
|
@@ -4004,9 +4342,6 @@ window.__ModuleLoader__.load({
|
|
|
4004
4342
|
"thinking.off": "关",
|
|
4005
4343
|
"effort.providerDefault": "Default",
|
|
4006
4344
|
"status.loading": "正在刷新模型列表…",
|
|
4007
|
-
"settings.remoteUnavailable": "官方 DSH 在非本机浏览器禁用配置读取与写入;请在 Host 的 127.0.0.1 页面选择模型。",
|
|
4008
|
-
"settings.loading": "正在加载主模型设置;加载完成后才能切换模型。",
|
|
4009
|
-
"settings.unavailable": "主模型设置不可写;请在 Host 的本机页面检查设置状态。",
|
|
4010
4345
|
"error.action": "模型操作失败:{message}",
|
|
4011
4346
|
"error.picker": "模型选择器出错:{message}(点击重试)",
|
|
4012
4347
|
"action.reload": "重新加载",
|
|
@@ -4027,7 +4362,6 @@ window.__ModuleLoader__.load({
|
|
|
4027
4362
|
"plan.pickerCrash": "执行模型选择器出错:{message}",
|
|
4028
4363
|
"plan.feedback": "拒绝时的反馈",
|
|
4029
4364
|
"plan.feedbackPlaceholder": "说明要改什么(可留空)",
|
|
4030
|
-
"plan.notRestored": "执行模型已切换,但主模型默认值未能还原({model});请在「设置 → 模型」中确认默认模型。",
|
|
4031
4365
|
"plan.dismissUnsupported": "当前 DSH 版本既未提供 PendingQuestion.dismiss() 也未提供 cancel(),无法回到聊天;请直接在输入框里发送你的意见。",
|
|
4032
4366
|
"plan.goalRun": "设置为目标并执行",
|
|
4033
4367
|
"plan.goalFailed": "设置目标失败:{message}",
|
|
@@ -4060,9 +4394,6 @@ window.__ModuleLoader__.load({
|
|
|
4060
4394
|
"thinking.off": "Off",
|
|
4061
4395
|
"effort.providerDefault": "Default",
|
|
4062
4396
|
"status.loading": "Refreshing model list…",
|
|
4063
|
-
"settings.remoteUnavailable": "Official DSH disables settings reads and writes in non-loopback browsers. Select a model on the Host loopback URL.",
|
|
4064
|
-
"settings.loading": "Main model settings are loading. Wait until they are ready before switching models.",
|
|
4065
|
-
"settings.unavailable": "Main model settings are not writable. Check their status on the Host loopback URL.",
|
|
4066
4397
|
"error.action": "Model operation failed: {message}",
|
|
4067
4398
|
"error.picker": "Model picker error: {message} (click to retry)",
|
|
4068
4399
|
"action.reload": "Reload",
|
|
@@ -4083,7 +4414,6 @@ window.__ModuleLoader__.load({
|
|
|
4083
4414
|
"plan.pickerCrash": "Execution model picker error: {message}",
|
|
4084
4415
|
"plan.feedback": "Feedback when declining",
|
|
4085
4416
|
"plan.feedbackPlaceholder": "Say what to change (optional)",
|
|
4086
|
-
"plan.notRestored": "The execution model switched, but the Main default could not be restored ({model}); confirm the default in Settings → Models.",
|
|
4087
4417
|
"plan.dismissUnsupported": "This DSH Host exposes neither PendingQuestion.dismiss() nor cancel(), so the review cannot return to chat; send your feedback as a message instead.",
|
|
4088
4418
|
"plan.goalRun": "Set as goal and run",
|
|
4089
4419
|
"plan.goalFailed": "Could not set the goal: {message}",
|
|
@@ -4183,7 +4513,6 @@ window.__ModuleLoader__.load({
|
|
|
4183
4513
|
function ModelSeat(props) {
|
|
4184
4514
|
const directory = props.useDirectory((snapshot) => snapshot);
|
|
4185
4515
|
const order = props.useProviderOrder((value) => value);
|
|
4186
|
-
const settingsUnavailableReason = mainDefaultsUnavailableReason((0, react.useSyncExternalStore)(props.subscribeMainDefaults, props.getMainDefaultsSnapshot), props.t);
|
|
4187
4516
|
const lock = (0, react.useSyncExternalStore)(props.providerLockStore.subscribe, props.providerLockStore.getSnapshot);
|
|
4188
4517
|
const phase = props.useInput((input) => input.phase);
|
|
4189
4518
|
const blank = props.useSession((session) => session.blank);
|
|
@@ -4199,12 +4528,11 @@ window.__ModuleLoader__.load({
|
|
|
4199
4528
|
]);
|
|
4200
4529
|
const providerLock = effectiveProviderLock(lock, directory.current?.provider, active, isAgentRole(props.roleOf?.(directory.current?.provider ?? "")));
|
|
4201
4530
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [lock.failed && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ProviderLockHint, { t: props.t }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ComposerPicker, {
|
|
4202
|
-
locked: props.locked ||
|
|
4531
|
+
locked: props.locked || lock.failed && directory.current === null,
|
|
4203
4532
|
providerLock,
|
|
4204
4533
|
agentLocked: agentProviderLocked(blank, providerLock, active),
|
|
4205
4534
|
...props.roleOf === void 0 ? {} : { roleOf: props.roleOf },
|
|
4206
4535
|
available: props.available,
|
|
4207
|
-
...settingsUnavailableReason === void 0 ? {} : { unavailableReason: settingsUnavailableReason },
|
|
4208
4536
|
directory: pickerDirectoryViewOrdered(directory, props, order, props.catalogRoutes?.() ?? {}),
|
|
4209
4537
|
t: props.t,
|
|
4210
4538
|
...props.resolveInteractionOperations === void 0 ? {} : { resolveInteractionOperations: props.resolveInteractionOperations }
|
|
@@ -4229,7 +4557,23 @@ window.__ModuleLoader__.load({
|
|
|
4229
4557
|
], (scope) => {
|
|
4230
4558
|
const models = scope.modelDirectories;
|
|
4231
4559
|
const sessions = scope.sessions;
|
|
4232
|
-
const
|
|
4560
|
+
const ownedForm = scope.configForms.get(MODEL_SWITCH_CONFIG_ID);
|
|
4561
|
+
const groupWatches = /* @__PURE__ */ new Map();
|
|
4562
|
+
const watchGroupsFor = (sessionId, source) => {
|
|
4563
|
+
const key = String(sessionId);
|
|
4564
|
+
if (groupWatches.has(key)) return;
|
|
4565
|
+
const watch = createProviderGroupWatch({
|
|
4566
|
+
source,
|
|
4567
|
+
apply: async (provider) => {
|
|
4568
|
+
await applyProviderGroupTo(ownedForm, normalizeProviderGroups(ownedForm.getSnapshot().value?.providerGroups), provider);
|
|
4569
|
+
}
|
|
4570
|
+
});
|
|
4571
|
+
groupWatches.set(key, watch.dispose);
|
|
4572
|
+
};
|
|
4573
|
+
scope.effect(() => () => {
|
|
4574
|
+
for (const dispose of groupWatches.values()) dispose();
|
|
4575
|
+
groupWatches.clear();
|
|
4576
|
+
});
|
|
4233
4577
|
let directoryService;
|
|
4234
4578
|
const orderStore = providerOrderStore(scope.configForms.get(PROVIDERS_CONFIG_ID));
|
|
4235
4579
|
scope.inject(["providerDirectory"], (directoryScope) => {
|
|
@@ -4260,6 +4604,7 @@ window.__ModuleLoader__.load({
|
|
|
4260
4604
|
};
|
|
4261
4605
|
const roleOf = (key) => readProviderRole(directoryService, key);
|
|
4262
4606
|
const providerLockStore = createProviderLockStore(readLock);
|
|
4607
|
+
if (available) watchGroupsFor(sessionId, directory.store);
|
|
4263
4608
|
return {
|
|
4264
4609
|
available,
|
|
4265
4610
|
roleOf,
|
|
@@ -4268,8 +4613,6 @@ window.__ModuleLoader__.load({
|
|
|
4268
4613
|
refreshProviderLock: () => {
|
|
4269
4614
|
providerLockStore.refresh();
|
|
4270
4615
|
},
|
|
4271
|
-
subscribeMainDefaults: (listener) => mainDefaults.subscribe(listener),
|
|
4272
|
-
getMainDefaultsSnapshot: () => mainDefaults.getSnapshot(),
|
|
4273
4616
|
goalRemote: () => goalRemoteFrom(scope),
|
|
4274
4617
|
planPreview,
|
|
4275
4618
|
hooks: {
|
|
@@ -4282,31 +4625,22 @@ window.__ModuleLoader__.load({
|
|
|
4282
4625
|
if (available) directory.load().catch(() => {});
|
|
4283
4626
|
},
|
|
4284
4627
|
select: async (selection) => {
|
|
4285
|
-
|
|
4286
|
-
committed: false,
|
|
4287
|
-
mainDefaultRestored: true
|
|
4288
|
-
};
|
|
4289
|
-
if (!available) return uncommitted;
|
|
4628
|
+
if (!available) return { committed: false };
|
|
4290
4629
|
const state = await providerLockStore.refresh();
|
|
4291
4630
|
const currentProvider = directory.store.getSnapshot().current?.provider;
|
|
4292
4631
|
if (!isProviderAllowed(state, selection.provider, currentProvider, {
|
|
4293
4632
|
...readSessionState(sessions, sessionId),
|
|
4294
4633
|
agent: isAgentRole(roleOf(selection.provider)),
|
|
4295
4634
|
currentAgent: currentProvider !== void 0 && isAgentRole(roleOf(currentProvider))
|
|
4296
|
-
})) return
|
|
4297
|
-
const defaultBeforeSwitch = mainDefaults.getSnapshot();
|
|
4298
|
-
if (defaultBeforeSwitch.status !== "ready" || defaultBeforeSwitch.mode !== "host" || !defaultBeforeSwitch.writable || defaultBeforeSwitch.value === void 0 || defaultBeforeSwitch.revision === void 0) return uncommitted;
|
|
4635
|
+
})) return { committed: false };
|
|
4299
4636
|
let result;
|
|
4300
4637
|
try {
|
|
4301
4638
|
result = await directory.select(selection);
|
|
4302
4639
|
} catch {
|
|
4303
|
-
return
|
|
4640
|
+
return { committed: false };
|
|
4304
4641
|
}
|
|
4305
|
-
if (!result.ok) return
|
|
4306
|
-
return {
|
|
4307
|
-
committed: true,
|
|
4308
|
-
mainDefaultRestored: (await restoreMainDefault(mainDefaults, defaultBeforeSwitch)).restored
|
|
4309
|
-
};
|
|
4642
|
+
if (!result.ok) return { committed: false };
|
|
4643
|
+
return { committed: true };
|
|
4310
4644
|
}
|
|
4311
4645
|
};
|
|
4312
4646
|
};
|
|
@@ -4403,46 +4737,12 @@ window.__ModuleLoader__.load({
|
|
|
4403
4737
|
}), "dsh-model-switch: localized Settings section");
|
|
4404
4738
|
const t = ctx.locale.bind(localeNamespace);
|
|
4405
4739
|
const remote = ctx.remote;
|
|
4406
|
-
const mainForm = ctx.configForms.get(MAIN_DEFAULT_CONFIG_ID);
|
|
4407
4740
|
const owned = ctx.configForms.get(MODEL_SWITCH_CONFIG_ID);
|
|
4408
|
-
const main = deriveConfigForm(mainForm, (value) => value, {
|
|
4409
|
-
provider: "provider",
|
|
4410
|
-
model: "model",
|
|
4411
|
-
reasoningEffort: "reasoningEffort"
|
|
4412
|
-
});
|
|
4413
4741
|
const subagent = deriveConfigForm(owned, deriveSubagentSettings, SUBAGENT_SETTINGS_FIELDS);
|
|
4414
4742
|
const search = deriveConfigForm(owned, deriveSearchSettings, SEARCH_SETTINGS_FIELDS);
|
|
4415
4743
|
const image = deriveConfigForm(owned, deriveImageSettings, IMAGE_SETTINGS_FIELDS);
|
|
4416
4744
|
const autoReview = deriveConfigForm(owned, deriveAutoReviewSettings, AUTO_REVIEW_SETTINGS_FIELDS);
|
|
4417
4745
|
const compaction = deriveConfigForm(owned, deriveCompactionSettings, COMPACTION_SETTINGS_FIELDS);
|
|
4418
|
-
const saveMain = async (next, expectedRevision) => {
|
|
4419
|
-
if (!await mainForm.mutate([
|
|
4420
|
-
{
|
|
4421
|
-
op: "set",
|
|
4422
|
-
path: ["provider"],
|
|
4423
|
-
value: next.provider
|
|
4424
|
-
},
|
|
4425
|
-
{
|
|
4426
|
-
op: "set",
|
|
4427
|
-
path: ["model"],
|
|
4428
|
-
value: next.model
|
|
4429
|
-
},
|
|
4430
|
-
next.reasoningEffort === void 0 || next.reasoningEffort === "" ? {
|
|
4431
|
-
op: "unset",
|
|
4432
|
-
path: ["reasoningEffort"]
|
|
4433
|
-
} : {
|
|
4434
|
-
op: "set",
|
|
4435
|
-
path: ["reasoningEffort"],
|
|
4436
|
-
value: next.reasoningEffort
|
|
4437
|
-
}
|
|
4438
|
-
], expectedRevision)) {
|
|
4439
|
-
if (mainForm.getSnapshot().revision !== expectedRevision) throw new MainSettingsConflictError(t("conflict"));
|
|
4440
|
-
throw new Error("settings-rejected");
|
|
4441
|
-
}
|
|
4442
|
-
const revision = mainForm.getSnapshot().revision;
|
|
4443
|
-
if (revision === void 0) throw new Error("settings unavailable after save");
|
|
4444
|
-
return revision;
|
|
4445
|
-
};
|
|
4446
4746
|
const connectionRpc = (() => {
|
|
4447
4747
|
try {
|
|
4448
4748
|
const rpc = ctx.get("connection", false)?.rpc;
|
|
@@ -4475,7 +4775,6 @@ window.__ModuleLoader__.load({
|
|
|
4475
4775
|
inject: () => ({
|
|
4476
4776
|
t,
|
|
4477
4777
|
hooks: {
|
|
4478
|
-
mainSettings: main,
|
|
4479
4778
|
subagentSettings: subagent,
|
|
4480
4779
|
searchSettings: search,
|
|
4481
4780
|
imageSettings: image,
|
|
@@ -4484,7 +4783,6 @@ window.__ModuleLoader__.load({
|
|
|
4484
4783
|
switchSettings: owned
|
|
4485
4784
|
},
|
|
4486
4785
|
capabilities: RUNTIME_CAPABILITIES,
|
|
4487
|
-
saveMain,
|
|
4488
4786
|
setSubagent: (field, value) => value === void 0 ? subagent.unset(field) : subagent.set(field, value),
|
|
4489
4787
|
setCapability: (route, field, value) => {
|
|
4490
4788
|
const scope = route === "search" ? search : route === "image" ? image : route === "compaction" ? compaction : autoReview;
|
|
@@ -4493,6 +4791,16 @@ window.__ModuleLoader__.load({
|
|
|
4493
4791
|
setCompactOnSwitch: async (value) => {
|
|
4494
4792
|
if (!await owned.set("compactOnSwitch", value)) throw new Error("settings-rejected");
|
|
4495
4793
|
},
|
|
4794
|
+
saveProviderGroup: async (provider, group) => {
|
|
4795
|
+
const current = owned.getSnapshot().value?.providerGroups;
|
|
4796
|
+
const next = withProviderGroup(current, provider, group);
|
|
4797
|
+
if (next.length === 0) {
|
|
4798
|
+
if (current === void 0) return;
|
|
4799
|
+
if (!await owned.unset("providerGroups")) throw new Error("settings-rejected");
|
|
4800
|
+
return;
|
|
4801
|
+
}
|
|
4802
|
+
if (!await owned.set("providerGroups", next)) throw new Error("settings-rejected");
|
|
4803
|
+
},
|
|
4496
4804
|
providerRoleOf,
|
|
4497
4805
|
loadCatalog: async () => {
|
|
4498
4806
|
const response = await remote.session.modelCatalog();
|