@anionex/dsh-vision-toolkit 0.1.7 → 0.1.9

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.
Files changed (51) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +34 -17
  3. package/README.zh.md +32 -7
  4. package/assets/community-group-qr.png +0 -0
  5. package/assets/vision-model-test.png +0 -0
  6. package/docs/requirements-traceability/README.i18n.yaml +2 -2
  7. package/docs/requirements-traceability/README.md +2 -2
  8. package/docs/requirements-traceability/README.zh.md +2 -2
  9. package/lib/client.js +361 -28
  10. package/lib/client.js.map +1 -1
  11. package/lib/config.js +14 -0
  12. package/lib/config.js.map +1 -1
  13. package/lib/image-input-variants.js +615 -0
  14. package/lib/image-input-variants.js.map +1 -0
  15. package/lib/index.js +8 -1
  16. package/lib/index.js.map +1 -1
  17. package/lib/paste-images.js +6 -0
  18. package/lib/paste-images.js.map +1 -1
  19. package/lib/runtime.js +37 -3
  20. package/lib/runtime.js.map +1 -1
  21. package/lib/types/client/index.d.ts +16 -5
  22. package/lib/types/client/index.d.ts.map +1 -1
  23. package/lib/types/client/paste-images.d.ts +69 -0
  24. package/lib/types/client/paste-images.d.ts.map +1 -1
  25. package/lib/types/config.d.ts +26 -0
  26. package/lib/types/config.d.ts.map +1 -1
  27. package/lib/types/image-input-variants.d.ts +153 -0
  28. package/lib/types/image-input-variants.d.ts.map +1 -0
  29. package/lib/types/index.d.ts.map +1 -1
  30. package/lib/types/paste-images.d.ts +35 -0
  31. package/lib/types/paste-images.d.ts.map +1 -1
  32. package/lib/types/runtime.d.ts +5 -2
  33. package/lib/types/runtime.d.ts.map +1 -1
  34. package/lib/types/web-request.d.ts +7 -0
  35. package/lib/types/web-request.d.ts.map +1 -1
  36. package/lib/types/web.d.ts +17 -2
  37. package/lib/types/web.d.ts.map +1 -1
  38. package/lib/web-request.js +11 -2
  39. package/lib/web-request.js.map +1 -1
  40. package/lib/web.js +88 -5
  41. package/lib/web.js.map +1 -1
  42. package/package.json +2 -1
  43. package/src/client/index.tsx +53 -14
  44. package/src/client/paste-images.tsx +331 -15
  45. package/src/config.ts +40 -0
  46. package/src/image-input-variants.ts +688 -0
  47. package/src/index.ts +18 -1
  48. package/src/paste-images.ts +39 -0
  49. package/src/runtime.ts +42 -3
  50. package/src/web-request.ts +12 -2
  51. package/src/web.ts +90 -4
