@deepseek-ai/dsh-client-ui-model-selection 0.1.6-alpha.1 → 0.1.6-alpha.2
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/README.i18n.yaml +2 -2
- package/README.md +5 -1
- package/README.zh.md +5 -1
- package/lib/client.js +152 -33
- package/lib/types/client/directory.d.ts +5 -4
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/slots.d.ts +3 -2
- package/package.json +16 -15
package/README.i18n.yaml
CHANGED
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
# side as of the last confirmed-consistent state. Both languages carry equal authority;
|
|
3
3
|
# after editing either side, bring the other along and re-record with:
|
|
4
4
|
# pnpm run verify-translation-pairing --write packages/client/ui-model-selection/README.md
|
|
5
|
-
README.md:
|
|
6
|
-
README.zh.md:
|
|
5
|
+
README.md: 5e16cdf18d2eccb63097c2a524bfb2d20178d15e
|
|
6
|
+
README.zh.md: a99581e827c5ce66a31f6e4cbad11394e03dde4d
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ The Web GUI lets users switch the model and reasoning effort for an existing ses
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## Use this package
|
|
27
27
|
|
|
28
|
-
Mount this plugin alongside `ui-conversation` and the commands package; the composer then shows the model seat next to the pending indicator, and `/model` opens the same directory as a popup. Both surfaces show the host-reported current selection when the exact provider/model pair remains in the advertised groups; a missing catalog row leaves the routable selection intact while the trigger prompts `Select model`.
|
|
28
|
+
Mount this plugin alongside `ui-conversation` and the commands package; the composer then shows the model seat next to the pending indicator, and `/model` opens the same directory as a popup. While the seat's menu is open, `↑`/`↓` move focus across the rows of the shown pane, Tab settles the focused row, and Escape and `Shift+Tab` leave a drilled pane first and otherwise close back to the trigger. Drilling lands on the row of the value in use, and going back lands on the cell that opened the pane left. Both surfaces show the host-reported current selection when the exact provider/model pair remains in the advertised groups; a missing catalog row leaves the routable selection intact while the trigger prompts `Select model`.
|
|
29
29
|
|
|
30
30
|
### Model and effort
|
|
31
31
|
|
|
@@ -35,6 +35,10 @@ Models stay grouped by provider. The composer menu shows model and effort names
|
|
|
35
35
|
|
|
36
36
|
When the Host reports that no adapter serves the session's route, this plugin raises a composer block and the input goes inert with its own copy; recovering clears it without a reload. A `null` before the first load or after one failed never blocks, and catalog membership never blocks either — a route serving a model it does not advertise is missing from the groups yet usable.
|
|
37
37
|
|
|
38
|
+
### Selection failures
|
|
39
|
+
|
|
40
|
+
When another writer owns the Session, model-selection failures tell the user to quit other running DSH instances and retry.
|
|
41
|
+
|
|
38
42
|
-----
|
|
39
43
|
|
|
40
44
|
<a id="understand-the-implementation"></a>
|
package/README.zh.md
CHANGED
|
@@ -25,7 +25,7 @@ Web GUI 允许用户通过 `/model` 弹窗或 composer 模型控件切换既有
|
|
|
25
25
|
<a id="use-this-package"></a>
|
|
26
26
|
## 使用本包
|
|
27
27
|
|
|
28
|
-
与 `ui-conversation` 及命令包一起挂载本插件;composer 随即在待处理指示器旁显示模型位,`/model`
|
|
28
|
+
与 `ui-conversation` 及命令包一起挂载本插件;composer 随即在待处理指示器旁显示模型位,`/model` 则以弹窗打开同一份目录。模型位菜单打开期间,`↑`/`↓` 在所显示面板的行间移动焦点,`Tab` 选定聚焦行,Escape 与 `Shift+Tab` 先退出已下钻的面板,否则关闭并回到触发器。下钻落在正在使用的那一行,返回则落在打开该面板的格子上。当确切提供方/模型对仍在已公布分组中时,两个界面都显示 Host 报告的当前选择;目录行缺席时,可路由的选择保持不变,触发器提示 `Select model`。
|
|
29
29
|
|
|
30
30
|
### 模型与推理强度
|
|
31
31
|
|
|
@@ -35,6 +35,10 @@ Web GUI 允许用户通过 `/model` 弹窗或 composer 模型控件切换既有
|
|
|
35
35
|
|
|
36
36
|
当 Host 报告没有适配器服务该会话的路由时,本插件注册一个 composer 阻塞块,输入框随之停用并显示本插件自己的文案;恢复后无需重新加载即清除。首次加载之前或加载失败之后的 `null` 绝不阻断;目录成员关系同样不阻断——一条仍在服务、只是不公布该模型的路由不在分组里,却可用。
|
|
37
37
|
|
|
38
|
+
### 选择失败
|
|
39
|
+
|
|
40
|
+
当会话被其他写句柄占用时,模型选择失败提示用户退出其他正在运行的 DSH 后重试。
|
|
41
|
+
|
|
38
42
|
-----
|
|
39
43
|
|
|
40
44
|
<a id="understand-the-implementation"></a>
|
package/lib/client.js
CHANGED
|
@@ -10,6 +10,69 @@ window.__ModuleLoader__.load({
|
|
|
10
10
|
let react_jsx_runtime = require("react/jsx-runtime");
|
|
11
11
|
let react = require("react");
|
|
12
12
|
let react_dom = require("react-dom");
|
|
13
|
+
//#region ../../util/values/src/index.ts
|
|
14
|
+
/**
|
|
15
|
+
* Weak-key lookup with a strongly retained iterable set of associated values.
|
|
16
|
+
*
|
|
17
|
+
* Each value must belong to only one key. The container performs no automatic
|
|
18
|
+
* cleanup; owners delete associations or clear the container at lifecycle end.
|
|
19
|
+
*/
|
|
20
|
+
var WeakMapWithValues = class {
|
|
21
|
+
keys = /* @__PURE__ */ new WeakMap();
|
|
22
|
+
valueSet = /* @__PURE__ */ new Set();
|
|
23
|
+
/** Live strongly retained values in insertion order. */
|
|
24
|
+
values = this.valueSet;
|
|
25
|
+
/**
|
|
26
|
+
* Read the value associated with a key.
|
|
27
|
+
* @param key - weakly held lookup key.
|
|
28
|
+
* @returns the associated value, or absence.
|
|
29
|
+
*/
|
|
30
|
+
get(key) {
|
|
31
|
+
return this.keys.get(key);
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Test whether a key has an association.
|
|
35
|
+
* @param key - weakly held lookup key.
|
|
36
|
+
* @returns whether the key is present.
|
|
37
|
+
*/
|
|
38
|
+
has(key) {
|
|
39
|
+
return this.keys.has(key);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Associate one key with one caller-unique value.
|
|
43
|
+
* @param key - weakly held lookup key.
|
|
44
|
+
* @param value - strongly retained value that belongs to no other key.
|
|
45
|
+
* @returns this container.
|
|
46
|
+
*/
|
|
47
|
+
set(key, value) {
|
|
48
|
+
if (this.keys.has(key)) {
|
|
49
|
+
const previous = this.keys.get(key);
|
|
50
|
+
if (previous === value) return this;
|
|
51
|
+
this.valueSet.delete(previous);
|
|
52
|
+
}
|
|
53
|
+
this.keys.set(key, value);
|
|
54
|
+
this.valueSet.add(value);
|
|
55
|
+
return this;
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Remove one association and its strongly retained value.
|
|
59
|
+
* @param key - weakly held lookup key.
|
|
60
|
+
* @returns whether an association was removed.
|
|
61
|
+
*/
|
|
62
|
+
delete(key) {
|
|
63
|
+
if (!this.keys.has(key)) return false;
|
|
64
|
+
const value = this.keys.get(key);
|
|
65
|
+
const deleted = this.keys.delete(key);
|
|
66
|
+
this.valueSet.delete(value);
|
|
67
|
+
return deleted;
|
|
68
|
+
}
|
|
69
|
+
/** Remove every association and strongly retained value. */
|
|
70
|
+
clear() {
|
|
71
|
+
this.keys = /* @__PURE__ */ new WeakMap();
|
|
72
|
+
this.valueSet.clear();
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
//#endregion
|
|
13
76
|
//#region lib/types/client/catalog.js
|
|
14
77
|
/** One Host-generation model catalog shared by every Session selector. */
|
|
15
78
|
/** Loads at most one model catalog for the current Host generation. */
|
|
@@ -146,8 +209,9 @@ window.__ModuleLoader__.load({
|
|
|
146
209
|
/**
|
|
147
210
|
* Select the complete provider/model/reasoning selection. The durable
|
|
148
211
|
* projection frame updates the shared current; failures surface on the store
|
|
149
|
-
* and
|
|
212
|
+
* and return with the operation so each entry can present its own failure.
|
|
150
213
|
* @param selection - provider, provider-owned model id, and optional adapter-owned effort.
|
|
214
|
+
* @returns the selection outcome, including the original Remote failure.
|
|
151
215
|
*/
|
|
152
216
|
async select(selection) {
|
|
153
217
|
this.assertAvailable();
|
|
@@ -162,22 +226,26 @@ window.__ModuleLoader__.load({
|
|
|
162
226
|
model: selection.model,
|
|
163
227
|
...selection.reasoningEffort === void 0 ? {} : { reasoningEffort: selection.reasoningEffort }
|
|
164
228
|
});
|
|
165
|
-
if (this.disposed || generation !== this.generation) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
229
|
+
if (this.disposed || generation !== this.generation) return result.ok ? {
|
|
230
|
+
ok: true,
|
|
231
|
+
value: void 0
|
|
232
|
+
} : result;
|
|
169
233
|
if (!result.ok) {
|
|
170
234
|
this.store.update((s) => {
|
|
171
235
|
s.status = "error";
|
|
172
236
|
s.error = `${result.error.code}: ${result.error.message}`;
|
|
173
237
|
});
|
|
174
|
-
|
|
238
|
+
return result;
|
|
175
239
|
}
|
|
176
240
|
this.store.update((s) => {
|
|
177
241
|
s.status = "ready";
|
|
178
242
|
s.error = null;
|
|
179
243
|
});
|
|
180
244
|
this.syncInputs();
|
|
245
|
+
return {
|
|
246
|
+
ok: true,
|
|
247
|
+
value: void 0
|
|
248
|
+
};
|
|
181
249
|
}
|
|
182
250
|
/**
|
|
183
251
|
* Invalidate an in-flight selection response from the previous Host generation.
|
|
@@ -260,7 +328,7 @@ window.__ModuleLoader__.load({
|
|
|
260
328
|
"remote",
|
|
261
329
|
"remote.session"
|
|
262
330
|
];
|
|
263
|
-
live = { directories:
|
|
331
|
+
live = { directories: new WeakMapWithValues() };
|
|
264
332
|
catalog;
|
|
265
333
|
/** Localized composer-block copy; this plugin owns the string it raises. */
|
|
266
334
|
blockReason;
|
|
@@ -275,7 +343,7 @@ window.__ModuleLoader__.load({
|
|
|
275
343
|
this.catalog.load().catch(() => {});
|
|
276
344
|
ctx.on("connection/reset", () => {
|
|
277
345
|
this.catalog.resetGeneration();
|
|
278
|
-
for (const directory of this.live.directories.values
|
|
346
|
+
for (const directory of this.live.directories.values) directory.resetConnected();
|
|
279
347
|
});
|
|
280
348
|
ctx.remote.$on("llm/adapters-updated", () => {
|
|
281
349
|
this.catalog.refresh();
|
|
@@ -295,15 +363,15 @@ window.__ModuleLoader__.load({
|
|
|
295
363
|
*/
|
|
296
364
|
directoryFor(sessionId) {
|
|
297
365
|
const { live } = this;
|
|
298
|
-
const existing = live.directories.get(sessionId);
|
|
299
|
-
if (existing !== void 0) return existing;
|
|
300
366
|
const sessions = this.ctx.sessions;
|
|
301
367
|
const actx = sessions.scope(sessionId);
|
|
302
368
|
if (actx === void 0) throw new Error(`ui-model-selection: session "${String(sessionId)}" resolved no scope`);
|
|
303
369
|
const binding = sessions.binding(sessionId);
|
|
304
370
|
if (binding === void 0) throw new Error(`ui-model-selection: session "${String(sessionId)}" resolved no binding`);
|
|
371
|
+
const existing = live.directories.get(binding);
|
|
372
|
+
if (existing !== void 0) return existing;
|
|
305
373
|
const directory = new ModelDirectory(this.ctx.remote.session, sessionId, () => sessions.subagentAddress(sessionId) === void 0, this.catalog, binding.session.projections.faceOf("modelSelection"));
|
|
306
|
-
live.directories.set(
|
|
374
|
+
live.directories.set(binding, directory);
|
|
307
375
|
const conversation = this.ctx.get("conversation");
|
|
308
376
|
if (conversation !== void 0) {
|
|
309
377
|
const publish = () => {
|
|
@@ -320,7 +388,7 @@ window.__ModuleLoader__.load({
|
|
|
320
388
|
}
|
|
321
389
|
actx.effect(() => () => {
|
|
322
390
|
directory.dispose();
|
|
323
|
-
live.directories.delete(
|
|
391
|
+
live.directories.delete(binding);
|
|
324
392
|
}, "ui-model-selection: session directory");
|
|
325
393
|
return directory;
|
|
326
394
|
}
|
|
@@ -387,11 +455,17 @@ window.__ModuleLoader__.load({
|
|
|
387
455
|
* each drilling into its own list — the provider-grouped model list over
|
|
388
456
|
* the shared directory, and the effort levels. The trigger (313:14108's
|
|
389
457
|
* ToggleButton) shows both: model name + effort in the caption tone.
|
|
390
|
-
*
|
|
391
|
-
*
|
|
392
|
-
*
|
|
393
|
-
*
|
|
394
|
-
*
|
|
458
|
+
* While open, ↑/↓ move focus across the rows of the shown pane (wrapping; a
|
|
459
|
+
* step taken while the trigger still holds focus enters at the near end), Tab
|
|
460
|
+
* settles like Enter, and Escape and Shift+Tab leave a drilled pane first and
|
|
461
|
+
* otherwise close back to the trigger. A drilled pane hands focus to the row
|
|
462
|
+
* of the value in use, and returning to the root pane hands it back to the
|
|
463
|
+
* cell that opened it. Data and submission ride the SAME per-session
|
|
464
|
+
* ModelDirectory as the /model popup; exact-model reasoning metadata and the
|
|
465
|
+
* selected effort come from the Host rather than a client-owned vocabulary. A
|
|
466
|
+
* rejected selection announces through the shared transient Toast anchored to
|
|
467
|
+
* the composer card; the in-menu strip with Retry remains the catalog-load
|
|
468
|
+
* surface.
|
|
395
469
|
*/
|
|
396
470
|
/** Unplaced portal card: hidden but laid out at a fixed origin so offsetWidth/offsetHeight are real (Menu primitive's measure pass). */
|
|
397
471
|
const MEASURE_STYLE = {
|
|
@@ -457,6 +531,18 @@ window.__ModuleLoader__.load({
|
|
|
457
531
|
document.removeEventListener("mousedown", closeOutside);
|
|
458
532
|
};
|
|
459
533
|
}, [open]);
|
|
534
|
+
const paneFocus = (0, react.useRef)(null);
|
|
535
|
+
(0, react.useEffect)(() => {
|
|
536
|
+
const intent = paneFocus.current;
|
|
537
|
+
paneFocus.current = null;
|
|
538
|
+
if (!open || intent === null) return;
|
|
539
|
+
if (intent === "drill") {
|
|
540
|
+
(menuRef.current?.querySelector("[role=\"menuitemradio\"][aria-checked=\"true\"]:not([disabled])") ?? itemRefs.current.find((item) => item !== null && !item.disabled) ?? triggerRef.current)?.focus();
|
|
541
|
+
return;
|
|
542
|
+
}
|
|
543
|
+
const cell = itemRefs.current[intent === "effort" ? 1 : 0];
|
|
544
|
+
(cell !== null && cell !== void 0 && !cell.disabled ? cell : triggerRef.current)?.focus();
|
|
545
|
+
}, [open, pane]);
|
|
460
546
|
(0, react.useLayoutEffect)(() => {
|
|
461
547
|
if (!open) {
|
|
462
548
|
setMenuPos(null);
|
|
@@ -503,20 +589,48 @@ window.__ModuleLoader__.load({
|
|
|
503
589
|
triggerRef.current?.focus();
|
|
504
590
|
});
|
|
505
591
|
};
|
|
592
|
+
const drill = (next) => {
|
|
593
|
+
paneFocus.current = "drill";
|
|
594
|
+
setPane(next);
|
|
595
|
+
};
|
|
596
|
+
/** Leave a drilled pane for the root one, handing the keyboard back to its cell. */
|
|
597
|
+
const back = (from) => {
|
|
598
|
+
paneFocus.current = from;
|
|
599
|
+
setPane("root");
|
|
600
|
+
};
|
|
506
601
|
const moveFocus = (offset) => {
|
|
507
602
|
const items = itemRefs.current.filter((item) => item !== null);
|
|
508
603
|
if (items.length === 0) return;
|
|
509
604
|
const active = items.findIndex((item) => item === document.activeElement);
|
|
510
|
-
items[
|
|
605
|
+
items[active === -1 ? offset > 0 ? 0 : items.length - 1 : (active + offset + items.length) % items.length]?.focus();
|
|
511
606
|
};
|
|
512
607
|
const onRootKeyDown = (event) => {
|
|
513
608
|
if (event.key === "Escape" && open) {
|
|
514
609
|
event.preventDefault();
|
|
515
|
-
if (pane !== "root")
|
|
610
|
+
if (pane !== "root") back(pane);
|
|
516
611
|
else close(true);
|
|
517
612
|
return;
|
|
518
613
|
}
|
|
519
614
|
if (!open) return;
|
|
615
|
+
if (event.key === "Tab") {
|
|
616
|
+
if (event.shiftKey) {
|
|
617
|
+
event.preventDefault();
|
|
618
|
+
if (pane !== "root") back(pane);
|
|
619
|
+
else close(true);
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
const focused = document.activeElement;
|
|
623
|
+
const rows = itemRefs.current.filter((item) => item !== null);
|
|
624
|
+
if (focused instanceof HTMLButtonElement && rows.includes(focused)) {
|
|
625
|
+
event.preventDefault();
|
|
626
|
+
focused.click();
|
|
627
|
+
return;
|
|
628
|
+
}
|
|
629
|
+
if (focused !== triggerRef.current) return;
|
|
630
|
+
event.preventDefault();
|
|
631
|
+
(menuRef.current?.querySelector("[role=\"menuitemradio\"][aria-checked=\"true\"]:not([disabled])") ?? rows.find((item) => !item.disabled))?.focus();
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
520
634
|
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
521
635
|
event.preventDefault();
|
|
522
636
|
moveFocus(event.key === "ArrowDown" ? 1 : -1);
|
|
@@ -526,19 +640,18 @@ window.__ModuleLoader__.load({
|
|
|
526
640
|
if (event.relatedTarget instanceof Node && (rootRef.current?.contains(event.relatedTarget) === true || menuRef.current?.contains(event.relatedTarget) === true)) return;
|
|
527
641
|
close();
|
|
528
642
|
};
|
|
529
|
-
const settleSelection = (
|
|
530
|
-
if (
|
|
643
|
+
const settleSelection = (result) => {
|
|
644
|
+
if (result === void 0) return;
|
|
645
|
+
if (result.ok) {
|
|
531
646
|
if (rootRef.current !== null) close(true);
|
|
532
647
|
return;
|
|
533
648
|
}
|
|
534
|
-
const
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
});
|
|
541
|
-
}
|
|
649
|
+
const { error } = result;
|
|
650
|
+
toastSeq.current += 1;
|
|
651
|
+
setToast({
|
|
652
|
+
seq: toastSeq.current,
|
|
653
|
+
text: error.code === "session/writer-held" ? t("error.sessionInUse") : t("error.action", { message: `${error.code}: ${error.message}` })
|
|
654
|
+
});
|
|
542
655
|
};
|
|
543
656
|
const choose = (selection) => {
|
|
544
657
|
if (state.current?.provider === selection.provider && state.current.model === selection.model) {
|
|
@@ -628,7 +741,7 @@ window.__ModuleLoader__.load({
|
|
|
628
741
|
role: "menuitem",
|
|
629
742
|
className: ModelSelect_module_css_default.cell,
|
|
630
743
|
onClick: () => {
|
|
631
|
-
|
|
744
|
+
drill("model");
|
|
632
745
|
},
|
|
633
746
|
children: [
|
|
634
747
|
(0, react_jsx_runtime.jsx)("span", {
|
|
@@ -647,7 +760,7 @@ window.__ModuleLoader__.load({
|
|
|
647
760
|
role: "menuitem",
|
|
648
761
|
className: ModelSelect_module_css_default.cell,
|
|
649
762
|
onClick: () => {
|
|
650
|
-
|
|
763
|
+
drill("effort");
|
|
651
764
|
},
|
|
652
765
|
children: [
|
|
653
766
|
(0, react_jsx_runtime.jsx)("span", {
|
|
@@ -809,6 +922,7 @@ window.__ModuleLoader__.load({
|
|
|
809
922
|
"effort.providerDefault": "Default",
|
|
810
923
|
"status.loading": "正在刷新模型列表…",
|
|
811
924
|
"error.action": "模型操作失败:{message}",
|
|
925
|
+
"error.sessionInUse": "当前会话已被占用,可能是其他正在运行的 DSH 导致的(如其他 dsh web、桌面端),请退出其他正在运行的 DSH 后重试。",
|
|
812
926
|
"action.reload": "重新加载",
|
|
813
927
|
"warning.groupLoad": "{name} 加载失败:{message}",
|
|
814
928
|
"empty.models": "没有可用的模型。",
|
|
@@ -833,6 +947,7 @@ window.__ModuleLoader__.load({
|
|
|
833
947
|
"effort.providerDefault": "Default",
|
|
834
948
|
"status.loading": "Refreshing model list…",
|
|
835
949
|
"error.action": "Model operation failed: {message}",
|
|
950
|
+
"error.sessionInUse": "This session is already in use, possibly by another running DSH instance (such as dsh web or the desktop app). Quit other running DSH instances and try again.",
|
|
836
951
|
"action.reload": "Reload",
|
|
837
952
|
"warning.groupLoad": "{name} failed to load: {message}",
|
|
838
953
|
"empty.models": "No models available.",
|
|
@@ -935,7 +1050,11 @@ window.__ModuleLoader__.load({
|
|
|
935
1050
|
const directory = models.directoryFor(session.sessionId);
|
|
936
1051
|
const selection = selectionOf(directory.store.getSnapshot(), option.id);
|
|
937
1052
|
if (selection === void 0) throw new Error("this provider's catalog failed to load — pick a model from a loaded group");
|
|
938
|
-
await directory.select(selection);
|
|
1053
|
+
const result = await directory.select(selection);
|
|
1054
|
+
if (!result.ok) {
|
|
1055
|
+
if (result.error.code === "session/writer-held") throw new Error(t("error.sessionInUse"));
|
|
1056
|
+
throw result.error;
|
|
1057
|
+
}
|
|
939
1058
|
}
|
|
940
1059
|
}
|
|
941
1060
|
}), "ui-model-selection: /model contribution");
|
|
@@ -955,7 +1074,7 @@ window.__ModuleLoader__.load({
|
|
|
955
1074
|
load: () => {
|
|
956
1075
|
if (available) directory.load().catch(() => {});
|
|
957
1076
|
},
|
|
958
|
-
select: (selection) => available ? directory.select(selection)
|
|
1077
|
+
select: (selection) => available ? directory.select(selection) : Promise.resolve(void 0)
|
|
959
1078
|
};
|
|
960
1079
|
}
|
|
961
1080
|
}, ModelSelect));
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { ModelCatalogFailure, ModelProviderGroup, ModelSelection } from '@deepseek-ai/dsh-api-session-controller/types';
|
|
8
8
|
import type { SessionId } from '@deepseek-ai/dsh-api-remotes/client';
|
|
9
|
-
import type { TypertClientRemote } from '@deepseek-ai/dsh-typert-protocol';
|
|
9
|
+
import type { RemoteResult, TypertClientRemote } from '@deepseek-ai/dsh-typert-protocol';
|
|
10
10
|
import type { ObservableSnapshot, SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
11
11
|
import type { ModelCatalogDirectory } from './catalog.ts';
|
|
12
12
|
/** Directory snapshot both entries render from. */
|
|
@@ -61,10 +61,11 @@ export declare class ModelDirectory {
|
|
|
61
61
|
/**
|
|
62
62
|
* Select the complete provider/model/reasoning selection. The durable
|
|
63
63
|
* projection frame updates the shared current; failures surface on the store
|
|
64
|
-
* and
|
|
64
|
+
* and return with the operation so each entry can present its own failure.
|
|
65
65
|
* @param selection - provider, provider-owned model id, and optional adapter-owned effort.
|
|
66
|
-
|
|
67
|
-
|
|
66
|
+
* @returns the selection outcome, including the original Remote failure.
|
|
67
|
+
*/
|
|
68
|
+
select(selection: ModelSelection): Promise<RemoteResult<void>>;
|
|
68
69
|
/**
|
|
69
70
|
* Invalidate an in-flight selection response from the previous Host generation.
|
|
70
71
|
*/
|
|
@@ -25,6 +25,7 @@ export declare const zh: {
|
|
|
25
25
|
'effort.providerDefault': string;
|
|
26
26
|
'status.loading': string;
|
|
27
27
|
'error.action': string;
|
|
28
|
+
'error.sessionInUse': string;
|
|
28
29
|
'action.reload': string;
|
|
29
30
|
'warning.groupLoad': string;
|
|
30
31
|
'empty.models': string;
|
|
@@ -51,6 +52,7 @@ export declare const en: {
|
|
|
51
52
|
'effort.providerDefault': string;
|
|
52
53
|
'status.loading': string;
|
|
53
54
|
'error.action': string;
|
|
55
|
+
'error.sessionInUse': string;
|
|
54
56
|
'action.reload': string;
|
|
55
57
|
'warning.groupLoad': string;
|
|
56
58
|
'empty.models': string;
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* entry; this package only contributes the single occupant, so no SlotMap
|
|
5
5
|
* merge lives here.
|
|
6
6
|
*/
|
|
7
|
+
import type { RemoteResult } from '@deepseek-ai/dsh-typert-protocol';
|
|
7
8
|
import type { ModelSelection } from '@deepseek-ai/dsh-api-remotes/client';
|
|
8
9
|
import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
|
|
9
10
|
import type { ModelDirectoryState } from './directory.ts';
|
|
@@ -18,8 +19,8 @@ export interface ModelSelectInjected {
|
|
|
18
19
|
/**
|
|
19
20
|
* Select a complete provider/model/reasoning selection.
|
|
20
21
|
* @param selection - model selection and optional adapter-owned effort.
|
|
21
|
-
* @returns
|
|
22
|
+
* @returns the Host outcome, or undefined when this Session cannot select a model.
|
|
22
23
|
*/
|
|
23
|
-
select: (selection: ModelSelection) => Promise<
|
|
24
|
+
select: (selection: ModelSelection) => Promise<RemoteResult<void> | undefined>;
|
|
24
25
|
}
|
|
25
26
|
//# sourceMappingURL=slots.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deepseek-ai/dsh-client-ui-model-selection",
|
|
3
3
|
"description": "Model selection over the shared model catalog, Session projection, and session.selectModel",
|
|
4
|
-
"version": "0.1.6-alpha.
|
|
4
|
+
"version": "0.1.6-alpha.2",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -46,21 +46,22 @@
|
|
|
46
46
|
"react-dom": "^18.2.0",
|
|
47
47
|
"@types/react-dom": "~18.3.0",
|
|
48
48
|
"clsx": "^2.1.1",
|
|
49
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.
|
|
50
|
-
"@deepseek-ai/dsh-api-session-controller": "^0.1.6-alpha.
|
|
51
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.
|
|
52
|
-
"@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.
|
|
53
|
-
"@deepseek-ai/dsh-client-ui-commands": "^0.1.6-alpha.
|
|
54
|
-
"@deepseek-ai/dsh-client-ui-
|
|
55
|
-
"@deepseek-ai/dsh-client-ui-
|
|
56
|
-
"@deepseek-ai/dsh-client-ui-
|
|
57
|
-
"@deepseek-ai/dsh-
|
|
49
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.6-alpha.2",
|
|
50
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.6-alpha.2",
|
|
51
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.6-alpha.2",
|
|
52
|
+
"@deepseek-ai/dsh-client-test-runtime": "^0.1.6-alpha.2",
|
|
53
|
+
"@deepseek-ai/dsh-client-ui-commands": "^0.1.6-alpha.2",
|
|
54
|
+
"@deepseek-ai/dsh-client-ui-conversation": "^0.1.6-alpha.2",
|
|
55
|
+
"@deepseek-ai/dsh-client-ui-input-trigger": "^0.1.6-alpha.2",
|
|
56
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.6-alpha.2",
|
|
57
|
+
"@deepseek-ai/dsh-typert-protocol": "^0.1.6-alpha.2",
|
|
58
58
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
59
|
-
"@deepseek-ai/dsh-
|
|
60
|
-
"@deepseek-ai/dsh-client-store": "^0.1.6-alpha.
|
|
61
|
-
"@deepseek-ai/dsh-
|
|
62
|
-
"@deepseek-ai/dsh-
|
|
63
|
-
"@deepseek-ai/dsh-client-ui-session": "^0.1.6-alpha.
|
|
59
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.6-alpha.2",
|
|
60
|
+
"@deepseek-ai/dsh-client-store": "^0.1.6-alpha.2",
|
|
61
|
+
"@deepseek-ai/dsh-session": "^0.1.6-alpha.2",
|
|
62
|
+
"@deepseek-ai/dsh-util-values": "^0.1.6-alpha.2",
|
|
63
|
+
"@deepseek-ai/dsh-client-ui-session": "^0.1.6-alpha.2",
|
|
64
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.6-alpha.2"
|
|
64
65
|
},
|
|
65
66
|
"files": [
|
|
66
67
|
"lib/index.js",
|