@leaves615/dsh-llm-ctl 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +174 -0
- package/cordis.patch.yml +7 -0
- package/lib/client-plugin.d.ts +82 -0
- package/lib/client-plugin.js +685 -0
- package/lib/client.js +1712 -0
- package/lib/concurrency.d.ts +28 -0
- package/lib/concurrency.js +36 -0
- package/lib/config.d.ts +203 -0
- package/lib/config.js +67 -0
- package/lib/controller.d.ts +42 -0
- package/lib/controller.js +51 -0
- package/lib/delay.d.ts +68 -0
- package/lib/delay.js +134 -0
- package/lib/discover-ui.d.ts +94 -0
- package/lib/discover-ui.js +91 -0
- package/lib/discover.d.ts +79 -0
- package/lib/discover.js +141 -0
- package/lib/events.d.ts +45 -0
- package/lib/events.js +37 -0
- package/lib/index.d.ts +38 -0
- package/lib/index.js +378 -0
- package/lib/menu-filter.d.ts +134 -0
- package/lib/menu-filter.js +428 -0
- package/lib/menu-visibility.d.ts +26 -0
- package/lib/menu-visibility.js +77 -0
- package/lib/queue-dock.d.ts +85 -0
- package/lib/queue-dock.js +291 -0
- package/lib/queue.d.ts +128 -0
- package/lib/queue.js +313 -0
- package/lib/reactive.d.ts +57 -0
- package/lib/reactive.js +75 -0
- package/lib/reasoning-efforts.d.ts +120 -0
- package/lib/reasoning-efforts.js +143 -0
- package/lib/routes.d.ts +126 -0
- package/lib/routes.js +267 -0
- package/lib/settings-ui.d.ts +183 -0
- package/lib/settings-ui.js +367 -0
- package/lib/visibility-settings.d.ts +193 -0
- package/lib/visibility-settings.js +225 -0
- package/lib/visibility.d.ts +152 -0
- package/lib/visibility.js +235 -0
- package/package.json +92 -0
package/lib/client.js
ADDED
|
@@ -0,0 +1,1712 @@
|
|
|
1
|
+
window.__ModuleLoader__.load({
|
|
2
|
+
id: "@leaves615/dsh-llm-ctl",
|
|
3
|
+
factory: (require) => {
|
|
4
|
+
var module = { exports: {} };
|
|
5
|
+
var exports = module.exports;
|
|
6
|
+
"use strict";
|
|
7
|
+
var __create = Object.create;
|
|
8
|
+
var __defProp = Object.defineProperty;
|
|
9
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
10
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
11
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
12
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
13
|
+
var __export = (target, all) => {
|
|
14
|
+
for (var name in all)
|
|
15
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
16
|
+
};
|
|
17
|
+
var __copyProps = (to, from, except, desc) => {
|
|
18
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
19
|
+
for (let key of __getOwnPropNames(from))
|
|
20
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
21
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
22
|
+
}
|
|
23
|
+
return to;
|
|
24
|
+
};
|
|
25
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
26
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
27
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
28
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
29
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
30
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
31
|
+
mod
|
|
32
|
+
));
|
|
33
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
34
|
+
|
|
35
|
+
// src/client-plugin.ts
|
|
36
|
+
var client_plugin_exports = {};
|
|
37
|
+
__export(client_plugin_exports, {
|
|
38
|
+
apply: () => apply,
|
|
39
|
+
inject: () => inject,
|
|
40
|
+
pollDelayForFailures: () => pollDelayForFailures,
|
|
41
|
+
resolveSeatProvider: () => resolveSeatProvider
|
|
42
|
+
});
|
|
43
|
+
module.exports = __toCommonJS(client_plugin_exports);
|
|
44
|
+
var import_react4 = __toESM(require("react"), 1);
|
|
45
|
+
|
|
46
|
+
// src/visibility.ts
|
|
47
|
+
function normalizePattern(pattern) {
|
|
48
|
+
return pattern.trim().toLowerCase();
|
|
49
|
+
}
|
|
50
|
+
function globMatch(pattern, value) {
|
|
51
|
+
let p = 0;
|
|
52
|
+
let v = 0;
|
|
53
|
+
let star = -1;
|
|
54
|
+
let resume = 0;
|
|
55
|
+
while (v < value.length) {
|
|
56
|
+
if (p < pattern.length && pattern[p] === "*") {
|
|
57
|
+
star = p;
|
|
58
|
+
resume = v;
|
|
59
|
+
p += 1;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
if (p < pattern.length && pattern[p] === value[v]) {
|
|
63
|
+
p += 1;
|
|
64
|
+
v += 1;
|
|
65
|
+
continue;
|
|
66
|
+
}
|
|
67
|
+
if (star >= 0) {
|
|
68
|
+
p = star + 1;
|
|
69
|
+
resume += 1;
|
|
70
|
+
v = resume;
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
while (p < pattern.length && pattern[p] === "*") p += 1;
|
|
76
|
+
return p === pattern.length;
|
|
77
|
+
}
|
|
78
|
+
function matchesPattern(pattern, provider, model) {
|
|
79
|
+
const normalized = normalizePattern(pattern);
|
|
80
|
+
const separator = normalized.indexOf(":");
|
|
81
|
+
const providerGlob = separator < 0 ? "*" : normalized.slice(0, separator);
|
|
82
|
+
const modelGlob = separator < 0 ? normalized : normalized.slice(separator + 1);
|
|
83
|
+
if (!globMatch(providerGlob, provider.trim().toLowerCase())) return false;
|
|
84
|
+
return globMatch(modelGlob, model === void 0 ? "" : model.trim().toLowerCase());
|
|
85
|
+
}
|
|
86
|
+
function matchesAnyPattern(config, provider, model) {
|
|
87
|
+
const patterns = config?.hiddenPatterns;
|
|
88
|
+
if (patterns === void 0) return false;
|
|
89
|
+
return patterns.some((pattern) => matchesPattern(pattern, provider, model));
|
|
90
|
+
}
|
|
91
|
+
function isProviderVisible(provider, settings, config) {
|
|
92
|
+
const explicit = settings.providers[provider];
|
|
93
|
+
if (explicit !== void 0) return explicit;
|
|
94
|
+
return !matchesAnyPattern(config, provider, void 0);
|
|
95
|
+
}
|
|
96
|
+
function isModelVisible(provider, model, settings, config) {
|
|
97
|
+
if (settings.providers[provider] === false) return false;
|
|
98
|
+
const explicitModel = settings.models[modelKey(provider, model)];
|
|
99
|
+
if (explicitModel !== void 0) return explicitModel;
|
|
100
|
+
const explicitProvider = settings.providers[provider];
|
|
101
|
+
if (explicitProvider !== void 0) return explicitProvider;
|
|
102
|
+
return !matchesAnyPattern(config, provider, model);
|
|
103
|
+
}
|
|
104
|
+
function modelKey(provider, model) {
|
|
105
|
+
return `${provider}:${model}`;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// src/settings-ui.ts
|
|
109
|
+
var import_react = __toESM(require("react"), 1);
|
|
110
|
+
|
|
111
|
+
// src/concurrency.ts
|
|
112
|
+
function concurrencyFor(table, provider) {
|
|
113
|
+
const exact = table[provider];
|
|
114
|
+
if (exact !== void 0) return exact <= 0 ? Infinity : exact;
|
|
115
|
+
const fallback = table["default"];
|
|
116
|
+
if (fallback === void 0 || fallback <= 0) return Infinity;
|
|
117
|
+
return fallback;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/settings-ui.ts
|
|
121
|
+
function buildProviderViews(input) {
|
|
122
|
+
const config = { hiddenPatterns: input.patterns };
|
|
123
|
+
return input.providers.map((entry) => ({
|
|
124
|
+
provider: entry.provider,
|
|
125
|
+
displayName: entry.displayName,
|
|
126
|
+
visible: isProviderVisible(entry.provider, input.settings, config),
|
|
127
|
+
models: entry.models.map((model) => ({
|
|
128
|
+
model: model.model,
|
|
129
|
+
name: model.name,
|
|
130
|
+
visible: isModelVisible(entry.provider, model.model, input.settings, config)
|
|
131
|
+
}))
|
|
132
|
+
}));
|
|
133
|
+
}
|
|
134
|
+
function summarize(views) {
|
|
135
|
+
let providers = 0;
|
|
136
|
+
let models = 0;
|
|
137
|
+
for (const view of views) {
|
|
138
|
+
if (!view.visible) providers += 1;
|
|
139
|
+
for (const model of view.models) if (!model.visible) models += 1;
|
|
140
|
+
}
|
|
141
|
+
return { providers, models, total: providers + models };
|
|
142
|
+
}
|
|
143
|
+
var rowStyle = { display: "flex", alignItems: "center", gap: 8, padding: "2px 0" };
|
|
144
|
+
var nameStyle = { flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" };
|
|
145
|
+
var buttonStyle = {
|
|
146
|
+
border: "1px solid var(--dsw-alias-border-inverted, rgba(127,127,127,.35))",
|
|
147
|
+
background: "transparent",
|
|
148
|
+
color: "inherit",
|
|
149
|
+
borderRadius: 6,
|
|
150
|
+
padding: "1px 8px",
|
|
151
|
+
fontSize: 11,
|
|
152
|
+
cursor: "pointer"
|
|
153
|
+
};
|
|
154
|
+
var COLLAPSE_THRESHOLD = 8;
|
|
155
|
+
function defaultExpanded(count) {
|
|
156
|
+
return count <= COLLAPSE_THRESHOLD;
|
|
157
|
+
}
|
|
158
|
+
function useAutoCollapse(count) {
|
|
159
|
+
const [manual, setManual] = import_react.default.useState(void 0);
|
|
160
|
+
const collapsed = manual ?? !defaultExpanded(count);
|
|
161
|
+
const toggle2 = () => {
|
|
162
|
+
setManual(!collapsed);
|
|
163
|
+
};
|
|
164
|
+
return [collapsed, toggle2];
|
|
165
|
+
}
|
|
166
|
+
function CollapseToggle(props) {
|
|
167
|
+
return import_react.default.createElement(
|
|
168
|
+
"button",
|
|
169
|
+
{
|
|
170
|
+
type: "button",
|
|
171
|
+
style: buttonStyle,
|
|
172
|
+
title: props.collapsed ? "\u5C55\u5F00" : "\u6536\u8D77",
|
|
173
|
+
"aria-expanded": !props.collapsed,
|
|
174
|
+
onClick: () => props.onToggle()
|
|
175
|
+
},
|
|
176
|
+
(props.collapsed ? "\u25B8 " : "\u25BE ") + props.label + "\uFF08" + props.count + "\uFF09"
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
function toggle(label, visible, onToggle) {
|
|
180
|
+
return import_react.default.createElement(
|
|
181
|
+
"button",
|
|
182
|
+
{
|
|
183
|
+
type: "button",
|
|
184
|
+
style: buttonStyle,
|
|
185
|
+
title: visible ? "\u9690\u85CF" : "\u663E\u793A",
|
|
186
|
+
"aria-pressed": !visible,
|
|
187
|
+
onClick: onToggle
|
|
188
|
+
},
|
|
189
|
+
visible ? "\u{1F441}" : "\u{1F6AB}",
|
|
190
|
+
label.length > 0 ? import_react.default.createElement("span", { style: { marginLeft: 4 } }, label) : null
|
|
191
|
+
);
|
|
192
|
+
}
|
|
193
|
+
function ProviderVisibilityCard(props) {
|
|
194
|
+
const { view, actions } = props;
|
|
195
|
+
const [modelsCollapsed, toggleModels] = useAutoCollapse(view.models.length);
|
|
196
|
+
return import_react.default.createElement(
|
|
197
|
+
"div",
|
|
198
|
+
{ style: { marginTop: 8, fontSize: 12 } },
|
|
199
|
+
import_react.default.createElement(
|
|
200
|
+
"div",
|
|
201
|
+
{ style: rowStyle },
|
|
202
|
+
import_react.default.createElement("span", { style: nameStyle }, view.visible ? "\u63D0\u4F9B\u65B9\u53EF\u89C1" : "\u63D0\u4F9B\u65B9\u5DF2\u9690\u85CF"),
|
|
203
|
+
toggle("", view.visible, () => actions.setProvider(view.provider, !view.visible))
|
|
204
|
+
),
|
|
205
|
+
view.models.length > 0 ? import_react.default.createElement(
|
|
206
|
+
import_react.default.Fragment,
|
|
207
|
+
null,
|
|
208
|
+
import_react.default.createElement(
|
|
209
|
+
"div",
|
|
210
|
+
{ style: { ...rowStyle, paddingLeft: 12 } },
|
|
211
|
+
CollapseToggle({ collapsed: modelsCollapsed, count: view.models.length, label: "\u6A21\u578B", onToggle: toggleModels })
|
|
212
|
+
),
|
|
213
|
+
modelsCollapsed ? null : view.models.map(
|
|
214
|
+
(model) => import_react.default.createElement(
|
|
215
|
+
"div",
|
|
216
|
+
{ key: model.model, style: { ...rowStyle, paddingLeft: 12, opacity: model.visible ? 1 : 0.5 } },
|
|
217
|
+
import_react.default.createElement("span", { style: nameStyle }, model.name),
|
|
218
|
+
toggle("", model.visible, () => actions.setModel(view.provider, model.model, !model.visible))
|
|
219
|
+
)
|
|
220
|
+
)
|
|
221
|
+
) : import_react.default.createElement(
|
|
222
|
+
"div",
|
|
223
|
+
{ style: { ...rowStyle, paddingLeft: 12, opacity: 0.6, fontSize: 11 } },
|
|
224
|
+
"\u8BE5\u63D0\u4F9B\u65B9\u6682\u65E0\u6A21\u578B\u5217\u8868\uFF0C\u65E0\u6CD5\u9010\u6A21\u578B\u63A7\u5236"
|
|
225
|
+
)
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
function VisibilityFooter(props) {
|
|
229
|
+
const { summary, queue, actions } = props;
|
|
230
|
+
const parts = [];
|
|
231
|
+
if (summary.total === 0) parts.push("\u6CA1\u6709\u9690\u85CF\u7684\u6A21\u578B");
|
|
232
|
+
else parts.push(`\u5DF2\u9690\u85CF ${summary.providers} \u4E2A\u63D0\u4F9B\u65B9 / ${summary.models} \u4E2A\u6A21\u578B`);
|
|
233
|
+
if (queue.queued > 0 || queue.cooling > 0) parts.push(`\u6392\u961F ${queue.queued} \xB7 \u51B7\u5374 ${queue.cooling}`);
|
|
234
|
+
return import_react.default.createElement(
|
|
235
|
+
"div",
|
|
236
|
+
{ style: { display: "flex", alignItems: "center", gap: 8, padding: "8px 0", fontSize: 12 } },
|
|
237
|
+
import_react.default.createElement("span", { style: nameStyle }, parts.join(" \xB7 ")),
|
|
238
|
+
summary.total > 0 ? import_react.default.createElement("button", { type: "button", style: buttonStyle, onClick: () => actions.resetAll() }, "\u5168\u90E8\u6062\u590D") : null
|
|
239
|
+
);
|
|
240
|
+
}
|
|
241
|
+
var pluginCardStyle = {
|
|
242
|
+
border: "0.5px solid var(--dsw-alias-border-l4, rgba(127,127,127,.28))",
|
|
243
|
+
background: "var(--dsw-alias-bg-layer-3, transparent)",
|
|
244
|
+
borderRadius: 16,
|
|
245
|
+
listStyle: "none"
|
|
246
|
+
};
|
|
247
|
+
var pluginHeaderStyle = {
|
|
248
|
+
appearance: "none",
|
|
249
|
+
width: "100%",
|
|
250
|
+
font: "inherit",
|
|
251
|
+
color: "inherit",
|
|
252
|
+
textAlign: "left",
|
|
253
|
+
cursor: "pointer",
|
|
254
|
+
background: "none",
|
|
255
|
+
border: 0,
|
|
256
|
+
borderRadius: 12,
|
|
257
|
+
display: "flex",
|
|
258
|
+
alignItems: "center",
|
|
259
|
+
gap: 12,
|
|
260
|
+
padding: "14px 16px"
|
|
261
|
+
};
|
|
262
|
+
var pluginHeadTextStyle = { display: "flex", flexDirection: "column", flex: 1, gap: 4, minWidth: 0 };
|
|
263
|
+
var pluginNameStyle = { color: "var(--dsw-alias-label-primary, inherit)", fontSize: 15, fontWeight: 600, lineHeight: 1.4 };
|
|
264
|
+
var pluginDescriptionStyle = { color: "var(--dsw-alias-label-tertiary, inherit)", fontSize: 13, lineHeight: 1.5 };
|
|
265
|
+
var pluginBodyStyle = { borderTop: "0.5px solid var(--dsw-alias-border-l2, rgba(127,127,127,.2))", margin: "0 16px", paddingBottom: 8 };
|
|
266
|
+
var pluginFieldStyle = { display: "flex", flexDirection: "column", gap: 4, padding: "12px 0 0" };
|
|
267
|
+
var pluginLabelStyle = { color: "var(--dsw-alias-label-primary, inherit)", fontSize: 13, lineHeight: 1.5 };
|
|
268
|
+
var pluginHintStyle = { color: "var(--dsw-alias-label-tertiary, inherit)", margin: 0, fontSize: 12, lineHeight: 1.5 };
|
|
269
|
+
var pluginInputStyle = {
|
|
270
|
+
border: "0.5px solid var(--dsw-alias-border-l4, rgba(127,127,127,.28))",
|
|
271
|
+
background: "var(--dsw-alias-bg-layer-3, transparent)",
|
|
272
|
+
height: 34,
|
|
273
|
+
font: "inherit",
|
|
274
|
+
color: "var(--dsw-alias-label-primary, inherit)",
|
|
275
|
+
borderRadius: 8,
|
|
276
|
+
padding: "0 12px",
|
|
277
|
+
fontSize: 13
|
|
278
|
+
};
|
|
279
|
+
var pluginFooterStyle = { borderTop: "0.5px solid var(--dsw-alias-border-l2, rgba(127,127,127,.2))", display: "flex", justifyContent: "flex-end", alignItems: "center", gap: 8, padding: "12px 0 4px" };
|
|
280
|
+
var pluginFailedStyle = { flex: 1, minWidth: 0, margin: 0, color: "var(--dsw-alias-label-error, #d33)", fontSize: 12, lineHeight: 1.5 };
|
|
281
|
+
var pluginActionStyle = { appearance: "none", font: "inherit", cursor: "pointer", border: "1px solid var(--dsw-alias-border-l2, rgba(127,127,127,.28))", color: "var(--dsw-alias-label-secondary, inherit)", background: "none", borderRadius: 8, padding: "5px 14px", fontSize: 13, lineHeight: 1.5 };
|
|
282
|
+
function PluginConfigCard(props) {
|
|
283
|
+
const { config, actions } = props;
|
|
284
|
+
const effectiveDefault = config.defaultConcurrency ?? config.defaults?.defaultConcurrency ?? 2;
|
|
285
|
+
const effectivePer = config.perProviderConcurrency ?? {};
|
|
286
|
+
const [open, setOpen] = import_react.default.useState(false);
|
|
287
|
+
const [waitSec, setWaitSec] = import_react.default.useState(() => String(Math.round(config.maxWaitMs / 1e3)));
|
|
288
|
+
const [depth, setDepth] = import_react.default.useState(() => String(config.maxQueueDepth));
|
|
289
|
+
const [defaultConc, setDefaultConc] = import_react.default.useState(() => String(effectiveDefault));
|
|
290
|
+
const [perDrafts, setPerDrafts] = import_react.default.useState(
|
|
291
|
+
() => Object.fromEntries(Object.entries(effectivePer).map(([key, value]) => [key, String(value)]))
|
|
292
|
+
);
|
|
293
|
+
const [dirty, setDirty] = import_react.default.useState(false);
|
|
294
|
+
const [saving, setSaving] = import_react.default.useState(false);
|
|
295
|
+
const [failed, setFailed] = import_react.default.useState(false);
|
|
296
|
+
const legacyHost = config.defaultConcurrency === void 0 || config.perProviderConcurrency === void 0;
|
|
297
|
+
const perTableJson = JSON.stringify(effectivePer);
|
|
298
|
+
import_react.default.useEffect(() => {
|
|
299
|
+
if (dirty || saving) return;
|
|
300
|
+
setWaitSec(String(Math.round(config.maxWaitMs / 1e3)));
|
|
301
|
+
setDepth(String(config.maxQueueDepth));
|
|
302
|
+
setDefaultConc(String(effectiveDefault));
|
|
303
|
+
setPerDrafts(Object.fromEntries(Object.entries(effectivePer).map(([key, value]) => [key, String(value)])));
|
|
304
|
+
}, [config.revision, config.maxWaitMs, config.maxQueueDepth, effectiveDefault, perTableJson, dirty, saving]);
|
|
305
|
+
const seconds = Number(waitSec);
|
|
306
|
+
const count = Number(depth);
|
|
307
|
+
const defaultCount = Number(defaultConc);
|
|
308
|
+
const perTable = (() => {
|
|
309
|
+
const table = {};
|
|
310
|
+
let bad = false;
|
|
311
|
+
for (const [key, raw] of Object.entries(perDrafts)) {
|
|
312
|
+
const trimmed = raw.trim();
|
|
313
|
+
if (trimmed === "") continue;
|
|
314
|
+
const value = Number(trimmed);
|
|
315
|
+
if (!Number.isFinite(value) || value < 0) {
|
|
316
|
+
bad = true;
|
|
317
|
+
break;
|
|
318
|
+
}
|
|
319
|
+
if (key.length > 0) table[key] = Math.floor(value);
|
|
320
|
+
}
|
|
321
|
+
return { table, bad };
|
|
322
|
+
})();
|
|
323
|
+
const invalid = !Number.isFinite(seconds) || seconds < 0 || !Number.isFinite(count) || count < 1 || !Number.isFinite(defaultCount) || defaultCount < 0 || perTable.bad;
|
|
324
|
+
const stage = (setter) => (event) => {
|
|
325
|
+
setter(event.target.value);
|
|
326
|
+
setDirty(true);
|
|
327
|
+
setFailed(false);
|
|
328
|
+
};
|
|
329
|
+
const onSave = async () => {
|
|
330
|
+
if (invalid || saving) return;
|
|
331
|
+
setSaving(true);
|
|
332
|
+
setFailed(false);
|
|
333
|
+
const nextWaitMs = Math.round(seconds * 1e3);
|
|
334
|
+
const nextDepth = Math.floor(count);
|
|
335
|
+
const nextDefault = Math.floor(defaultCount);
|
|
336
|
+
const patch = { expectedRevision: config.revision };
|
|
337
|
+
if (nextWaitMs !== config.maxWaitMs) patch.maxWaitMs = nextWaitMs;
|
|
338
|
+
if (nextDepth !== config.maxQueueDepth) patch.maxQueueDepth = nextDepth;
|
|
339
|
+
if (nextDefault !== effectiveDefault) patch.defaultConcurrency = nextDefault;
|
|
340
|
+
if (JSON.stringify(perTable.table) !== JSON.stringify(effectivePer)) patch.perProviderConcurrency = perTable.table;
|
|
341
|
+
const result = await actions.setQueue(patch);
|
|
342
|
+
setSaving(false);
|
|
343
|
+
if (result.ok) setDirty(false);
|
|
344
|
+
else setFailed(result.error ?? "\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
345
|
+
};
|
|
346
|
+
const onDiscard = () => {
|
|
347
|
+
setWaitSec(String(Math.round(config.maxWaitMs / 1e3)));
|
|
348
|
+
setDepth(String(config.maxQueueDepth));
|
|
349
|
+
setDefaultConc(String(effectiveDefault));
|
|
350
|
+
setPerDrafts(Object.fromEntries(Object.entries(effectivePer).map(([key, value]) => [key, String(value)])));
|
|
351
|
+
setDirty(false);
|
|
352
|
+
setFailed(false);
|
|
353
|
+
};
|
|
354
|
+
const onResetDefaults = async () => {
|
|
355
|
+
if (saving) return;
|
|
356
|
+
setSaving(true);
|
|
357
|
+
setFailed(false);
|
|
358
|
+
const ok = await actions.resetQueue(config.revision);
|
|
359
|
+
setSaving(false);
|
|
360
|
+
if (ok) {
|
|
361
|
+
setDirty(false);
|
|
362
|
+
setFailed(false);
|
|
363
|
+
} else setFailed("\u4FDD\u5B58\u5931\u8D25\uFF0C\u8BF7\u91CD\u8BD5\u3002");
|
|
364
|
+
};
|
|
365
|
+
const field = (input) => import_react.default.createElement(
|
|
366
|
+
"div",
|
|
367
|
+
{ style: pluginFieldStyle },
|
|
368
|
+
import_react.default.createElement("label", { htmlFor: input.id, style: pluginLabelStyle }, input.label),
|
|
369
|
+
import_react.default.createElement("input", {
|
|
370
|
+
id: input.id,
|
|
371
|
+
type: "number",
|
|
372
|
+
style: pluginInputStyle,
|
|
373
|
+
min: input.min,
|
|
374
|
+
...input.step === void 0 ? {} : { step: input.step },
|
|
375
|
+
disabled: saving,
|
|
376
|
+
value: input.value,
|
|
377
|
+
onChange: input.onEdit
|
|
378
|
+
}),
|
|
379
|
+
import_react.default.createElement("p", { style: pluginHintStyle }, invalid ? input.invalidHint : input.hint)
|
|
380
|
+
);
|
|
381
|
+
const header = import_react.default.createElement(
|
|
382
|
+
"button",
|
|
383
|
+
{
|
|
384
|
+
type: "button",
|
|
385
|
+
style: pluginHeaderStyle,
|
|
386
|
+
"aria-expanded": open,
|
|
387
|
+
"aria-label": (open ? "\u6536\u8D77\u8BBE\u7F6E: " : "\u5C55\u5F00\u8BBE\u7F6E: ") + "LLM \u6392\u961F\u63A7\u5236",
|
|
388
|
+
onClick: () => setOpen(!open)
|
|
389
|
+
},
|
|
390
|
+
import_react.default.createElement(
|
|
391
|
+
"span",
|
|
392
|
+
{ style: pluginHeadTextStyle },
|
|
393
|
+
import_react.default.createElement("span", { style: pluginNameStyle }, "LLM \u6392\u961F\u63A7\u5236"),
|
|
394
|
+
import_react.default.createElement("span", { style: pluginDescriptionStyle }, "\u6309 provider \u7684\u5E76\u53D1\u4E0A\u9650\u3001\u51C6\u5165\u6392\u961F\u3001\u51B7\u5374\u7B49\u5F85\u4E0E\u91CD\u8BD5\u9884\u7B97\u3002")
|
|
395
|
+
),
|
|
396
|
+
config.overridden && !dirty ? import_react.default.createElement("span", { style: pluginHintStyle }, "\u5DF2\u81EA\u5B9A\u4E49") : null,
|
|
397
|
+
import_react.default.createElement(
|
|
398
|
+
"svg",
|
|
399
|
+
{
|
|
400
|
+
width: 14,
|
|
401
|
+
height: 14,
|
|
402
|
+
viewBox: "0 0 14 14",
|
|
403
|
+
fill: "none",
|
|
404
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
405
|
+
style: { color: "var(--dsw-alias-label-tertiary, inherit)", flex: "none", transition: "transform .16s", transform: open ? "rotate(180deg)" : "none" }
|
|
406
|
+
},
|
|
407
|
+
import_react.default.createElement("path", { d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z", fill: "currentColor" })
|
|
408
|
+
)
|
|
409
|
+
);
|
|
410
|
+
const stagePer = (provider) => (event) => {
|
|
411
|
+
const value = event.target.value;
|
|
412
|
+
setPerDrafts((prev) => ({ ...prev, [provider]: value }));
|
|
413
|
+
setDirty(true);
|
|
414
|
+
setFailed(false);
|
|
415
|
+
};
|
|
416
|
+
const directory = (props.providers ?? []).filter((entry) => entry.provider.length > 0);
|
|
417
|
+
const rowIds = directory.map((entry) => entry.provider);
|
|
418
|
+
for (const key of [...Object.keys(effectivePer), ...Object.keys(perDrafts)]) {
|
|
419
|
+
if (key.length > 0 && !rowIds.includes(key)) rowIds.push(key);
|
|
420
|
+
}
|
|
421
|
+
const displayNameOf = (id) => directory.find((entry) => entry.provider === id)?.displayName ?? id;
|
|
422
|
+
const lookupTable = { default: effectiveDefault, ...effectivePer };
|
|
423
|
+
const inheritHint = (id) => {
|
|
424
|
+
const effective = concurrencyFor(lookupTable, id);
|
|
425
|
+
return Number.isFinite(effective) ? `\u9ED8\u8BA4 ${effective}` : "\u4E0D\u9650\u5236\uFF08\u9ED8\u8BA4\uFF09";
|
|
426
|
+
};
|
|
427
|
+
const concurrencySection = import_react.default.createElement(
|
|
428
|
+
"div",
|
|
429
|
+
{ style: pluginFieldStyle },
|
|
430
|
+
import_react.default.createElement("span", { style: pluginLabelStyle }, "\u5404\u63D0\u4F9B\u65B9\u5E76\u53D1\u4E0A\u9650"),
|
|
431
|
+
import_react.default.createElement("p", { style: pluginHintStyle }, "\u7559\u7A7A\u7EE7\u627F\u9ED8\u8BA4\uFF1B\u586B 0 \u4E3A\u8BE5\u63D0\u4F9B\u65B9\u4E0D\u9650\u5236\u3002"),
|
|
432
|
+
rowIds.length === 0 ? import_react.default.createElement("p", { style: pluginHintStyle }, "\u6682\u65E0\u5DF2\u77E5\u63D0\u4F9B\u65B9\uFF0C\u5148\u8BBE\u7F6E\u9ED8\u8BA4\u5E76\u53D1\u5373\u53EF\u3002") : rowIds.map(
|
|
433
|
+
(id) => import_react.default.createElement(
|
|
434
|
+
"div",
|
|
435
|
+
{ key: id, style: { ...rowStyle, paddingLeft: 12 } },
|
|
436
|
+
import_react.default.createElement("span", { style: nameStyle }, displayNameOf(id)),
|
|
437
|
+
import_react.default.createElement("input", {
|
|
438
|
+
type: "number",
|
|
439
|
+
style: { ...pluginInputStyle, width: 88, height: 28, flex: "none" },
|
|
440
|
+
min: 0,
|
|
441
|
+
step: 1,
|
|
442
|
+
disabled: saving,
|
|
443
|
+
value: perDrafts[id] ?? "",
|
|
444
|
+
placeholder: inheritHint(id),
|
|
445
|
+
"aria-label": `${displayNameOf(id)} \u5E76\u53D1\u4E0A\u9650`,
|
|
446
|
+
onChange: stagePer(id)
|
|
447
|
+
})
|
|
448
|
+
)
|
|
449
|
+
)
|
|
450
|
+
);
|
|
451
|
+
const body = import_react.default.createElement(
|
|
452
|
+
"div",
|
|
453
|
+
{ style: pluginBodyStyle },
|
|
454
|
+
legacyHost ? import_react.default.createElement("p", { style: pluginFailedStyle }, "\u68C0\u6D4B\u5230 Host \u4E3A\u65E7\u7248\u672C\uFF1A\u5E76\u53D1\u8BBE\u7F6E\u4FDD\u5B58\u4F1A\u5931\u8D25\uFF0C\u8BF7\u91CD\u8F7D\u63D2\u4EF6\u540E\u518D\u8BD5\uFF1B\u6392\u961F\u8D85\u65F6\u4E0E\u961F\u5217\u6DF1\u5EA6\u4ECD\u53EF\u4FDD\u5B58\u3002") : null,
|
|
455
|
+
field({ id: "llm-ctl-queue-max-wait", label: "\u6392\u961F\u8D85\u65F6\uFF08\u79D2\uFF09", hint: "\u5355\u8BF7\u6C42\u6392\u961F\u8010\u5FC3\uFF0C\u4E0E\u613F\u610F\u91C7\u7EB3\u7684 provider \u51B7\u5374\u5171\u7528\u4E00\u4E2A\u9884\u7B97\u3002", invalidHint: "\u8BF7\u8F93\u5165 \u2265 0 \u7684\u79D2\u6570\u3002", value: waitSec, min: 0, onEdit: stage(setWaitSec) }),
|
|
456
|
+
field({ id: "llm-ctl-queue-depth", label: "\u961F\u5217\u6DF1\u5EA6", hint: "\u540C\u4E00\u65F6\u523B\u5141\u8BB8\u6392\u961F\u7684\u6700\u5927\u8BF7\u6C42\u6570\u3002", invalidHint: "\u8BF7\u8F93\u5165 \u2265 1 \u7684\u6574\u6570\u3002", value: depth, min: 1, step: 1, onEdit: stage(setDepth) }),
|
|
457
|
+
field({ id: "llm-ctl-queue-concurrency", label: "\u9ED8\u8BA4\u5E76\u53D1\u4E0A\u9650", hint: "\u6BCF\u4E2A\u63D0\u4F9B\u65B9\u540C\u65F6\u8FDB\u884C\u7684\u8BF7\u6C42\u6570\uFF0C\u8D85\u9650\u7684\u6392\u961F\u7B49\u5F85\uFF1B0 \u8868\u793A\u4E0D\u9650\u5236\uFF08\u9ED8\u8BA4\uFF09\u3002", invalidHint: "\u8BF7\u8F93\u5165 \u2265 0 \u7684\u6574\u6570\uFF080 = \u4E0D\u9650\u5236\uFF09\u3002", value: defaultConc, min: 0, step: 1, onEdit: stage(setDefaultConc) }),
|
|
458
|
+
concurrencySection,
|
|
459
|
+
import_react.default.createElement(
|
|
460
|
+
"div",
|
|
461
|
+
{ style: pluginFooterStyle },
|
|
462
|
+
failed !== false ? import_react.default.createElement("p", { style: pluginFailedStyle }, failed) : null,
|
|
463
|
+
config.overridden ? import_react.default.createElement(
|
|
464
|
+
"button",
|
|
465
|
+
{ type: "button", style: pluginActionStyle, disabled: saving, onClick: () => void onResetDefaults() },
|
|
466
|
+
"\u6062\u590D\u9ED8\u8BA4"
|
|
467
|
+
) : null,
|
|
468
|
+
import_react.default.createElement(
|
|
469
|
+
"button",
|
|
470
|
+
{ type: "button", style: pluginActionStyle, disabled: !dirty || invalid || saving, onClick: onDiscard },
|
|
471
|
+
"\u653E\u5F03\u4FEE\u6539"
|
|
472
|
+
),
|
|
473
|
+
import_react.default.createElement(
|
|
474
|
+
"button",
|
|
475
|
+
{ type: "button", style: { ...pluginActionStyle, background: "var(--dsw-alias-label-primary, currentColor)", color: "var(--dsw-alias-bg-layer-3, inherit)" }, disabled: !dirty || invalid || saving, onClick: () => void onSave() },
|
|
476
|
+
saving ? "\u4FDD\u5B58\u4E2D\u2026" : "\u4FDD\u5B58"
|
|
477
|
+
)
|
|
478
|
+
)
|
|
479
|
+
);
|
|
480
|
+
return import_react.default.createElement("li", { style: pluginCardStyle }, header, open ? body : null);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// src/menu-filter.ts
|
|
484
|
+
var EMPTY_STATE_ID = "dsh-llm-ctl-empty";
|
|
485
|
+
var DOCUMENT_POSITION_FOLLOWING = 4;
|
|
486
|
+
function isModelMenuLabel(label) {
|
|
487
|
+
const value = label.toLowerCase();
|
|
488
|
+
return value.includes("model") || label.includes("\u63A8\u7406\u7B49\u7EA7") || value.includes("effort");
|
|
489
|
+
}
|
|
490
|
+
function isElement(node) {
|
|
491
|
+
return typeof node === "object" && node !== null && typeof node.matches === "function";
|
|
492
|
+
}
|
|
493
|
+
function groupsOf(roots) {
|
|
494
|
+
const groups = roots?.groups;
|
|
495
|
+
return groups ?? void 0;
|
|
496
|
+
}
|
|
497
|
+
function menuOf(roots) {
|
|
498
|
+
const menu = roots?.menu;
|
|
499
|
+
return menu ?? void 0;
|
|
500
|
+
}
|
|
501
|
+
function documentOf(...candidates) {
|
|
502
|
+
for (const candidate of candidates) {
|
|
503
|
+
const owner = candidate?.ownerDocument;
|
|
504
|
+
if (owner !== void 0 && owner !== null) return owner;
|
|
505
|
+
}
|
|
506
|
+
return typeof document === "undefined" ? void 0 : document;
|
|
507
|
+
}
|
|
508
|
+
function classAttributeOf(element) {
|
|
509
|
+
return element.getAttribute("class") ?? "";
|
|
510
|
+
}
|
|
511
|
+
function findGroupsContainer(menu) {
|
|
512
|
+
for (const candidate of menu.querySelectorAll("div")) {
|
|
513
|
+
if (candidate.getAttribute("role") !== null) continue;
|
|
514
|
+
if (candidate.querySelector('[role="group"]') !== null) return candidate;
|
|
515
|
+
}
|
|
516
|
+
for (const child of Array.from(menu.children)) {
|
|
517
|
+
if (child.querySelector('[role="group"]') !== null) return child;
|
|
518
|
+
}
|
|
519
|
+
return menu;
|
|
520
|
+
}
|
|
521
|
+
function findModelMenu(root) {
|
|
522
|
+
try {
|
|
523
|
+
if (root === void 0 || root === null) return void 0;
|
|
524
|
+
let menu;
|
|
525
|
+
if (isElement(root) && root.matches('div[role="menu"]') && isModelMenuLabel(root.getAttribute("aria-label") ?? "")) {
|
|
526
|
+
menu = root;
|
|
527
|
+
}
|
|
528
|
+
const candidates = root.querySelectorAll('div[role="menu"]');
|
|
529
|
+
if (menu === void 0) {
|
|
530
|
+
for (const candidate of candidates) {
|
|
531
|
+
if (isModelMenuLabel(candidate.getAttribute("aria-label") ?? "")) {
|
|
532
|
+
menu = candidate;
|
|
533
|
+
break;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (menu === void 0) {
|
|
538
|
+
for (const candidate of candidates) {
|
|
539
|
+
if ((candidate.getAttribute("aria-label") ?? "").trim() !== "") continue;
|
|
540
|
+
if (candidate.querySelector('[role="group"]') === null) continue;
|
|
541
|
+
menu = candidate;
|
|
542
|
+
break;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
if (menu === void 0) return void 0;
|
|
546
|
+
return { menu, groups: findGroupsContainer(menu) };
|
|
547
|
+
} catch {
|
|
548
|
+
return void 0;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
function readGroupTitle(group) {
|
|
552
|
+
try {
|
|
553
|
+
const titled = group.querySelector("div[id]");
|
|
554
|
+
const fromTitle = titled?.textContent?.trim() ?? "";
|
|
555
|
+
if (fromTitle !== "") return fromTitle;
|
|
556
|
+
const labelledBy = (group.getAttribute("aria-labelledby") ?? "").trim();
|
|
557
|
+
if (labelledBy !== "") {
|
|
558
|
+
const owner = group.ownerDocument;
|
|
559
|
+
for (const id of labelledBy.split(/\s+/)) {
|
|
560
|
+
const target = owner?.getElementById(id);
|
|
561
|
+
const text = target?.textContent?.trim() ?? "";
|
|
562
|
+
if (text !== "") return text;
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
return (group.getAttribute("aria-label") ?? "").trim();
|
|
566
|
+
} catch {
|
|
567
|
+
return "";
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
function readRowName(row) {
|
|
571
|
+
try {
|
|
572
|
+
const title = (row.getAttribute("title") ?? "").trim();
|
|
573
|
+
if (title !== "") return title;
|
|
574
|
+
const named = row.querySelector('[class*="modelName"]');
|
|
575
|
+
const fromClass = named?.textContent?.trim() ?? "";
|
|
576
|
+
if (fromClass !== "") return fromClass;
|
|
577
|
+
return (row.textContent ?? "").trim();
|
|
578
|
+
} catch {
|
|
579
|
+
return "";
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
function listModelRows(roots) {
|
|
583
|
+
const rows = [];
|
|
584
|
+
const groups = groupsOf(roots);
|
|
585
|
+
if (groups === void 0) return rows;
|
|
586
|
+
try {
|
|
587
|
+
for (const group of groups.querySelectorAll('section[role="group"]')) {
|
|
588
|
+
const providerName = readGroupTitle(group);
|
|
589
|
+
for (const row of group.querySelectorAll('[role="menuitemradio"]')) {
|
|
590
|
+
rows.push({ row, providerName, modelName: readRowName(row) });
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
} catch {
|
|
594
|
+
}
|
|
595
|
+
return rows;
|
|
596
|
+
}
|
|
597
|
+
function parseMenuQuery(raw) {
|
|
598
|
+
const providerTerms = [];
|
|
599
|
+
const modelTerms = [];
|
|
600
|
+
for (const token of raw.trim().toLowerCase().split(/\s+/).filter((part) => part.length > 0)) {
|
|
601
|
+
const providerTerm = token.startsWith("p:") ? token.slice(2) : token.startsWith("provider:") ? token.slice(9) : void 0;
|
|
602
|
+
if (providerTerm !== void 0) {
|
|
603
|
+
if (providerTerm.length > 0) providerTerms.push(providerTerm);
|
|
604
|
+
} else {
|
|
605
|
+
modelTerms.push(token);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
return { providerTerms, modelTerms };
|
|
609
|
+
}
|
|
610
|
+
function matchesQuery(row, query2) {
|
|
611
|
+
const { providerTerms, modelTerms } = parseMenuQuery(query2);
|
|
612
|
+
const provider = row.providerName.toLowerCase();
|
|
613
|
+
const model = row.modelName.toLowerCase();
|
|
614
|
+
return providerTerms.every((term) => provider.includes(term)) && modelTerms.every((term) => model.includes(term) || provider.includes(term));
|
|
615
|
+
}
|
|
616
|
+
function applyMenuFilter(roots, options = {}) {
|
|
617
|
+
const result = { shown: 0, hidden: 0, groupsHidden: 0 };
|
|
618
|
+
const groups = groupsOf(roots);
|
|
619
|
+
if (groups === void 0) return result;
|
|
620
|
+
try {
|
|
621
|
+
const hideUnmatched = options?.hideUnmatched !== false;
|
|
622
|
+
const query2 = hideUnmatched ? (options?.query ?? "").trim().toLowerCase() : "";
|
|
623
|
+
const isRowVisible = options?.isRowVisible;
|
|
624
|
+
for (const group of groups.querySelectorAll('section[role="group"]')) {
|
|
625
|
+
const providerName = readGroupTitle(group);
|
|
626
|
+
const rows = group.querySelectorAll('[role="menuitemradio"]');
|
|
627
|
+
let visibleRows = 0;
|
|
628
|
+
for (const row of rows) {
|
|
629
|
+
const model = { row, providerName, modelName: readRowName(row) };
|
|
630
|
+
let visible = true;
|
|
631
|
+
if (typeof isRowVisible === "function") {
|
|
632
|
+
try {
|
|
633
|
+
visible = isRowVisible(model) !== false;
|
|
634
|
+
} catch {
|
|
635
|
+
visible = false;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
if (visible && hideUnmatched && !matchesQuery(model, query2)) visible = false;
|
|
639
|
+
row.style.display = visible ? "" : "none";
|
|
640
|
+
if (visible) visibleRows += 1;
|
|
641
|
+
else result.hidden += 1;
|
|
642
|
+
}
|
|
643
|
+
result.shown += visibleRows;
|
|
644
|
+
if (rows.length > 0 && visibleRows === 0) {
|
|
645
|
+
group.style.display = "none";
|
|
646
|
+
result.groupsHidden += 1;
|
|
647
|
+
} else {
|
|
648
|
+
group.style.display = "";
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
} catch {
|
|
652
|
+
}
|
|
653
|
+
return result;
|
|
654
|
+
}
|
|
655
|
+
function resetMenuFilter(roots) {
|
|
656
|
+
const groups = groupsOf(roots);
|
|
657
|
+
if (groups === void 0) return;
|
|
658
|
+
try {
|
|
659
|
+
for (const group of groups.querySelectorAll('section[role="group"]')) {
|
|
660
|
+
group.style.display = "";
|
|
661
|
+
for (const row of group.querySelectorAll('[role="menuitemradio"]')) {
|
|
662
|
+
row.style.display = "";
|
|
663
|
+
delete row.dataset["llmCtlHidden"];
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
} catch {
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
function containsForeignSearch(element) {
|
|
670
|
+
if (element.matches('[class*="dsh-model-search-container"]')) return true;
|
|
671
|
+
if (element.querySelector('[class*="dsh-model-search-container"]') !== null) return true;
|
|
672
|
+
if (element.matches("input") && classAttributeOf(element).includes("dsh-model-search")) return true;
|
|
673
|
+
for (const input of element.querySelectorAll("input")) {
|
|
674
|
+
if (classAttributeOf(input).includes("dsh-model-search")) return true;
|
|
675
|
+
}
|
|
676
|
+
return false;
|
|
677
|
+
}
|
|
678
|
+
function isBefore(element, other) {
|
|
679
|
+
if (element === other) return false;
|
|
680
|
+
if (typeof element.compareDocumentPosition !== "function") return true;
|
|
681
|
+
return (element.compareDocumentPosition(other) & DOCUMENT_POSITION_FOLLOWING) !== 0;
|
|
682
|
+
}
|
|
683
|
+
function hasForeignSearchWidget(roots) {
|
|
684
|
+
const groups = groupsOf(roots);
|
|
685
|
+
const menu = menuOf(roots);
|
|
686
|
+
if (groups === void 0 || menu === void 0) return false;
|
|
687
|
+
try {
|
|
688
|
+
const previous = groups.previousElementSibling;
|
|
689
|
+
if (previous !== null && containsForeignSearch(previous)) return true;
|
|
690
|
+
const scopes = [menu];
|
|
691
|
+
const parent = menu.parentElement;
|
|
692
|
+
if (parent !== null) scopes.push(parent);
|
|
693
|
+
const anchor = groups.querySelector('section[role="group"]') ?? groups;
|
|
694
|
+
for (const scope of scopes) {
|
|
695
|
+
const widgets = scope.querySelectorAll(
|
|
696
|
+
'[class*="dsh-model-search-container"], input[class*="dsh-model-search-input"]'
|
|
697
|
+
);
|
|
698
|
+
for (const widget of widgets) {
|
|
699
|
+
if (widget === anchor || widget.contains(anchor)) continue;
|
|
700
|
+
if (!isBefore(widget, anchor)) continue;
|
|
701
|
+
return true;
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
return false;
|
|
705
|
+
} catch {
|
|
706
|
+
return false;
|
|
707
|
+
}
|
|
708
|
+
}
|
|
709
|
+
function findEmptyState(groups, menu) {
|
|
710
|
+
const parent = groups?.parentElement ?? menu?.parentElement ?? null;
|
|
711
|
+
const local = parent?.querySelector(`#${EMPTY_STATE_ID}`);
|
|
712
|
+
if (local !== void 0 && local !== null) return local;
|
|
713
|
+
const global = documentOf(groups, menu)?.getElementById(EMPTY_STATE_ID);
|
|
714
|
+
return global ?? void 0;
|
|
715
|
+
}
|
|
716
|
+
function ensureEmptyState(roots, text, actionLabel, onAction) {
|
|
717
|
+
const groups = groupsOf(roots);
|
|
718
|
+
const menu = menuOf(roots);
|
|
719
|
+
const existing = findEmptyState(groups, menu);
|
|
720
|
+
const owner = documentOf(existing, groups, menu);
|
|
721
|
+
if (owner === void 0) return roots?.menu ?? roots?.groups;
|
|
722
|
+
let element = existing;
|
|
723
|
+
try {
|
|
724
|
+
element = element ?? owner.createElement("div");
|
|
725
|
+
element.id = EMPTY_STATE_ID;
|
|
726
|
+
element.className = "dsh-llm-ctl-empty";
|
|
727
|
+
const message = owner.createElement("span");
|
|
728
|
+
message.className = "dsh-llm-ctl-empty-text";
|
|
729
|
+
message.textContent = text;
|
|
730
|
+
const action = owner.createElement("button");
|
|
731
|
+
action.type = "button";
|
|
732
|
+
action.className = "dsh-llm-ctl-empty-action";
|
|
733
|
+
action.textContent = actionLabel;
|
|
734
|
+
action.addEventListener("click", (event) => {
|
|
735
|
+
event.preventDefault();
|
|
736
|
+
try {
|
|
737
|
+
onAction();
|
|
738
|
+
} catch {
|
|
739
|
+
}
|
|
740
|
+
});
|
|
741
|
+
element.replaceChildren(message, action);
|
|
742
|
+
const parent = groups?.parentElement ?? menu?.parentElement ?? null;
|
|
743
|
+
if (parent !== null) {
|
|
744
|
+
if (groups !== void 0 && groups.parentElement === parent) parent.insertBefore(element, groups);
|
|
745
|
+
else if (element.parentElement !== parent) parent.appendChild(element);
|
|
746
|
+
}
|
|
747
|
+
} catch {
|
|
748
|
+
}
|
|
749
|
+
return element ?? (roots?.menu ?? roots?.groups);
|
|
750
|
+
}
|
|
751
|
+
function removeEmptyState(roots) {
|
|
752
|
+
try {
|
|
753
|
+
const groups = groupsOf(roots);
|
|
754
|
+
const menu = menuOf(roots);
|
|
755
|
+
findEmptyState(groups, menu)?.remove();
|
|
756
|
+
} catch {
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
// src/menu-visibility.ts
|
|
761
|
+
var HIDDEN_MARKER = "llmCtlHidden";
|
|
762
|
+
function groupTitleOf(group) {
|
|
763
|
+
try {
|
|
764
|
+
const titled = group.querySelector("div[id]");
|
|
765
|
+
if (titled !== null) return (titled.textContent ?? "").trim();
|
|
766
|
+
const labelledBy = group.getAttribute("aria-labelledby");
|
|
767
|
+
if (labelledBy !== null && labelledBy.length > 0) {
|
|
768
|
+
const target = group.ownerDocument?.getElementById(labelledBy);
|
|
769
|
+
if (target !== null && target !== void 0) return (target.textContent ?? "").trim();
|
|
770
|
+
}
|
|
771
|
+
return (group.getAttribute("aria-label") ?? "").trim();
|
|
772
|
+
} catch {
|
|
773
|
+
return "";
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
function rowNameOf(row) {
|
|
777
|
+
try {
|
|
778
|
+
const title = row.getAttribute("title");
|
|
779
|
+
if (title !== null && title.trim().length > 0) return title.trim();
|
|
780
|
+
const named = row.querySelector('[class*="modelName"]');
|
|
781
|
+
if (named !== null && (named.textContent ?? "").trim().length > 0) return (named.textContent ?? "").trim();
|
|
782
|
+
return (row.textContent ?? "").trim();
|
|
783
|
+
} catch {
|
|
784
|
+
return "";
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
function applyVisibilityOnly(roots, isRowVisible) {
|
|
788
|
+
const result = { hidden: 0, restored: 0 };
|
|
789
|
+
try {
|
|
790
|
+
for (const group of roots.groups.querySelectorAll('section[role="group"]')) {
|
|
791
|
+
const providerName = groupTitleOf(group);
|
|
792
|
+
for (const row of group.querySelectorAll('[role="menuitemradio"]')) {
|
|
793
|
+
const model = { row, providerName, modelName: rowNameOf(row) };
|
|
794
|
+
let visible = true;
|
|
795
|
+
try {
|
|
796
|
+
visible = isRowVisible(model) !== false;
|
|
797
|
+
} catch {
|
|
798
|
+
visible = false;
|
|
799
|
+
}
|
|
800
|
+
if (!visible) {
|
|
801
|
+
if (row.style.display !== "none") row.style.display = "none";
|
|
802
|
+
if (row.dataset[HIDDEN_MARKER] !== "1") {
|
|
803
|
+
row.dataset[HIDDEN_MARKER] = "1";
|
|
804
|
+
result.hidden += 1;
|
|
805
|
+
}
|
|
806
|
+
} else if (row.dataset[HIDDEN_MARKER] === "1") {
|
|
807
|
+
row.style.display = "";
|
|
808
|
+
delete row.dataset[HIDDEN_MARKER];
|
|
809
|
+
result.restored += 1;
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
} catch {
|
|
814
|
+
}
|
|
815
|
+
return result;
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
// src/discover-ui.ts
|
|
819
|
+
var import_react2 = __toESM(require("react"), 1);
|
|
820
|
+
function buildDiscoveredRows(input) {
|
|
821
|
+
const config = { hiddenPatterns: input.patterns };
|
|
822
|
+
const rows = [];
|
|
823
|
+
for (const entry of input.discovered) {
|
|
824
|
+
if (entry.id === "") continue;
|
|
825
|
+
rows.push({
|
|
826
|
+
id: entry.id,
|
|
827
|
+
name: entry.name ?? entry.id,
|
|
828
|
+
isNew: !input.advertisedIds.includes(entry.id),
|
|
829
|
+
visible: isModelVisible(input.provider, entry.id, input.settings, config)
|
|
830
|
+
});
|
|
831
|
+
}
|
|
832
|
+
return rows;
|
|
833
|
+
}
|
|
834
|
+
var rowStyle2 = { display: "flex", alignItems: "center", gap: 8, padding: "2px 0" };
|
|
835
|
+
var nameStyle2 = { flex: 1, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap" };
|
|
836
|
+
var buttonStyle2 = {
|
|
837
|
+
border: "1px solid var(--dsw-alias-border-inverted, rgba(127,127,127,.35))",
|
|
838
|
+
background: "transparent",
|
|
839
|
+
color: "inherit",
|
|
840
|
+
borderRadius: 6,
|
|
841
|
+
padding: "1px 8px",
|
|
842
|
+
fontSize: 11,
|
|
843
|
+
cursor: "pointer"
|
|
844
|
+
};
|
|
845
|
+
var badgeStyle = {
|
|
846
|
+
border: "1px solid var(--dsw-alias-border-inverted, rgba(127,127,127,.35))",
|
|
847
|
+
borderRadius: 4,
|
|
848
|
+
padding: "0 4px",
|
|
849
|
+
fontSize: 10
|
|
850
|
+
};
|
|
851
|
+
var errorStyle = { color: "#f31260", padding: "2px 0" };
|
|
852
|
+
function RefreshModelsButton(props) {
|
|
853
|
+
const disabled = props.pending || props.disabled === true;
|
|
854
|
+
return import_react2.default.createElement(
|
|
855
|
+
"button",
|
|
856
|
+
{
|
|
857
|
+
type: "button",
|
|
858
|
+
style: buttonStyle2,
|
|
859
|
+
disabled,
|
|
860
|
+
onClick: () => props.onRefresh()
|
|
861
|
+
},
|
|
862
|
+
props.pending ? "\u5237\u65B0\u4E2D\u2026" : "\u5237\u65B0\u6E05\u5355"
|
|
863
|
+
);
|
|
864
|
+
}
|
|
865
|
+
function DiscoveredModelsList(props) {
|
|
866
|
+
const [collapsed, toggleCollapsed] = useAutoCollapse(props.rows.length);
|
|
867
|
+
if (props.rows.length === 0 && !props.pending && !props.failed) return null;
|
|
868
|
+
const failed = props.failed ?? "";
|
|
869
|
+
return import_react2.default.createElement(
|
|
870
|
+
"section",
|
|
871
|
+
{ style: { marginTop: 8, fontSize: 12 } },
|
|
872
|
+
import_react2.default.createElement(
|
|
873
|
+
"div",
|
|
874
|
+
{ style: rowStyle2 },
|
|
875
|
+
CollapseToggle({ collapsed, count: props.rows.length, label: "\u4E0A\u6E38\u53D1\u73B0", onToggle: toggleCollapsed }),
|
|
876
|
+
import_react2.default.createElement(RefreshModelsButton, { pending: props.pending, onRefresh: props.onRefresh })
|
|
877
|
+
),
|
|
878
|
+
failed.length > 0 ? import_react2.default.createElement("div", { style: errorStyle }, failed) : null,
|
|
879
|
+
collapsed ? null : props.rows.map(
|
|
880
|
+
(row) => import_react2.default.createElement(
|
|
881
|
+
"div",
|
|
882
|
+
{ key: row.id, style: { ...rowStyle2, opacity: row.visible ? 1 : 0.5 } },
|
|
883
|
+
import_react2.default.createElement("span", { style: nameStyle2 }, row.name),
|
|
884
|
+
row.isNew ? import_react2.default.createElement("span", { style: badgeStyle }, "\u65B0") : null,
|
|
885
|
+
import_react2.default.createElement(
|
|
886
|
+
"button",
|
|
887
|
+
{
|
|
888
|
+
type: "button",
|
|
889
|
+
style: buttonStyle2,
|
|
890
|
+
title: row.visible ? "\u9690\u85CF" : "\u663E\u793A",
|
|
891
|
+
"aria-pressed": !row.visible,
|
|
892
|
+
onClick: () => props.onToggle(props.provider, row.id, !row.visible)
|
|
893
|
+
},
|
|
894
|
+
row.visible ? "\u{1F441}" : "\u{1F6AB}"
|
|
895
|
+
)
|
|
896
|
+
)
|
|
897
|
+
)
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// src/queue-dock.ts
|
|
902
|
+
var import_react3 = __toESM(require("react"), 1);
|
|
903
|
+
var import_react_dom = require("react-dom");
|
|
904
|
+
function formatMs(ms) {
|
|
905
|
+
if (ms < 1e3) return Math.max(0, Math.round(ms)) + "ms";
|
|
906
|
+
const seconds = ms / 1e3;
|
|
907
|
+
if (seconds < 60) return (seconds < 10 ? seconds.toFixed(1) : String(Math.round(seconds))) + "s";
|
|
908
|
+
return Math.floor(seconds / 60) + "m" + Math.round(seconds % 60) + "s";
|
|
909
|
+
}
|
|
910
|
+
function buildQueueDockView(queue) {
|
|
911
|
+
const cooling = queue.lanes.filter((lane) => lane.cooldownRemainingMs > 0);
|
|
912
|
+
if (queue.waiters.length === 0 && cooling.length === 0) return void 0;
|
|
913
|
+
return { waiters: [...queue.waiters], cooling };
|
|
914
|
+
}
|
|
915
|
+
var DOCK_CANCEL_CLASS = "dsh-llm-ctl-dock-cancel";
|
|
916
|
+
var DOCK_PILL_CLASS = "dsh-llm-ctl-dock-pill";
|
|
917
|
+
var panelStyle = {
|
|
918
|
+
display: "flex",
|
|
919
|
+
alignItems: "center",
|
|
920
|
+
justifyContent: "center",
|
|
921
|
+
flexWrap: "wrap",
|
|
922
|
+
gap: "4px 12px",
|
|
923
|
+
boxSizing: "border-box",
|
|
924
|
+
width: "100%",
|
|
925
|
+
maxWidth: "var(--dsh-chat-content-width)",
|
|
926
|
+
margin: "0 auto",
|
|
927
|
+
padding: "4px calc(var(--dsh-composer-side-clearance, 16px) + 16px) 0",
|
|
928
|
+
fontSize: "var(--dsh-content-font-size-secondary, 13px)",
|
|
929
|
+
lineHeight: "calc(20px + var(--dsh-content-font-delta-secondary, 0px))",
|
|
930
|
+
color: "var(--dsw-alias-label-tertiary, #93a1c0)"
|
|
931
|
+
};
|
|
932
|
+
var anchorStyle = { display: "inline-flex", minWidth: 0 };
|
|
933
|
+
var triggerStyle = {
|
|
934
|
+
boxSizing: "border-box",
|
|
935
|
+
display: "inline-flex",
|
|
936
|
+
alignItems: "center",
|
|
937
|
+
gap: 6,
|
|
938
|
+
maxWidth: "100%",
|
|
939
|
+
padding: "1px 8px",
|
|
940
|
+
border: "none",
|
|
941
|
+
borderRadius: 24,
|
|
942
|
+
background: "transparent",
|
|
943
|
+
color: "inherit",
|
|
944
|
+
font: "inherit",
|
|
945
|
+
fontVariantNumeric: "tabular-nums",
|
|
946
|
+
lineHeight: "inherit",
|
|
947
|
+
whiteSpace: "nowrap",
|
|
948
|
+
cursor: "pointer"
|
|
949
|
+
};
|
|
950
|
+
var labelStyle = { minWidth: 0, overflow: "hidden", textOverflow: "ellipsis" };
|
|
951
|
+
var glyphStyle = { flex: "none", width: 14, height: 14 };
|
|
952
|
+
var dialogPanelStyle = {
|
|
953
|
+
position: "fixed",
|
|
954
|
+
zIndex: 1100,
|
|
955
|
+
boxSizing: "border-box",
|
|
956
|
+
transform: "translateX(-50%)",
|
|
957
|
+
width: "max-content",
|
|
958
|
+
minWidth: "min(300px, 100vw - 24px)",
|
|
959
|
+
maxWidth: "min(440px, 100vw - 24px)",
|
|
960
|
+
background: "var(--dsw-specific-menu)",
|
|
961
|
+
boxShadow: "var(--dsw-elevation-prominent)",
|
|
962
|
+
color: "var(--dsw-alias-label-secondary)",
|
|
963
|
+
border: 0,
|
|
964
|
+
borderRadius: 12,
|
|
965
|
+
padding: 16,
|
|
966
|
+
fontSize: 12,
|
|
967
|
+
lineHeight: "18px"
|
|
968
|
+
};
|
|
969
|
+
var dialogTitleStyle = {
|
|
970
|
+
display: "flex",
|
|
971
|
+
alignItems: "center",
|
|
972
|
+
gap: 6,
|
|
973
|
+
color: "var(--dsw-alias-label-primary)",
|
|
974
|
+
marginBottom: 8,
|
|
975
|
+
fontWeight: 500
|
|
976
|
+
};
|
|
977
|
+
var dialogRuleStyle = { borderTop: ".5px solid var(--dsw-alias-border-l2)", marginBottom: 10 };
|
|
978
|
+
var listStyle = {
|
|
979
|
+
listStyle: "none",
|
|
980
|
+
margin: 0,
|
|
981
|
+
padding: 0,
|
|
982
|
+
display: "flex",
|
|
983
|
+
flexDirection: "column",
|
|
984
|
+
gap: 6,
|
|
985
|
+
maxHeight: 240,
|
|
986
|
+
overflowY: "auto"
|
|
987
|
+
};
|
|
988
|
+
var rowStyle3 = { display: "flex", alignItems: "center", gap: 8, minWidth: 0 };
|
|
989
|
+
var rowBadgeStyle = { flex: "none", color: "var(--dsw-alias-label-tertiary)", fontVariantNumeric: "tabular-nums" };
|
|
990
|
+
var rowProviderStyle = {
|
|
991
|
+
flex: 1,
|
|
992
|
+
minWidth: 0,
|
|
993
|
+
overflow: "hidden",
|
|
994
|
+
textOverflow: "ellipsis",
|
|
995
|
+
whiteSpace: "nowrap",
|
|
996
|
+
color: "var(--dsw-alias-label-primary)"
|
|
997
|
+
};
|
|
998
|
+
var rowMetaStyle = { flex: "none", color: "var(--dsw-alias-label-tertiary)", fontVariantNumeric: "tabular-nums" };
|
|
999
|
+
var cancelButtonStyle = {
|
|
1000
|
+
flex: "none",
|
|
1001
|
+
border: "none",
|
|
1002
|
+
background: "transparent",
|
|
1003
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
1004
|
+
borderRadius: 6,
|
|
1005
|
+
padding: "2px 8px",
|
|
1006
|
+
font: "inherit",
|
|
1007
|
+
cursor: "pointer"
|
|
1008
|
+
};
|
|
1009
|
+
function QueueGlyph() {
|
|
1010
|
+
return import_react3.default.createElement(
|
|
1011
|
+
"svg",
|
|
1012
|
+
{ width: 14, height: 14, viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, style: glyphStyle },
|
|
1013
|
+
import_react3.default.createElement("circle", { cx: 8, cy: 8, r: 6, stroke: "currentColor", strokeWidth: 1.25 }),
|
|
1014
|
+
import_react3.default.createElement("path", {
|
|
1015
|
+
d: "M8 4.6V8.4L10.6 10",
|
|
1016
|
+
stroke: "currentColor",
|
|
1017
|
+
strokeWidth: 1.25,
|
|
1018
|
+
strokeLinecap: "round",
|
|
1019
|
+
strokeLinejoin: "round"
|
|
1020
|
+
})
|
|
1021
|
+
);
|
|
1022
|
+
}
|
|
1023
|
+
function CoolingGlyph() {
|
|
1024
|
+
return import_react3.default.createElement(
|
|
1025
|
+
"svg",
|
|
1026
|
+
{ width: 14, height: 14, viewBox: "0 0 16 16", fill: "none", "aria-hidden": true, style: glyphStyle },
|
|
1027
|
+
import_react3.default.createElement("path", {
|
|
1028
|
+
d: "M8 1.5V14.5M2.4 4.75L13.6 11.25M13.6 4.75L2.4 11.25",
|
|
1029
|
+
stroke: "currentColor",
|
|
1030
|
+
strokeWidth: 1.25,
|
|
1031
|
+
strokeLinecap: "round"
|
|
1032
|
+
})
|
|
1033
|
+
);
|
|
1034
|
+
}
|
|
1035
|
+
function DockPill(props) {
|
|
1036
|
+
const { icon, label, title, children } = props;
|
|
1037
|
+
const [open, setOpen] = import_react3.default.useState(false);
|
|
1038
|
+
const [pos, setPos] = import_react3.default.useState(void 0);
|
|
1039
|
+
const rootRef = import_react3.default.useRef(null);
|
|
1040
|
+
const panelRef = import_react3.default.useRef(null);
|
|
1041
|
+
import_react3.default.useEffect(() => {
|
|
1042
|
+
if (!open) {
|
|
1043
|
+
setPos(void 0);
|
|
1044
|
+
return;
|
|
1045
|
+
}
|
|
1046
|
+
const anchor = rootRef.current;
|
|
1047
|
+
if (anchor === null) return;
|
|
1048
|
+
const rect = anchor.getBoundingClientRect();
|
|
1049
|
+
setPos({ left: rect.left + rect.width / 2, bottom: window.innerHeight - rect.top + 8 });
|
|
1050
|
+
}, [open]);
|
|
1051
|
+
import_react3.default.useEffect(() => {
|
|
1052
|
+
if (!open) return;
|
|
1053
|
+
const onPointerDown = (event) => {
|
|
1054
|
+
const target = event.target;
|
|
1055
|
+
if (target !== null) {
|
|
1056
|
+
if (rootRef.current !== null && rootRef.current.contains(target)) return;
|
|
1057
|
+
if (panelRef.current !== null && panelRef.current.contains(target)) return;
|
|
1058
|
+
}
|
|
1059
|
+
setOpen(false);
|
|
1060
|
+
};
|
|
1061
|
+
const onKeyDown = (event) => {
|
|
1062
|
+
if (event.key === "Escape") setOpen(false);
|
|
1063
|
+
};
|
|
1064
|
+
document.addEventListener("mousedown", onPointerDown, true);
|
|
1065
|
+
document.addEventListener("keydown", onKeyDown);
|
|
1066
|
+
return () => {
|
|
1067
|
+
document.removeEventListener("mousedown", onPointerDown, true);
|
|
1068
|
+
document.removeEventListener("keydown", onKeyDown);
|
|
1069
|
+
};
|
|
1070
|
+
}, [open]);
|
|
1071
|
+
const trigger = import_react3.default.createElement(
|
|
1072
|
+
"span",
|
|
1073
|
+
{ ref: rootRef, style: anchorStyle },
|
|
1074
|
+
import_react3.default.createElement(
|
|
1075
|
+
"button",
|
|
1076
|
+
{
|
|
1077
|
+
type: "button",
|
|
1078
|
+
className: DOCK_PILL_CLASS,
|
|
1079
|
+
style: triggerStyle,
|
|
1080
|
+
"aria-haspopup": "dialog",
|
|
1081
|
+
"aria-expanded": open,
|
|
1082
|
+
"aria-label": label,
|
|
1083
|
+
title: label,
|
|
1084
|
+
onClick: () => setOpen(!open)
|
|
1085
|
+
},
|
|
1086
|
+
icon,
|
|
1087
|
+
import_react3.default.createElement("span", { style: labelStyle }, label)
|
|
1088
|
+
)
|
|
1089
|
+
);
|
|
1090
|
+
const dialog = open ? (0, import_react_dom.createPortal)(
|
|
1091
|
+
import_react3.default.createElement(
|
|
1092
|
+
"div",
|
|
1093
|
+
{
|
|
1094
|
+
ref: panelRef,
|
|
1095
|
+
className: "dsh-llm-ctl-queue-dialog",
|
|
1096
|
+
role: "dialog",
|
|
1097
|
+
"aria-label": title,
|
|
1098
|
+
style: pos === void 0 ? { ...dialogPanelStyle, visibility: "hidden" } : { ...dialogPanelStyle, ...pos }
|
|
1099
|
+
},
|
|
1100
|
+
import_react3.default.createElement("div", { style: dialogTitleStyle }, icon, title),
|
|
1101
|
+
import_react3.default.createElement("div", { style: dialogRuleStyle, "aria-hidden": true }),
|
|
1102
|
+
children
|
|
1103
|
+
),
|
|
1104
|
+
document.body
|
|
1105
|
+
) : null;
|
|
1106
|
+
return import_react3.default.createElement(import_react3.default.Fragment, null, trigger, dialog);
|
|
1107
|
+
}
|
|
1108
|
+
function QueuePill(props) {
|
|
1109
|
+
const { waiters, onCancel } = props;
|
|
1110
|
+
const first = waiters[0];
|
|
1111
|
+
const label = first === void 0 ? "\u6392\u961F " + waiters.length : "\u6392\u961F " + waiters.length + " \xB7 ~" + formatMs(first.etaMs);
|
|
1112
|
+
return import_react3.default.createElement(
|
|
1113
|
+
DockPill,
|
|
1114
|
+
{ icon: QueueGlyph(), label, title: "\u961F\u5217\u72B6\u51B5" },
|
|
1115
|
+
import_react3.default.createElement(QueueDetails, { waiters, onCancel })
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
function QueueDetails(props) {
|
|
1119
|
+
const { waiters, onCancel } = props;
|
|
1120
|
+
return import_react3.default.createElement(
|
|
1121
|
+
"ul",
|
|
1122
|
+
{ style: listStyle },
|
|
1123
|
+
waiters.map(
|
|
1124
|
+
(waiter) => import_react3.default.createElement(
|
|
1125
|
+
"li",
|
|
1126
|
+
{ key: waiter.queueId, style: rowStyle3 },
|
|
1127
|
+
import_react3.default.createElement("span", { style: rowBadgeStyle }, "#" + waiter.position),
|
|
1128
|
+
import_react3.default.createElement("span", { style: rowProviderStyle }, waiter.provider),
|
|
1129
|
+
import_react3.default.createElement(
|
|
1130
|
+
"span",
|
|
1131
|
+
{ style: rowMetaStyle },
|
|
1132
|
+
(waiter.origin === "background" ? "\u540E\u53F0" : "\u5BF9\u8BDD") + " \xB7 " + formatMs(waiter.etaMs)
|
|
1133
|
+
),
|
|
1134
|
+
import_react3.default.createElement(
|
|
1135
|
+
"button",
|
|
1136
|
+
{
|
|
1137
|
+
type: "button",
|
|
1138
|
+
className: DOCK_CANCEL_CLASS,
|
|
1139
|
+
style: cancelButtonStyle,
|
|
1140
|
+
onClick: () => onCancel(waiter.queueId)
|
|
1141
|
+
},
|
|
1142
|
+
"\u53D6\u6D88"
|
|
1143
|
+
)
|
|
1144
|
+
)
|
|
1145
|
+
)
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
function CoolingPill(props) {
|
|
1149
|
+
const { lanes } = props;
|
|
1150
|
+
const longest = lanes.reduce((max, lane) => Math.max(max, lane.cooldownRemainingMs), 0);
|
|
1151
|
+
return import_react3.default.createElement(
|
|
1152
|
+
DockPill,
|
|
1153
|
+
{ icon: CoolingGlyph(), label: "\u51B7\u5374 " + lanes.length + " \xB7 \u6700\u957F " + formatMs(longest), title: "\u51B7\u5374\u4E2D\u7684 provider" },
|
|
1154
|
+
import_react3.default.createElement(CoolingDetails, { lanes })
|
|
1155
|
+
);
|
|
1156
|
+
}
|
|
1157
|
+
function CoolingDetails(props) {
|
|
1158
|
+
const { lanes } = props;
|
|
1159
|
+
return import_react3.default.createElement(
|
|
1160
|
+
"ul",
|
|
1161
|
+
{ style: listStyle },
|
|
1162
|
+
lanes.map(
|
|
1163
|
+
(lane) => import_react3.default.createElement(
|
|
1164
|
+
"li",
|
|
1165
|
+
{ key: lane.provider, style: rowStyle3 },
|
|
1166
|
+
import_react3.default.createElement("span", { style: rowProviderStyle }, lane.provider),
|
|
1167
|
+
import_react3.default.createElement("span", { style: rowMetaStyle }, formatMs(lane.cooldownRemainingMs))
|
|
1168
|
+
)
|
|
1169
|
+
)
|
|
1170
|
+
);
|
|
1171
|
+
}
|
|
1172
|
+
function QueueDockPanel(props) {
|
|
1173
|
+
const { view, onCancel } = props;
|
|
1174
|
+
const children = [];
|
|
1175
|
+
if (view.waiters.length > 0) {
|
|
1176
|
+
children.push(import_react3.default.createElement(QueuePill, { key: "queue", waiters: view.waiters, onCancel }));
|
|
1177
|
+
}
|
|
1178
|
+
if (view.cooling.length > 0) {
|
|
1179
|
+
children.push(import_react3.default.createElement(CoolingPill, { key: "cooling", lanes: view.cooling }));
|
|
1180
|
+
}
|
|
1181
|
+
return import_react3.default.createElement("div", { style: panelStyle, role: "status", "aria-live": "polite" }, children);
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
// src/client-plugin.ts
|
|
1185
|
+
var inject = ["slots", "remote", "remote.session"];
|
|
1186
|
+
var STATE_URL = "/api/llm-ctl/state";
|
|
1187
|
+
var CANCEL_URL = "/api/llm-ctl/cancel";
|
|
1188
|
+
var DISCOVER_URL = "/api/llm-ctl/discover";
|
|
1189
|
+
var VISIBILITY_URL = "/api/llm-ctl/visibility";
|
|
1190
|
+
var VISIBILITY_RESET_URL = "/api/llm-ctl/visibility/reset";
|
|
1191
|
+
var QUEUE_URL = "/api/llm-ctl/queue";
|
|
1192
|
+
var QUEUE_RESET_URL = "/api/llm-ctl/queue/reset";
|
|
1193
|
+
var POLL_MS = 1e3;
|
|
1194
|
+
var HIDDEN_POLL_MS = 5e3;
|
|
1195
|
+
var MAX_POLL_MS = 3e4;
|
|
1196
|
+
var CATALOG_TTL_MS = 3e4;
|
|
1197
|
+
var SEARCH_ID = "dsh-llm-ctl-search";
|
|
1198
|
+
var ctxRef;
|
|
1199
|
+
var latestState;
|
|
1200
|
+
var catalog;
|
|
1201
|
+
var catalogAt = 0;
|
|
1202
|
+
var catalogRequest;
|
|
1203
|
+
var attached;
|
|
1204
|
+
var searchInput;
|
|
1205
|
+
var query = "";
|
|
1206
|
+
var timer;
|
|
1207
|
+
var fetchFailures = 0;
|
|
1208
|
+
var lastNotifiedSignature;
|
|
1209
|
+
var observer;
|
|
1210
|
+
var syncScheduled = false;
|
|
1211
|
+
var subscribers = /* @__PURE__ */ new Set();
|
|
1212
|
+
function notify() {
|
|
1213
|
+
for (const listener of [...subscribers]) listener();
|
|
1214
|
+
}
|
|
1215
|
+
function useCtlState() {
|
|
1216
|
+
const [, force] = import_react4.default.useState(0);
|
|
1217
|
+
import_react4.default.useEffect(() => {
|
|
1218
|
+
const listener = () => force((value) => value + 1);
|
|
1219
|
+
subscribers.add(listener);
|
|
1220
|
+
return () => {
|
|
1221
|
+
subscribers.delete(listener);
|
|
1222
|
+
};
|
|
1223
|
+
}, []);
|
|
1224
|
+
return { state: latestState, catalog };
|
|
1225
|
+
}
|
|
1226
|
+
function ensureMenuStyles() {
|
|
1227
|
+
if (document.getElementById(`${SEARCH_ID}-style`) !== null) return;
|
|
1228
|
+
const style = document.createElement("style");
|
|
1229
|
+
style.id = `${SEARCH_ID}-style`;
|
|
1230
|
+
style.textContent = `
|
|
1231
|
+
#${SEARCH_ID} {
|
|
1232
|
+
width: calc(100% - 8px);
|
|
1233
|
+
margin: 4px;
|
|
1234
|
+
height: 28px;
|
|
1235
|
+
padding: 0 8px;
|
|
1236
|
+
border: 1px solid var(--dsw-alias-border-inverted, rgba(127, 127, 127, 0.35));
|
|
1237
|
+
border-radius: 6px;
|
|
1238
|
+
background: transparent;
|
|
1239
|
+
color: inherit;
|
|
1240
|
+
font-size: 13px;
|
|
1241
|
+
outline: none;
|
|
1242
|
+
}
|
|
1243
|
+
.dsh-llm-ctl-empty {
|
|
1244
|
+
padding: 12px 8px;
|
|
1245
|
+
display: flex;
|
|
1246
|
+
flex-direction: column;
|
|
1247
|
+
gap: 8px;
|
|
1248
|
+
align-items: flex-start;
|
|
1249
|
+
font-size: 12px;
|
|
1250
|
+
color: var(--dsw-alias-label-secondary, #93a1c0);
|
|
1251
|
+
}
|
|
1252
|
+
.dsh-llm-ctl-empty-action {
|
|
1253
|
+
border: 1px solid var(--dsw-alias-border-inverted, rgba(127, 127, 127, 0.35));
|
|
1254
|
+
background: transparent;
|
|
1255
|
+
color: inherit;
|
|
1256
|
+
border-radius: 6px;
|
|
1257
|
+
padding: 2px 8px;
|
|
1258
|
+
font-size: 12px;
|
|
1259
|
+
cursor: pointer;
|
|
1260
|
+
}
|
|
1261
|
+
`;
|
|
1262
|
+
document.head.appendChild(style);
|
|
1263
|
+
}
|
|
1264
|
+
function ensureDockStyles() {
|
|
1265
|
+
if (document.getElementById("dsh-llm-ctl-dock-style") !== null) return;
|
|
1266
|
+
const style = document.createElement("style");
|
|
1267
|
+
style.id = "dsh-llm-ctl-dock-style";
|
|
1268
|
+
style.textContent = [
|
|
1269
|
+
".dsh-llm-ctl-dock-pill { background: transparent; border: none; cursor: pointer; }",
|
|
1270
|
+
'.dsh-llm-ctl-dock-pill:hover, .dsh-llm-ctl-dock-pill[aria-expanded="true"] { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-secondary); }',
|
|
1271
|
+
".dsh-llm-ctl-dock-pill:focus-visible { outline: 2px solid var(--dsw-alias-label-tertiary); outline-offset: -2px; }",
|
|
1272
|
+
".dsh-llm-ctl-dock-cancel:hover { background: var(--dsw-alias-interactive-bg-hover); color: var(--dsw-alias-label-secondary); }",
|
|
1273
|
+
".dsh-llm-ctl-queue-dialog { cursor: default; }"
|
|
1274
|
+
].join("\n");
|
|
1275
|
+
document.head.appendChild(style);
|
|
1276
|
+
}
|
|
1277
|
+
async function postJson(url, body) {
|
|
1278
|
+
const response = await fetch(url, {
|
|
1279
|
+
method: "POST",
|
|
1280
|
+
headers: { "content-type": "application/json" },
|
|
1281
|
+
credentials: "same-origin",
|
|
1282
|
+
body: JSON.stringify(body)
|
|
1283
|
+
});
|
|
1284
|
+
let parsed;
|
|
1285
|
+
try {
|
|
1286
|
+
parsed = await response.json();
|
|
1287
|
+
} catch {
|
|
1288
|
+
parsed = void 0;
|
|
1289
|
+
}
|
|
1290
|
+
return { ok: response.ok, status: response.status, body: parsed };
|
|
1291
|
+
}
|
|
1292
|
+
async function fetchState() {
|
|
1293
|
+
const response = await fetch(STATE_URL, { headers: { accept: "application/json" }, credentials: "same-origin" });
|
|
1294
|
+
if (!response.ok) throw new Error(`state ${response.status}`);
|
|
1295
|
+
return await response.json();
|
|
1296
|
+
}
|
|
1297
|
+
function refreshCatalog() {
|
|
1298
|
+
if (catalogRequest !== void 0) return catalogRequest;
|
|
1299
|
+
const client = ctxRef;
|
|
1300
|
+
if (client === void 0) return Promise.resolve();
|
|
1301
|
+
catalogRequest = (async () => {
|
|
1302
|
+
try {
|
|
1303
|
+
const result = await client.remote.session.modelCatalog();
|
|
1304
|
+
if (result.ok && result.value !== void 0) {
|
|
1305
|
+
const fresh = result.value;
|
|
1306
|
+
catalogAt = Date.now();
|
|
1307
|
+
if (catalog === void 0 || JSON.stringify(fresh) !== JSON.stringify(catalog)) {
|
|
1308
|
+
catalog = fresh;
|
|
1309
|
+
notify();
|
|
1310
|
+
}
|
|
1311
|
+
}
|
|
1312
|
+
} catch (error) {
|
|
1313
|
+
client.logger?.warn("llm-ctl: model catalog failed", error);
|
|
1314
|
+
} finally {
|
|
1315
|
+
catalogRequest = void 0;
|
|
1316
|
+
}
|
|
1317
|
+
})();
|
|
1318
|
+
return catalogRequest;
|
|
1319
|
+
}
|
|
1320
|
+
function visibilitySettings() {
|
|
1321
|
+
return latestState?.visibility.settings ?? { providers: {}, models: {} };
|
|
1322
|
+
}
|
|
1323
|
+
function hiddenPatterns() {
|
|
1324
|
+
return latestState?.visibility.patterns ?? [];
|
|
1325
|
+
}
|
|
1326
|
+
function nameIndex() {
|
|
1327
|
+
const index = /* @__PURE__ */ new Map();
|
|
1328
|
+
for (const group of catalog?.groups ?? []) {
|
|
1329
|
+
const models = /* @__PURE__ */ new Map();
|
|
1330
|
+
for (const model of group.models) models.set(model.name, { provider: group.id, model: model.id });
|
|
1331
|
+
index.set(group.name, models);
|
|
1332
|
+
}
|
|
1333
|
+
return index;
|
|
1334
|
+
}
|
|
1335
|
+
function providerViews() {
|
|
1336
|
+
const settings = visibilitySettings();
|
|
1337
|
+
const patterns = hiddenPatterns();
|
|
1338
|
+
const entries = (catalog?.groups ?? []).map((group) => ({
|
|
1339
|
+
provider: group.id,
|
|
1340
|
+
displayName: group.name,
|
|
1341
|
+
models: group.models.map((model) => ({ model: model.id, name: model.name }))
|
|
1342
|
+
}));
|
|
1343
|
+
const seen = new Set(entries.map((entry) => entry.provider));
|
|
1344
|
+
for (const declared of latestState?.visibility.configurableProviders ?? []) {
|
|
1345
|
+
if (seen.has(declared.provider)) continue;
|
|
1346
|
+
seen.add(declared.provider);
|
|
1347
|
+
entries.push({ provider: declared.provider, displayName: declared.displayName, models: [] });
|
|
1348
|
+
}
|
|
1349
|
+
return buildProviderViews({ providers: entries, settings, patterns });
|
|
1350
|
+
}
|
|
1351
|
+
var discoveries = /* @__PURE__ */ new Map();
|
|
1352
|
+
async function refreshDiscovery(provider) {
|
|
1353
|
+
const current = discoveries.get(provider);
|
|
1354
|
+
if (current !== void 0 && current.pending) return;
|
|
1355
|
+
discoveries.set(provider, { rows: current?.rows ?? [], pending: true, failed: void 0 });
|
|
1356
|
+
notify();
|
|
1357
|
+
try {
|
|
1358
|
+
const response = await postJson(DISCOVER_URL, { provider });
|
|
1359
|
+
const body = response.body;
|
|
1360
|
+
if (!response.ok) throw new Error("discover " + response.status);
|
|
1361
|
+
if (body !== void 0 && typeof body.error === "string" && body.error.length > 0) {
|
|
1362
|
+
discoveries.set(provider, { rows: current?.rows ?? [], pending: false, failed: body.error });
|
|
1363
|
+
} else {
|
|
1364
|
+
const rows = buildDiscoveredRows({
|
|
1365
|
+
provider,
|
|
1366
|
+
discovered: body?.discovered ?? [],
|
|
1367
|
+
advertisedIds: (catalog?.groups ?? []).find((group) => group.id === provider)?.models.map((model) => model.id) ?? [],
|
|
1368
|
+
settings: visibilitySettings(),
|
|
1369
|
+
patterns: hiddenPatterns()
|
|
1370
|
+
});
|
|
1371
|
+
discoveries.set(provider, { rows, pending: false });
|
|
1372
|
+
}
|
|
1373
|
+
} catch (error) {
|
|
1374
|
+
discoveries.set(provider, {
|
|
1375
|
+
rows: current?.rows ?? [],
|
|
1376
|
+
pending: false,
|
|
1377
|
+
failed: error instanceof Error ? error.message : String(error)
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1380
|
+
notify();
|
|
1381
|
+
}
|
|
1382
|
+
function applyToMenu() {
|
|
1383
|
+
const roots = findModelMenu(document);
|
|
1384
|
+
if (roots === void 0) return;
|
|
1385
|
+
attached = roots;
|
|
1386
|
+
if (listModelRows(roots).length === 0) {
|
|
1387
|
+
searchInput?.remove();
|
|
1388
|
+
searchInput = void 0;
|
|
1389
|
+
removeEmptyState(roots);
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
const index = nameIndex();
|
|
1393
|
+
const settings = visibilitySettings();
|
|
1394
|
+
const config = { hiddenPatterns: hiddenPatterns() };
|
|
1395
|
+
const isRowVisible = (row) => {
|
|
1396
|
+
const ids = index.get(row.providerName)?.get(row.modelName);
|
|
1397
|
+
if (ids === void 0) return true;
|
|
1398
|
+
return isModelVisible(ids.provider, ids.model, settings, config);
|
|
1399
|
+
};
|
|
1400
|
+
if (hasForeignSearchWidget(roots)) {
|
|
1401
|
+
applyVisibilityOnly(roots, isRowVisible);
|
|
1402
|
+
return;
|
|
1403
|
+
}
|
|
1404
|
+
const result = applyMenuFilter(roots, { query, isRowVisible });
|
|
1405
|
+
const sibling = roots.groups.previousElementSibling;
|
|
1406
|
+
const hasEmptyState = sibling !== null && sibling.nodeType === 1 && sibling.id === "dsh-llm-ctl-empty";
|
|
1407
|
+
if (result.shown === 0) {
|
|
1408
|
+
if (!hasEmptyState) {
|
|
1409
|
+
ensureEmptyState(roots, "\u65E0\u53EF\u89C1\u6A21\u578B", "\u663E\u793A\u5168\u90E8\u9690\u85CF\u9879", () => {
|
|
1410
|
+
void resetAllVisibility();
|
|
1411
|
+
});
|
|
1412
|
+
}
|
|
1413
|
+
} else if (hasEmptyState) {
|
|
1414
|
+
removeEmptyState(roots);
|
|
1415
|
+
}
|
|
1416
|
+
}
|
|
1417
|
+
function ensureSearchBox(roots) {
|
|
1418
|
+
if (listModelRows(roots).length === 0) return;
|
|
1419
|
+
if (hasForeignSearchWidget(roots)) {
|
|
1420
|
+
searchInput?.remove();
|
|
1421
|
+
searchInput = void 0;
|
|
1422
|
+
return;
|
|
1423
|
+
}
|
|
1424
|
+
if (searchInput !== void 0) {
|
|
1425
|
+
let sibling = roots.groups.previousElementSibling;
|
|
1426
|
+
while (sibling !== null) {
|
|
1427
|
+
if (sibling === searchInput) {
|
|
1428
|
+
if (!document.body.contains(searchInput)) break;
|
|
1429
|
+
return;
|
|
1430
|
+
}
|
|
1431
|
+
sibling = sibling.previousElementSibling;
|
|
1432
|
+
}
|
|
1433
|
+
searchInput.remove();
|
|
1434
|
+
searchInput = void 0;
|
|
1435
|
+
}
|
|
1436
|
+
const input = document.createElement("input");
|
|
1437
|
+
input.id = SEARCH_ID;
|
|
1438
|
+
input.type = "search";
|
|
1439
|
+
input.name = "dsh-llm-ctl-model-search";
|
|
1440
|
+
input.setAttribute("aria-label", "\u641C\u7D22\u6A21\u578B");
|
|
1441
|
+
input.placeholder = "\u641C\u7D22\u6A21\u578B\u2026 (Ctrl+F) \xB7 p: \u9650\u5B9A\u63D0\u4F9B\u65B9";
|
|
1442
|
+
input.value = query;
|
|
1443
|
+
input.addEventListener("input", (event) => {
|
|
1444
|
+
event.stopPropagation();
|
|
1445
|
+
query = input.value;
|
|
1446
|
+
applyToMenu();
|
|
1447
|
+
});
|
|
1448
|
+
input.addEventListener("keydown", (event) => {
|
|
1449
|
+
event.stopPropagation();
|
|
1450
|
+
if (event.key === "Escape") {
|
|
1451
|
+
input.value = "";
|
|
1452
|
+
query = "";
|
|
1453
|
+
applyToMenu();
|
|
1454
|
+
}
|
|
1455
|
+
});
|
|
1456
|
+
roots.groups.parentNode?.insertBefore(input, roots.groups);
|
|
1457
|
+
searchInput = input;
|
|
1458
|
+
}
|
|
1459
|
+
function detachMenu() {
|
|
1460
|
+
if (attached !== void 0) resetMenuFilter(attached);
|
|
1461
|
+
if (attached !== void 0) removeEmptyState(attached);
|
|
1462
|
+
attached = void 0;
|
|
1463
|
+
searchInput?.remove();
|
|
1464
|
+
searchInput = void 0;
|
|
1465
|
+
}
|
|
1466
|
+
function scheduleSync() {
|
|
1467
|
+
if (syncScheduled) return;
|
|
1468
|
+
syncScheduled = true;
|
|
1469
|
+
setTimeout(() => {
|
|
1470
|
+
syncScheduled = false;
|
|
1471
|
+
syncMenu();
|
|
1472
|
+
}, 50);
|
|
1473
|
+
}
|
|
1474
|
+
function syncMenu() {
|
|
1475
|
+
const roots = findModelMenu(document);
|
|
1476
|
+
if (roots === void 0) {
|
|
1477
|
+
if (attached !== void 0) detachMenu();
|
|
1478
|
+
return;
|
|
1479
|
+
}
|
|
1480
|
+
if (attached !== void 0 && attached.menu === roots.menu) {
|
|
1481
|
+
ensureSearchBox(roots);
|
|
1482
|
+
applyToMenu();
|
|
1483
|
+
return;
|
|
1484
|
+
}
|
|
1485
|
+
detachMenu();
|
|
1486
|
+
attached = roots;
|
|
1487
|
+
ensureSearchBox(roots);
|
|
1488
|
+
applyToMenu();
|
|
1489
|
+
}
|
|
1490
|
+
async function writeVisibility(input) {
|
|
1491
|
+
try {
|
|
1492
|
+
await postJson(VISIBILITY_URL, input);
|
|
1493
|
+
} catch (error) {
|
|
1494
|
+
ctxRef?.logger?.warn("llm-ctl: visibility write failed", error);
|
|
1495
|
+
}
|
|
1496
|
+
await tick();
|
|
1497
|
+
applyToMenu();
|
|
1498
|
+
}
|
|
1499
|
+
async function resetAllVisibility() {
|
|
1500
|
+
try {
|
|
1501
|
+
await postJson(VISIBILITY_RESET_URL, {});
|
|
1502
|
+
} catch (error) {
|
|
1503
|
+
ctxRef?.logger?.warn("llm-ctl: visibility reset failed", error);
|
|
1504
|
+
}
|
|
1505
|
+
await tick();
|
|
1506
|
+
applyToMenu();
|
|
1507
|
+
}
|
|
1508
|
+
async function writeQueue(input) {
|
|
1509
|
+
try {
|
|
1510
|
+
const response = await postJson(QUEUE_URL, input);
|
|
1511
|
+
const body = response.body;
|
|
1512
|
+
const error = typeof body?.error === "string" && body.error.length > 0 ? body.error : void 0;
|
|
1513
|
+
if (!response.ok) return { ok: false, ...error === void 0 ? {} : { error } };
|
|
1514
|
+
return { ok: true };
|
|
1515
|
+
} catch (error) {
|
|
1516
|
+
ctxRef?.logger?.warn("llm-ctl: queue write failed", error);
|
|
1517
|
+
return { ok: false };
|
|
1518
|
+
} finally {
|
|
1519
|
+
await tick();
|
|
1520
|
+
applyToMenu();
|
|
1521
|
+
}
|
|
1522
|
+
}
|
|
1523
|
+
async function resetQueue(expectedRevision) {
|
|
1524
|
+
let ok = true;
|
|
1525
|
+
try {
|
|
1526
|
+
ok = (await postJson(QUEUE_RESET_URL, { ...expectedRevision === void 0 ? {} : { expectedRevision } })).ok;
|
|
1527
|
+
} catch (error) {
|
|
1528
|
+
ctxRef?.logger?.warn("llm-ctl: queue reset failed", error);
|
|
1529
|
+
ok = false;
|
|
1530
|
+
}
|
|
1531
|
+
await tick();
|
|
1532
|
+
applyToMenu();
|
|
1533
|
+
return ok;
|
|
1534
|
+
}
|
|
1535
|
+
function FooterSeat() {
|
|
1536
|
+
const { state } = useCtlState();
|
|
1537
|
+
if (state === void 0) return null;
|
|
1538
|
+
const views = providerViews();
|
|
1539
|
+
const cooling = state.queue.lanes.filter((lane) => lane.cooldownRemainingMs > 0).length;
|
|
1540
|
+
const element = VisibilityFooter({
|
|
1541
|
+
summary: summarize(views),
|
|
1542
|
+
queue: { queued: state.queue.waiters.length, cooling },
|
|
1543
|
+
actions: {
|
|
1544
|
+
setProvider: (provider, visible) => void writeVisibility({ provider, visible }),
|
|
1545
|
+
setModel: (provider, model, visible) => void writeVisibility({ provider, model, visible }),
|
|
1546
|
+
resetAll: () => void resetAllVisibility()
|
|
1547
|
+
}
|
|
1548
|
+
});
|
|
1549
|
+
return element;
|
|
1550
|
+
}
|
|
1551
|
+
function PluginConfigSeat() {
|
|
1552
|
+
const { state } = useCtlState();
|
|
1553
|
+
if (state === void 0) return null;
|
|
1554
|
+
const views = providerViews();
|
|
1555
|
+
const element = PluginConfigCard({
|
|
1556
|
+
config: state.queueConfig,
|
|
1557
|
+
providers: views.map((view) => ({ provider: view.provider, displayName: view.displayName })),
|
|
1558
|
+
actions: {
|
|
1559
|
+
setQueue: (input) => writeQueue(input),
|
|
1560
|
+
resetQueue: (revision) => resetQueue(revision)
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
return element;
|
|
1564
|
+
}
|
|
1565
|
+
function resolveSeatProvider(owner, fallbackProviderId) {
|
|
1566
|
+
const dispatched = owner?.provider?.provider;
|
|
1567
|
+
return typeof dispatched === "string" && dispatched.length > 0 ? dispatched : fallbackProviderId;
|
|
1568
|
+
}
|
|
1569
|
+
function makeProviderCardSeat(fallbackProviderId) {
|
|
1570
|
+
return function ProviderCardSeat(props) {
|
|
1571
|
+
const { state } = useCtlState();
|
|
1572
|
+
if (state === void 0) return null;
|
|
1573
|
+
const rowProvider = resolveSeatProvider(props, fallbackProviderId);
|
|
1574
|
+
const settings = visibilitySettings();
|
|
1575
|
+
const patterns = hiddenPatterns();
|
|
1576
|
+
const view = providerViews().find((candidate) => candidate.provider === rowProvider) ?? {
|
|
1577
|
+
provider: rowProvider,
|
|
1578
|
+
displayName: typeof props.provider?.displayName === "string" && props.provider.displayName.length > 0 ? props.provider.displayName : rowProvider,
|
|
1579
|
+
visible: isProviderVisible(rowProvider, settings, { hiddenPatterns: patterns }),
|
|
1580
|
+
models: []
|
|
1581
|
+
};
|
|
1582
|
+
const discovery = discoveries.get(rowProvider) ?? { rows: [], pending: false };
|
|
1583
|
+
const actions = {
|
|
1584
|
+
setProvider: (provider, visible) => void writeVisibility({ provider, visible }),
|
|
1585
|
+
setModel: (provider, model, visible) => void writeVisibility({ provider, model, visible }),
|
|
1586
|
+
resetAll: () => void resetAllVisibility()
|
|
1587
|
+
};
|
|
1588
|
+
const showEntryRefresh = discovery.rows.length === 0 && !discovery.pending && discovery.failed === void 0;
|
|
1589
|
+
const element = import_react4.default.createElement(
|
|
1590
|
+
import_react4.default.Fragment,
|
|
1591
|
+
null,
|
|
1592
|
+
ProviderVisibilityCard({ view, actions }),
|
|
1593
|
+
showEntryRefresh ? RefreshModelsButton({ pending: false, onRefresh: () => void refreshDiscovery(rowProvider) }) : null,
|
|
1594
|
+
DiscoveredModelsList({
|
|
1595
|
+
provider: rowProvider,
|
|
1596
|
+
rows: discovery.rows,
|
|
1597
|
+
pending: discovery.pending,
|
|
1598
|
+
failed: discovery.failed,
|
|
1599
|
+
onToggle: (provider, model, visible) => void writeVisibility({ provider, model, visible }),
|
|
1600
|
+
onRefresh: () => void refreshDiscovery(rowProvider)
|
|
1601
|
+
})
|
|
1602
|
+
);
|
|
1603
|
+
return element;
|
|
1604
|
+
};
|
|
1605
|
+
}
|
|
1606
|
+
function QueueDockSeat() {
|
|
1607
|
+
const { state } = useCtlState();
|
|
1608
|
+
if (state === void 0) return null;
|
|
1609
|
+
const view = buildQueueDockView(state.queue);
|
|
1610
|
+
if (view === void 0) return null;
|
|
1611
|
+
return QueueDockPanel({ view, onCancel: (queueId) => void postJson(CANCEL_URL, { queueId }) });
|
|
1612
|
+
}
|
|
1613
|
+
function registerSeats(client) {
|
|
1614
|
+
const slots = client.slots;
|
|
1615
|
+
if (slots === void 0) return;
|
|
1616
|
+
slots.inject(
|
|
1617
|
+
"settings.plugin.item",
|
|
1618
|
+
() => slots.register({ name: "settings.plugin.item", key: "llm-ctl", order: 100 }, PluginConfigSeat)
|
|
1619
|
+
);
|
|
1620
|
+
slots.inject(
|
|
1621
|
+
"settings.models.footer",
|
|
1622
|
+
() => slots.register({ name: "settings.models.footer", id: "llm-ctl-visibility", order: 100 }, FooterSeat)
|
|
1623
|
+
);
|
|
1624
|
+
slots.inject(
|
|
1625
|
+
"conversation.composer.dock",
|
|
1626
|
+
() => slots.register({ name: "conversation.composer.dock", id: "llm-ctl-queue", order: 1e3 }, QueueDockSeat)
|
|
1627
|
+
);
|
|
1628
|
+
}
|
|
1629
|
+
var providerSeatsRegistered = false;
|
|
1630
|
+
function ensureProviderSeats(client) {
|
|
1631
|
+
if (providerSeatsRegistered) return;
|
|
1632
|
+
const providers = latestState?.visibility.configurableProviders;
|
|
1633
|
+
if (providers === void 0 || providers.length === 0) return;
|
|
1634
|
+
const slots = client.slots;
|
|
1635
|
+
if (slots === void 0) return;
|
|
1636
|
+
providerSeatsRegistered = true;
|
|
1637
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1638
|
+
for (const entry of providers) {
|
|
1639
|
+
if (typeof entry.settingsNs !== "string" || entry.settingsNs.length === 0) continue;
|
|
1640
|
+
if (seen.has(entry.settingsNs)) continue;
|
|
1641
|
+
seen.add(entry.settingsNs);
|
|
1642
|
+
slots.inject(
|
|
1643
|
+
"settings.models.provider-card",
|
|
1644
|
+
() => slots.register({ name: "settings.models.provider-card", key: entry.settingsNs }, makeProviderCardSeat(entry.provider))
|
|
1645
|
+
);
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
function pollDelayForFailures(failures) {
|
|
1649
|
+
if (failures <= 0) return POLL_MS;
|
|
1650
|
+
return Math.min(POLL_MS * 2 ** failures, MAX_POLL_MS);
|
|
1651
|
+
}
|
|
1652
|
+
function stateSignature(state) {
|
|
1653
|
+
return JSON.stringify([state.queue, state.visibility, state.reactive, state.queueConfig]);
|
|
1654
|
+
}
|
|
1655
|
+
function scheduleNext() {
|
|
1656
|
+
timer = setTimeout(() => void loop(), pollDelayForFailures(fetchFailures));
|
|
1657
|
+
}
|
|
1658
|
+
async function loop() {
|
|
1659
|
+
timer = void 0;
|
|
1660
|
+
if (typeof document !== "undefined" && document.hidden) {
|
|
1661
|
+
timer = setTimeout(() => void loop(), HIDDEN_POLL_MS);
|
|
1662
|
+
return;
|
|
1663
|
+
}
|
|
1664
|
+
await tick();
|
|
1665
|
+
if (ctxRef === void 0) return;
|
|
1666
|
+
scheduleNext();
|
|
1667
|
+
}
|
|
1668
|
+
async function tick() {
|
|
1669
|
+
try {
|
|
1670
|
+
latestState = await fetchState();
|
|
1671
|
+
fetchFailures = 0;
|
|
1672
|
+
const signature = stateSignature(latestState);
|
|
1673
|
+
if (signature !== lastNotifiedSignature) {
|
|
1674
|
+
lastNotifiedSignature = signature;
|
|
1675
|
+
notify();
|
|
1676
|
+
}
|
|
1677
|
+
if (ctxRef !== void 0) ensureProviderSeats(ctxRef);
|
|
1678
|
+
if (catalog === void 0 || Date.now() - catalogAt > CATALOG_TTL_MS) void refreshCatalog();
|
|
1679
|
+
syncMenu();
|
|
1680
|
+
} catch (error) {
|
|
1681
|
+
fetchFailures += 1;
|
|
1682
|
+
ctxRef?.logger?.warn("llm-ctl: state fetch failed", error);
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
function apply(ctx) {
|
|
1686
|
+
ctxRef = ctx;
|
|
1687
|
+
fetchFailures = 0;
|
|
1688
|
+
lastNotifiedSignature = void 0;
|
|
1689
|
+
ensureMenuStyles();
|
|
1690
|
+
ensureDockStyles();
|
|
1691
|
+
registerSeats(ctx);
|
|
1692
|
+
void refreshCatalog();
|
|
1693
|
+
void tick();
|
|
1694
|
+
scheduleNext();
|
|
1695
|
+
observer = new MutationObserver(() => {
|
|
1696
|
+
scheduleSync();
|
|
1697
|
+
});
|
|
1698
|
+
observer.observe(document.body, { childList: true, subtree: true });
|
|
1699
|
+
ctx.effect?.(() => () => {
|
|
1700
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1701
|
+
timer = void 0;
|
|
1702
|
+
observer?.disconnect();
|
|
1703
|
+
observer = void 0;
|
|
1704
|
+
detachMenu();
|
|
1705
|
+
subscribers.clear();
|
|
1706
|
+
ctxRef = void 0;
|
|
1707
|
+
}, "llm-ctl: stop polling and menu observation");
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
return module.exports;
|
|
1711
|
+
}
|
|
1712
|
+
});
|