@@ -0,0 +1,615 @@
1
+ /**
2
+ * Image-input variants: sibling model-selector entries for every model the
3
+ * host positively declares text-only. A variant declares image input, so
4
+ * pasted images keep the native attachment flow — composer thumbnail and the
5
+ * durable session image — while the variant's stream rewrites every image
6
+ * block into a Vision Toolkit description before delegating to the original
7
+ * route. The durable log is untouched; only the wire carries text.
8
+ * @module dsh-vision-toolkit/image-input-variants
9
+ */
10
+ import { mkdtemp, rm, writeFile } from 'node:fs/promises';
11
+ import { tmpdir } from 'node:os';
12
+ import { join } from 'node:path';
13
+ import { LlmAdapter, contentHasImage } from '@deepseek-ai/dsh-llm';
14
+ /** Provider-id prefix for the variant routes this plugin registers. */
15
+ export const VARIANT_PROVIDER_PREFIX = 'vision-toolkit-';
16
+ /** Display suffix shared by variant provider names and variant model names. */
17
+ export const VARIANT_SUFFIX = ' (Vision Toolkit)';
18
+ /** Promise-cache bound for image descriptions, so a long-lived Web profile cannot hoard evidence text. */
19
+ const EVIDENCE_CACHE_LIMIT = 64;
20
+ /**
21
+ * Media types the Vision Toolkit glance pipeline accepts, by declared media
22
+ * type. Narrower than the paste-to-workspace route (which stores any image
23
+ * type): a paste of an unsupported type on a variant session degrades loudly
24
+ * on the wire instead of being described.
25
+ */
26
+ const MEDIA_EXTENSIONS = {
27
+ 'image/png': '.png',
28
+ 'image/jpeg': '.jpg',
29
+ 'image/webp': '.webp',
30
+ 'image/gif': '.gif',
31
+ };
32
+ /** Model-facing prefix on converted image blocks. */
33
+ const DESCRIBED_PREFIX = '[Image described by the Vision Toolkit]\n';
34
+ /** Model-facing prefix on degraded conversions; the model must never guess at image content. */
35
+ const DEGRADED_PREFIX = '[The Vision Toolkit could not describe this image: ';
36
+ /** The variant provider route minted for one upstream route. */
37
+ export function variantProviderId(upstream) {
38
+ return `${VARIANT_PROVIDER_PREFIX}${upstream}`;
39
+ }
40
+ /**
41
+ * Whether one model earns an image-input variant: the host must positively
42
+ * declare it text-only. A model with unknown modalities is left alone — its
43
+ * native channel is the safe default, and the variant would degrade it.
44
+ * @param info - model metadata from the host catalog.
45
+ * @returns true when the model is confirmed text-only.
46
+ */
47
+ export function shouldWrapModel(info) {
48
+ return Array.isArray(info.inputModalities) && !info.inputModalities.includes('image');
49
+ }
50
+ /** Whether a content block list carries an image at any depth (tool-result nesting included). */
51
+ export { contentHasImage } from '@deepseek-ai/dsh-llm';
52
+ function messageOf(error) {
53
+ return error instanceof Error ? error.message : String(error);
54
+ }
55
+ /** Bounded promise cache for one attachment's description; failed reads are not retained. */
56
+ export class EvidenceCache {
57
+ limit;
58
+ entries = new Map();
59
+ constructor(limit) {
60
+ this.limit = limit;
61
+ }
62
+ /**
63
+ * Read one key's entry or compute it. Concurrent readers join the in-flight
64
+ * computation; a settled failure is evicted so a fixed configuration gets a
65
+ * fresh chance.
66
+ * @param key - the attachment identity (content-addressed).
67
+ * @param load - computes the description; must resolve `{ ok, block }` and never reject.
68
+ * @returns the cached or computed block.
69
+ */
70
+ read(key, load) {
71
+ const existing = this.entries.get(key);
72
+ if (existing !== undefined) {
73
+ // Refresh recency: Map iteration order is insertion order.
74
+ this.entries.delete(key);
75
+ this.entries.set(key, existing);
76
+ return existing;
77
+ }
78
+ const pending = load().then((result) => {
79
+ // Only evict our own entry: this promise may have been LRU-evicted and
80
+ // the key re-populated by a newer read meanwhile.
81
+ if (!result.ok && this.entries.get(key) === pending) {
82
+ this.entries.delete(key);
83
+ }
84
+ return result.block;
85
+ }, (error) => {
86
+ if (this.entries.get(key) === pending) {
87
+ this.entries.delete(key);
88
+ }
89
+ throw error;
90
+ });
91
+ this.entries.set(key, pending);
92
+ while (this.entries.size > this.limit) {
93
+ const oldest = this.entries.keys().next().value;
94
+ if (oldest === undefined)
95
+ break;
96
+ this.entries.delete(oldest);
97
+ }
98
+ return pending;
99
+ }
100
+ /** Drop every cached description (runtime reconfiguration invalidates provider-specific reads). */
101
+ clear() {
102
+ this.entries.clear();
103
+ }
104
+ }
105
+ /**
106
+ * Wait on a shared promise without inheriting its lifetime: the caller's
107
+ * abort rejects this wait immediately, while the underlying read keeps
108
+ * running and lands in the cache for the retry.
109
+ * @param promise - the shared computation.
110
+ * @param signal - the caller's cancellation, or undefined to wait unconditionally.
111
+ * @returns the computed value, unless the caller aborted first.
112
+ */
113
+ export function abortableWait(promise, signal) {
114
+ if (signal === undefined)
115
+ return promise;
116
+ return new Promise((resolve, reject) => {
117
+ if (signal.aborted) {
118
+ reject(signal.reason ?? new Error('aborted'));
119
+ return;
120
+ }
121
+ const onAbort = () => { reject(signal.reason ?? new Error('aborted')); };
122
+ signal.addEventListener('abort', onAbort, { once: true });
123
+ promise.then((value) => {
124
+ signal.removeEventListener('abort', onAbort);
125
+ resolve(value);
126
+ }, (error) => {
127
+ signal.removeEventListener('abort', onAbort);
128
+ reject(error);
129
+ });
130
+ });
131
+ }
132
+ async function convertBlocks(blocks, convert) {
133
+ const out = [];
134
+ for (const block of blocks) {
135
+ if (block.type === 'image') {
136
+ out.push(await convert(block));
137
+ }
138
+ else if (block.type === 'tool-result' && contentHasImage(block.content)) {
139
+ out.push({ ...block, content: await convertBlocks(block.content, convert) });
140
+ }
141
+ else {
142
+ out.push(block);
143
+ }
144
+ }
145
+ return out;
146
+ }
147
+ /**
148
+ * Read one image block into a Vision Toolkit description text block. Never
149
+ * throws: failures degrade to an explanatory block with `ok: false`, so the
150
+ * caller can decide what a failure means (the cache refuses to memoize it).
151
+ * @param ctx - plugin context; reads the optional `attachments` service.
152
+ * @param runtime - the currently serving Vision Toolkit runtime, if ready.
153
+ * @param block - the image block to describe.
154
+ * @returns the outcome and its model-facing replacement block.
155
+ */
156
+ async function readImageBlock(ctx, runtime, block) {
157
+ const attachments = ctx.get('attachments');
158
+ const current = runtime();
159
+ if (attachments === undefined || current === undefined) {
160
+ return { ok: false, block: { type: 'text', text: `${DEGRADED_PREFIX}the Vision Toolkit runtime is not ready.]` } };
161
+ }
162
+ const extension = MEDIA_EXTENSIONS[block.attachment.mediaType];
163
+ if (extension === undefined) {
164
+ return { ok: false, block: { type: 'text', text: `${DEGRADED_PREFIX}unsupported image media type ${block.attachment.mediaType}.]` } };
165
+ }
166
+ let directory;
167
+ try {
168
+ // The glance pipeline validates paths against the workspace it is given,
169
+ // so the temp copy lives in a dedicated directory passed as that workspace.
170
+ const stored = await attachments.readImage(block.attachment);
171
+ directory = await mkdtemp(join(tmpdir(), 'dsh-vision-toolkit-'));
172
+ const file = join(directory, `image${extension}`);
173
+ await writeFile(file, Buffer.from(stored.data), { mode: 0o600 });
174
+ // A fresh signal on purpose: the cached run must not die with its first
175
+ // caller (their abort used to cancel every concurrent joiner); the runtime
176
+ // deadline still bounds it.
177
+ const result = await current.glance({ images: [file] }, { signal: new AbortController().signal, workspace: directory });
178
+ const answer = result.answer.trim();
179
+ if (answer.length === 0)
180
+ throw new Error('the Vision Toolkit returned an empty description');
181
+ return { ok: true, block: { type: 'text', text: `${DESCRIBED_PREFIX}${answer}` } };
182
+ }
183
+ catch (error) {
184
+ return {
185
+ ok: false,
186
+ block: { type: 'text', text: `${DEGRADED_PREFIX}${messageOf(error).slice(0, 300)}.]` },
187
+ };
188
+ }
189
+ finally {
190
+ if (directory !== undefined) {
191
+ await rm(directory, { recursive: true, force: true }).catch(() => { });
192
+ }
193
+ }
194
+ }
195
+ /**
196
+ * Rewrite image blocks in one message list into description text blocks.
197
+ * The original messages are returned untouched when nothing carries an image;
198
+ * converted messages are new objects, so the durable request stays immutable.
199
+ * @param ctx - plugin context for the attachments service.
200
+ * @param runtime - the currently serving runtime (lazily read per conversion).
201
+ * @param cache - shared per-adapter description cache.
202
+ * @param messages - the assembled request messages.
203
+ * @param signal - the caller's cancellation for this conversion pass.
204
+ * @returns the rewritten message list.
205
+ */
206
+ export async function convertImagesToEvidence(ctx, runtime, cache, messages, signal) {
207
+ const out = [];
208
+ for (const message of messages) {
209
+ if (!contentHasImage(message.content)) {
210
+ out.push(message);
211
+ continue;
212
+ }
213
+ const content = await convertBlocks(message.content, (block) => abortableWait(cache.read(String(block.attachment.attachmentId), () => readImageBlock(ctx, runtime, block)), signal));
214
+ out.push({ ...message, content });
215
+ }
216
+ return out;
217
+ }
218
+ /**
219
+ * The adapter behind one variant route: model metadata declares image input,
220
+ * and every stream rewrites image blocks before delegating to the upstream
221
+ * route through the host service (so the upstream route's own middleware,
222
+ * retry policy, and replay handling still apply).
223
+ */
224
+ export class ImageInputVariantAdapter extends LlmAdapter {
225
+ ctx;
226
+ llm;
227
+ upstream;
228
+ upstreamName;
229
+ runtime;
230
+ cache;
231
+ lastRuntime;
232
+ constructor(ctx, llm, upstream, upstreamName, runtime, cache) {
233
+ super();
234
+ this.ctx = ctx;
235
+ this.llm = llm;
236
+ this.upstream = upstream;
237
+ this.upstreamName = upstreamName;
238
+ this.runtime = runtime;
239
+ this.cache = cache;
240
+ }
241
+ providerInfo(provider) {
242
+ return { id: provider, name: `${this.upstreamName}${VARIANT_SUFFIX}` };
243
+ }
244
+ async listModels(provider) {
245
+ const models = await this.llm.listModels(this.upstream);
246
+ return models.filter(shouldWrapModel).map((model) => ({
247
+ provider,
248
+ id: model.id,
249
+ name: `${model.name}${VARIANT_SUFFIX}`,
250
+ inputModalities: ['text', 'image'],
251
+ ...(model.description === undefined ? {} : { description: model.description }),
252
+ }));
253
+ }
254
+ async resolveModel(provider, model, signal) {
255
+ const info = await this.llm.resolveModelInfo(this.upstream, model, signal);
256
+ if (!shouldWrapModel(info)) {
257
+ throw new Error(`model "${model}" is not a text-only model and needs no image-input variant`);
258
+ }
259
+ return {
260
+ provider,
261
+ id: model,
262
+ name: `${info.name}${VARIANT_SUFFIX}`,
263
+ inputModalities: ['text', 'image'],
264
+ ...(info.description === undefined ? {} : { description: info.description }),
265
+ // Capability and call-default metadata rides through unchanged: the
266
+ // variant is a wire-only facade, so context capacity, output caps, and
267
+ // reasoning efforts must behave exactly like the upstream route.
268
+ ...(info.context === undefined ? {} : { context: info.context }),
269
+ ...(info.defaultMaxTokens === undefined ? {} : { defaultMaxTokens: info.defaultMaxTokens }),
270
+ ...(info.reasoning === undefined ? {} : { reasoning: info.reasoning }),
271
+ };
272
+ }
273
+ async *stream(options) {
274
+ // A reconfigured runtime is a NEW instance; descriptions read through the
275
+ // previous provider must not be replayed for the new one.
276
+ const current = this.runtime();
277
+ if (current !== this.lastRuntime) {
278
+ this.cache.clear();
279
+ this.lastRuntime = current;
280
+ }
281
+ const messages = await convertImagesToEvidence(this.ctx, this.runtime, this.cache, options.messages, options.signal);
282
+ // Delegate through the host service under the upstream route: the variant
283
+ // is a wire-only facade, and the upstream route owns retry and replay.
284
+ yield* this.llm.stream({ ...options, provider: this.upstream, messages });
285
+ }
286
+ }
287
+ /**
288
+ * Whether the plugin should take a paste over for one live Session: true only
289
+ * when the current model is positively declared text-only. The model-selector
290
+ * label is the authoritative source when supplied — the Session's persisted
291
+ * route header only updates on a request, so a model switch would otherwise be
292
+ * invisible until the next turn — with a fallback to that header. Unknown
293
+ * routes answer false: the native attachment flow is the safe default, and a
294
+ * text-only model merely keeps its ordinary image-admission error.
295
+ * @param ctx - plugin context with `sessions` and `llm`.
296
+ * @param sessionId - the live Session id the paste belongs to.
297
+ * @param modelLabel - the model-selector label the client currently shows, if any.
298
+ * @returns true when pastes should become workspace paths instead of attachments.
299
+ */
300
+ export async function sessionPasteTakeover(ctx, sessionId, modelLabel) {
301
+ if (modelLabel !== undefined && modelLabel.trim() !== '') {
302
+ const byLabel = await labelTakeoverVerdict(ctx, modelLabel);
303
+ if (byLabel !== undefined)
304
+ return byLabel;
305
+ }
306
+ return sessionHeaderTakeover(ctx, sessionId);
307
+ }
308
+ /**
309
+ * Resolve the takeover verdict from the Session's last requested route header.
310
+ * @param ctx - plugin context with `sessions` and `llm`.
311
+ * @param sessionId - the live Session id.
312
+ * @returns true when the persisted route is positively text-only.
313
+ */
314
+ export async function sessionHeaderTakeover(ctx, sessionId) {
315
+ const session = ctx.sessions.get(sessionId);
316
+ if (session === undefined)
317
+ return false;
318
+ const routed = session.requestHeader()?.config;
319
+ if (routed === undefined)
320
+ return false;
321
+ const llm = ctx.get('llm');
322
+ if (llm === undefined)
323
+ return false;
324
+ let info;
325
+ try {
326
+ info = await llm.resolveModelInfo(routed.provider, routed.model);
327
+ }
328
+ catch {
329
+ return false;
330
+ }
331
+ return shouldWrapModel(info);
332
+ }
333
+ /**
334
+ * Resolve the takeover verdict from a model-selector label alone. Every model
335
+ * whose name or id appears in the label votes: any image-capable (or unknown-
336
+ * capability) match vetoes the takeover, and at least one positively text-only
337
+ * match confirms it. A route whose catalog cannot be read also vetoes — the
338
+ * unreadable route is exactly where an image-capable twin could hide, so a
339
+ * label match on a half-read catalog must not confirm a takeover. The label
340
+ * carries no provider id, so no picking is attempted: the answer is decisive
341
+ * only when the whole catalog was walkable and every match agrees.
342
+ * @param ctx - plugin context with the `llm` service.
343
+ * @param label - the selector label the browser shows.
344
+ * @returns true (take over), false (native), or undefined when nothing matched.
345
+ */
346
+ export async function labelTakeoverVerdict(ctx, label) {
347
+ const llm = ctx.get('llm');
348
+ if (llm === undefined)
349
+ return undefined;
350
+ const lowered = label.toLowerCase();
351
+ let matchedTextOnly = false;
352
+ for (const provider of llm.listProviders()) {
353
+ let models;
354
+ try {
355
+ models = await llm.listModels(provider.id);
356
+ }
357
+ catch (error) {
358
+ // An unreadable route cannot vote — and it is exactly where an
359
+ // image-capable twin could hide (a variant route probes its upstream).
360
+ // A label match on a half-read catalog must not confirm a takeover, so
361
+ // the verdict is vetoed for THIS label; the session header fallback is
362
+ // deliberately not used here because it may still describe a previous
363
+ // model after a switch. Loud, so a broken provider is diagnosable.
364
+ ctx.logger.warn('dsh-vision-toolkit: paste verdict could not read route "%s"; native paste wins for this label. %s', provider.id, messageOf(error).slice(0, 300));
365
+ return false;
366
+ }
367
+ for (const model of models) {
368
+ for (const candidate of [model.name, model.id]) {
369
+ if (typeof candidate !== 'string' || candidate.length === 0)
370
+ continue;
371
+ if (!lowered.includes(candidate.toLowerCase()))
372
+ continue;
373
+ if (!shouldWrapModel(model)) {
374
+ // An image-capable or unconfirmed model in the label keeps its
375
+ // native paste; the variant routes declare image input and are
376
+ // covered by this veto.
377
+ return false;
378
+ }
379
+ // Positive confirmation has a floor: one- and two-character names
380
+ // match label prose far too easily to identify the selected model.
381
+ if (candidate.length >= 3)
382
+ matchedTextOnly = true;
383
+ }
384
+ }
385
+ }
386
+ return matchedTextOnly ? true : undefined;
387
+ }
388
+ /** Label-verdict cache bound, so a long-lived Web profile cannot hoard catalog walks. */
389
+ const LABEL_VERDICT_TTL_MS = 15_000;
390
+ const LABEL_VERDICT_CAP = 32;
391
+ /**
392
+ * Resolve the paste verdict for one exact model route. Image-capable (or
393
+ * unresolvable) routes keep the native flow; a text-only route whose
394
+ * image-input variant is registered gets an auto-switch instruction; a
395
+ * text-only route without a usable variant falls back to the path takeover.
396
+ * @param ctx - plugin context with the `llm` service.
397
+ * @param getConfig - resolves the current plugin configuration.
398
+ * @param selection - the exact provider/model the browser currently selects.
399
+ * @returns the verdict for that route.
400
+ */
401
+ async function routePasteVerdict(ctx, getConfig, selection) {
402
+ const llm = ctx.get('llm');
403
+ if (llm === undefined)
404
+ return { takeOver: false };
405
+ let info;
406
+ try {
407
+ info = await llm.resolveModelInfo(selection.provider, selection.model);
408
+ }
409
+ catch {
410
+ // An unresolvable route keeps the native flow; the host's own admission
411
+ // error is the honest answer for a model that cannot take images.
412
+ return { takeOver: false };
413
+ }
414
+ if (!shouldWrapModel(info))
415
+ return { takeOver: false };
416
+ const variants = getConfig().imageInputVariants;
417
+ // Auto-switch is an opt-in refinement of the takeover: off means the
418
+ // text-only route keeps its ordinary path takeover.
419
+ if (!variants.enabled || !variants.autoSwitch)
420
+ return { takeOver: true };
421
+ const variantProvider = variantProviderId(selection.provider);
422
+ try {
423
+ if (!llm.listProviders().some(provider => provider.id === variantProvider))
424
+ return { takeOver: true };
425
+ const models = await llm.listModels(variantProvider);
426
+ const twin = models.find(model => model.id === selection.model);
427
+ // The variant only wraps text-only models, so membership confirms both
428
+ // the route and the wrap; anything else keeps the path takeover.
429
+ if (twin === undefined)
430
+ return { takeOver: true };
431
+ return {
432
+ takeOver: false,
433
+ autoSwitch: {
434
+ provider: variantProvider,
435
+ model: selection.model,
436
+ label: twin.name,
437
+ ...(selection.reasoningEffort === undefined ? {} : { reasoningEffort: selection.reasoningEffort }),
438
+ },
439
+ };
440
+ }
441
+ catch {
442
+ return { takeOver: true };
443
+ }
444
+ }
445
+ /**
446
+ * Paste-policy resolver with a short cache. The exact route is the live fact
447
+ * (the browser re-reads it per paste), and the host catalog only changes on
448
+ * topology events, so a brief cache is safe; every `llm/adapters-updated`
449
+ * notification empties it — including the sweep that registers a variant
450
+ * after the first sweep, so a stale "no variant" verdict cannot outlive the
451
+ * route it described.
452
+ * @param ctx - plugin context with the `llm` service.
453
+ * @param getConfig - resolves the current plugin configuration per verdict.
454
+ * @returns the cached verdict resolver for the Web paste-policy route.
455
+ */
456
+ export function createPasteTakeoverResolver(ctx, getConfig) {
457
+ const routes = new Map();
458
+ const labels = new Map();
459
+ const trim = (map) => {
460
+ while (map.size > LABEL_VERDICT_CAP) {
461
+ const oldest = map.keys().next().value;
462
+ if (oldest === undefined)
463
+ break;
464
+ map.delete(oldest);
465
+ }
466
+ };
467
+ if (typeof ctx.on === 'function') {
468
+ ctx.on('llm/adapters-updated', () => {
469
+ routes.clear();
470
+ labels.clear();
471
+ });
472
+ }
473
+ return async (sessionId, selection, modelLabel) => {
474
+ if (selection !== undefined && selection.provider.trim() !== '' && selection.model.trim() !== '') {
475
+ const key = `route:${selection.provider}|${selection.model}`;
476
+ const cached = routes.get(key);
477
+ if (cached !== undefined && Date.now() - cached.at <= LABEL_VERDICT_TTL_MS)
478
+ return cached.verdict;
479
+ const verdict = await routePasteVerdict(ctx, getConfig, selection);
480
+ routes.set(key, { verdict, at: Date.now() });
481
+ trim(routes);
482
+ return verdict;
483
+ }
484
+ if (modelLabel !== undefined && modelLabel.trim() !== '') {
485
+ const key = `label:${modelLabel}`;
486
+ const cached = labels.get(key);
487
+ if (cached !== undefined && Date.now() - cached.at <= LABEL_VERDICT_TTL_MS) {
488
+ return { takeOver: cached.takeOver ?? (await sessionHeaderTakeover(ctx, sessionId)) };
489
+ }
490
+ const verdict = await labelTakeoverVerdict(ctx, modelLabel);
491
+ // A decisive answer AND a miss are both cached: a label that matches
492
+ // nothing would otherwise pay a full catalog walk on every paste.
493
+ labels.set(key, { takeOver: verdict, at: Date.now() });
494
+ trim(labels);
495
+ return { takeOver: verdict ?? (await sessionHeaderTakeover(ctx, sessionId)) };
496
+ }
497
+ return { takeOver: await sessionHeaderTakeover(ctx, sessionId) };
498
+ };
499
+ }
500
+ /**
501
+ * Register and maintain one variant route per eligible upstream route. Routes
502
+ * that later vanish are released; routes that gain eligible models later are
503
+ * picked up by the next sweep (host topology notifications included).
504
+ * @param ctx - plugin context with the `llm` service.
505
+ * @param getConfig - resolves the current plugin configuration per sweep.
506
+ * @param getRuntime - the currently serving Vision Toolkit runtime, if ready.
507
+ * @returns the disposer and a manual re-sweep trigger (settings changes).
508
+ */
509
+ export function installImageInputVariants(ctx, getConfig, getRuntime) {
510
+ const registrations = new Map();
511
+ let disposed = false;
512
+ // Serialize sweeps: a registration itself announces llm/adapters-updated,
513
+ // and two interleaved sweeps must never probe the same route concurrently.
514
+ let sweeping = Promise.resolve();
515
+ // Coalesce bursts: a sweep triggered while one is pending is one extra pass,
516
+ // not one per notification (a single registration emits a notification).
517
+ let sweepQueued = false;
518
+ const releaseAll = () => {
519
+ for (const dispose of [...registrations.values()])
520
+ dispose();
521
+ registrations.clear();
522
+ };
523
+ const sweep = () => {
524
+ if (sweepQueued)
525
+ return;
526
+ sweepQueued = true;
527
+ queueMicrotask(() => {
528
+ sweepQueued = false;
529
+ sweeping = sweeping.then(sweepOnce, sweepOnce);
530
+ });
531
+ };
532
+ const sweepOnce = async () => {
533
+ if (disposed)
534
+ return;
535
+ try {
536
+ const variants = getConfig().imageInputVariants;
537
+ if (!variants.enabled) {
538
+ releaseAll();
539
+ return;
540
+ }
541
+ const llm = ctx.get('llm');
542
+ if (llm === undefined)
543
+ return;
544
+ const restrict = new Set(variants.providers);
545
+ let providers;
546
+ try {
547
+ providers = llm.listProviders();
548
+ }
549
+ catch {
550
+ return;
551
+ }
552
+ for (const provider of providers) {
553
+ const upstream = provider.id;
554
+ if (restrict.size > 0 && !restrict.has(upstream))
555
+ continue;
556
+ // Our own variant routes declare image input and are never wrapped;
557
+ // probing them would just re-probe their upstream route.
558
+ if (upstream.startsWith(VARIANT_PROVIDER_PREFIX))
559
+ continue;
560
+ let models;
561
+ try {
562
+ models = await llm.listModels(upstream);
563
+ }
564
+ catch {
565
+ continue;
566
+ }
567
+ const eligible = models.some(shouldWrapModel);
568
+ const registered = registrations.has(upstream);
569
+ if (!eligible && registered) {
570
+ // The route lost its eligible models: release the stale variant.
571
+ const dispose = registrations.get(upstream);
572
+ dispose?.();
573
+ registrations.delete(upstream);
574
+ continue;
575
+ }
576
+ if (!eligible || registered)
577
+ continue;
578
+ if (disposed)
579
+ return;
580
+ try {
581
+ const dispose = llm.registerAdapter([variantProviderId(upstream)], new ImageInputVariantAdapter(ctx, llm, upstream, provider.name, getRuntime, new EvidenceCache(EVIDENCE_CACHE_LIMIT)));
582
+ registrations.set(upstream, dispose);
583
+ }
584
+ catch (error) {
585
+ ctx.logger.warn('dsh-vision-toolkit: image-input variant registration skipped for "%s": %s', upstream, messageOf(error));
586
+ }
587
+ }
588
+ const live = new Set(providers.map(provider => provider.id));
589
+ for (const [upstream, dispose] of [...registrations]) {
590
+ // A wrapper is released when its upstream route vanished OR the
591
+ // current configuration no longer allows that route (restrict
592
+ // narrowing must not leave stale variants behind).
593
+ if (!live.has(upstream) || (restrict.size > 0 && !restrict.has(upstream))) {
594
+ dispose();
595
+ registrations.delete(upstream);
596
+ }
597
+ }
598
+ }
599
+ catch (error) {
600
+ ctx.logger.warn('dsh-vision-toolkit: image-input variant sweep failed: %s', messageOf(error));
601
+ }
602
+ };
603
+ if (typeof ctx.on === 'function') {
604
+ ctx.on('llm/adapters-updated', () => { sweep(); });
605
+ }
606
+ sweep();
607
+ return {
608
+ dispose: () => {
609
+ disposed = true;
610
+ releaseAll();
611
+ },
612
+ reconcile: () => { sweep(); },
613
+ };
614
+ }
615
+ //# sourceMappingURL=image-input-variants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image-input-variants.js","sourceRoot":"","sources":["../src/image-input-variants.ts"],"names":[],"mappings":"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"}