@mengruo/dsh-vision-toolkit 0.1.1 → 0.1.3

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/src/config.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  import z from '@deepseek-ai/schemastery'
10
10
  import type Schema from '@deepseek-ai/schemastery'
11
11
  import { credentialRef, type CredentialRef } from '@deepseek-ai/dsh-credentials'
12
- import { settingsNamespace } from '@deepseek-ai/dsh-settings'
12
+ import type { SettingsNamespace } from '@deepseek-ai/dsh-settings'
13
13
  import { VisionToolkitError } from './errors.ts'
14
14
  import {
15
15
  BUILT_IN_FREE_VISION_BASE_URL,
@@ -24,8 +24,21 @@ export {
24
24
  BUILT_IN_FREE_VISION_MODEL,
25
25
  } from './defaults.ts'
26
26
 
27
+ /**
28
+ * The namespace pattern the removed `settingsNamespace` helper enforced.
29
+ * dsh 0.1.2-alpha dropped that export; importing a missing named export is a
30
+ * module-evaluation error that stops the host from booting, so the check is
31
+ * inlined here instead of imported. The namespace is a static string, so no
32
+ * runtime dependency on `@deepseek-ai/dsh-settings` is needed.
33
+ */
34
+ const SETTINGS_NAMESPACE_PATTERN = /^[a-z][a-z0-9-]*$/
35
+
27
36
  /** Settings document namespace owned by this plugin. */
28
- export const VISION_TOOLKIT_SETTINGS_NAMESPACE = settingsNamespace('vision-toolkit')
37
+ export const VISION_TOOLKIT_SETTINGS_NAMESPACE = 'vision-toolkit' as SettingsNamespace
38
+
39
+ if (!SETTINGS_NAMESPACE_PATTERN.test(VISION_TOOLKIT_SETTINGS_NAMESPACE)) {
40
+ throw new TypeError(`settings namespace "${VISION_TOOLKIT_SETTINGS_NAMESPACE}" must match ${String(SETTINGS_NAMESPACE_PATTERN)}`)
41
+ }
29
42
 
30
43
  /** Browser-compatible default shared with the vendored Python client. */
31
44
  export const DEFAULT_VISION_USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/126.0.0.0 Safari/537.36'
@@ -64,8 +77,10 @@ export interface VisionProviderConfig {
64
77
  anthropicThinking?: 'omit' | 'disabled' | 'adaptive'
65
78
  /** Outbound User-Agent for provider requests and connection tests. */
66
79
  userAgent?: string
67
- /** Per-provider single request timeout in milliseconds. */
68
- timeoutMs?: number
80
+ /** t1: per-request hedge threshold in seconds. A single request exceeding t1 keeps running while the next provider starts in parallel. */
81
+ t1Seconds?: number
82
+ /** t2: per-provider cumulative cutoff in seconds. Total accumulated request time reaching t2 terminates the provider. */
83
+ t2Seconds?: number
69
84
  /** Per-provider maximum input image bytes; larger images skip to a later provider or are compressed. */
70
85
  maxImageBytes?: number
71
86
  /** Per-provider maximum decoded pixel count per input image. */
@@ -96,13 +111,17 @@ export interface VisionToolkitConfig {
96
111
  providers?: VisionProviderConfig[]
97
112
  /** Vision output language (`zh` or `en`). */
98
113
  language?: 'zh' | 'en'
99
- /** Single remote/upstream call budget in milliseconds. */
100
- timeoutMs?: number
114
+ /** Global hard timeout in seconds for one tool invocation; the call never exceeds it. */
115
+ hardTimeoutSeconds?: number
116
+ /** Per-session cap on concurrent tool operations (default 6). */
117
+ sessionMaxConcurrency?: number
118
+ /** Minimum remaining budget in seconds required before issuing a new request (default 20). */
119
+ minAvailableSeconds?: number
101
120
  /** Maximum input image size in bytes; larger images are auto-compressed (lossless first). */
102
121
  maxImageBytes?: number
103
122
  /** Maximum decoded pixel count per input image; larger images are auto-downscaled to fit. */
104
123
  maxImagePixels?: number
105
- /** In-flight tool execution cap per session. */
124
+ /** Default per-model in-flight request cap inherited by providers that do not set their own. */
106
125
  concurrency?: number
107
126
  runtime?: {
108
127
  /** `managed` uses the packaged snapshot and isolated venv; `external` uses a clean pinned checkout. */
@@ -166,14 +185,17 @@ export const Config: Schema<VisionToolkitConfig> = z.object({
166
185
  protocol: z.union(['openai', 'anthropic'] as const).default('openai'),
167
186
  anthropicThinking: z.union(['omit', 'disabled', 'adaptive'] as const).default('omit'),
168
187
  userAgent: z.string(),
169
- timeoutMs: z.number(),
188
+ t1Seconds: z.number(),
189
+ t2Seconds: z.number(),
170
190
  maxImageBytes: z.number(),
171
191
  maxImagePixels: z.number(),
172
192
  concurrency: z.number(),
173
193
  attempts: z.number(),
174
194
  })).default([]),
175
195
  language: z.union(['zh', 'en'] as const).default('zh'),
176
- timeoutMs: z.number().default(30000),
196
+ hardTimeoutSeconds: z.number().default(180),
197
+ sessionMaxConcurrency: z.number().default(6),
198
+ minAvailableSeconds: z.number().default(20),
177
199
  maxImageBytes: z.number().default(4194304),
178
200
  maxImagePixels: z.number().default(20000000),
179
201
  concurrency: z.number().default(4),
@@ -203,7 +225,8 @@ export interface ResolvedProvider {
203
225
  protocol: 'openai' | 'anthropic'
204
226
  anthropicThinking: 'omit' | 'disabled' | 'adaptive'
205
227
  userAgent: string
206
- timeoutMs: number
228
+ t1Seconds: number
229
+ t2Seconds: number
207
230
  maxImageBytes: number
208
231
  maxImagePixels: number
209
232
  concurrency: number
@@ -223,7 +246,9 @@ export interface ResolvedVisionToolkitConfig {
223
246
  /** Ordered failover pool; array order is the priority, highest first. */
224
247
  providers: ResolvedProvider[]
225
248
  language: 'zh' | 'en'
226
- timeoutMs: number
249
+ hardTimeoutSeconds: number
250
+ sessionMaxConcurrency: number
251
+ minAvailableSeconds: number
227
252
  maxImageBytes: number
228
253
  maxImagePixels: number
229
254
  concurrency: number
@@ -241,7 +266,7 @@ export interface ResolvedVisionToolkitConfig {
241
266
  }
242
267
  }
243
268
 
244
- const MAX_TIMEOUT_MS = 600000
269
+ const MAX_TIMEOUT_SECONDS = 600
245
270
  const MAX_IMAGE_BYTES = 268435456
246
271
  const MAX_IMAGE_PIXELS = 268435456
247
272
  const MAX_CONCURRENCY = 16
@@ -251,7 +276,6 @@ const DEFAULT_PROVIDER_ATTEMPTS = 3
251
276
 
252
277
  /** Global limits a provider inherits when it does not set its own. */
253
278
  interface ProviderDefaults {
254
- timeoutMs: number
255
279
  maxImageBytes: number
256
280
  maxImagePixels: number
257
281
  concurrency: number
@@ -330,9 +354,13 @@ function resolveProvider(
330
354
  if (userAgent.length === 0) {
331
355
  throw new VisionToolkitError('config', `${label}.userAgent must not be empty`)
332
356
  }
333
- const timeoutMs = input.timeoutMs ?? defaults.timeoutMs
334
- if (!Number.isInteger(timeoutMs) || timeoutMs < 1000 || timeoutMs > MAX_TIMEOUT_MS) {
335
- throw new VisionToolkitError('config', `${label}.timeoutMs must be an integer between 1000 and ${MAX_TIMEOUT_MS}`)
357
+ const t1Seconds = input.t1Seconds ?? 90
358
+ if (!Number.isInteger(t1Seconds) || t1Seconds < 1 || t1Seconds > MAX_TIMEOUT_SECONDS) {
359
+ throw new VisionToolkitError('config', `${label}.t1Seconds must be an integer between 1 and ${MAX_TIMEOUT_SECONDS}`)
360
+ }
361
+ const t2Seconds = input.t2Seconds ?? 90
362
+ if (!Number.isInteger(t2Seconds) || t2Seconds < 1 || t2Seconds > MAX_TIMEOUT_SECONDS) {
363
+ throw new VisionToolkitError('config', `${label}.t2Seconds must be an integer between 1 and ${MAX_TIMEOUT_SECONDS}`)
336
364
  }
337
365
  const maxImageBytes = input.maxImageBytes ?? defaults.maxImageBytes
338
366
  if (!Number.isInteger(maxImageBytes) || maxImageBytes < 1024 || maxImageBytes > MAX_IMAGE_BYTES) {
@@ -362,7 +390,8 @@ function resolveProvider(
362
390
  protocol,
363
391
  anthropicThinking,
364
392
  userAgent,
365
- timeoutMs,
393
+ t1Seconds,
394
+ t2Seconds,
366
395
  maxImageBytes,
367
396
  maxImagePixels,
368
397
  concurrency,
@@ -384,9 +413,17 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
384
413
  if (language !== 'zh' && language !== 'en') {
385
414
  throw new VisionToolkitError('config', 'language must be "zh" or "en"')
386
415
  }
387
- const timeoutMs = config.timeoutMs ?? 30000
388
- if (!Number.isInteger(timeoutMs) || timeoutMs < 1000 || timeoutMs > MAX_TIMEOUT_MS) {
389
- throw new VisionToolkitError('config', `timeoutMs must be an integer between 1000 and ${MAX_TIMEOUT_MS}`)
416
+ const hardTimeoutSeconds = config.hardTimeoutSeconds ?? 180
417
+ if (!Number.isInteger(hardTimeoutSeconds) || hardTimeoutSeconds < 1 || hardTimeoutSeconds > MAX_TIMEOUT_SECONDS) {
418
+ throw new VisionToolkitError('config', `hardTimeoutSeconds must be an integer between 1 and ${MAX_TIMEOUT_SECONDS}`)
419
+ }
420
+ const sessionMaxConcurrency = config.sessionMaxConcurrency ?? 6
421
+ if (!Number.isInteger(sessionMaxConcurrency) || sessionMaxConcurrency < 1 || sessionMaxConcurrency > MAX_CONCURRENCY) {
422
+ throw new VisionToolkitError('config', `sessionMaxConcurrency must be an integer between 1 and ${MAX_CONCURRENCY}`)
423
+ }
424
+ const minAvailableSeconds = config.minAvailableSeconds ?? 20
425
+ if (!Number.isInteger(minAvailableSeconds) || minAvailableSeconds < 1 || minAvailableSeconds > MAX_TIMEOUT_SECONDS) {
426
+ throw new VisionToolkitError('config', `minAvailableSeconds must be an integer between 1 and ${MAX_TIMEOUT_SECONDS}`)
390
427
  }
391
428
  const maxImageBytes = config.maxImageBytes ?? 4194304
392
429
  if (!Number.isInteger(maxImageBytes) || maxImageBytes < 1024 || maxImageBytes > MAX_IMAGE_BYTES) {
@@ -423,7 +460,7 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
423
460
  const variantProviders = (imageInputVariants.providers ?? [])
424
461
  .map(provider => provider.trim())
425
462
  .filter(provider => provider.length > 0)
426
- const providerDefaults: ProviderDefaults = { timeoutMs, maxImageBytes, maxImagePixels, concurrency }
463
+ const providerDefaults: ProviderDefaults = { maxImageBytes, maxImagePixels, concurrency }
427
464
  const configuredProviders = config.providers ?? []
428
465
  if (configuredProviders.length > MAX_PROVIDERS) {
429
466
  throw new VisionToolkitError('config', `providers must have at most ${MAX_PROVIDERS} entries`)
@@ -444,7 +481,9 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
444
481
  },
445
482
  providers,
446
483
  language,
447
- timeoutMs,
484
+ hardTimeoutSeconds,
485
+ sessionMaxConcurrency,
486
+ minAvailableSeconds,
448
487
  maxImageBytes,
449
488
  maxImagePixels,
450
489
  concurrency,
package/src/errors.ts CHANGED
@@ -16,6 +16,7 @@ export const VISION_TOOLKIT_ERROR_CODES = [
16
16
  'timeout',
17
17
  'cancelled',
18
18
  'path',
19
+ 'rate_limit',
19
20
  ] as const
20
21
 
21
22
  /** Stable machine-readable error category. */
@@ -108,7 +108,9 @@ export function evidenceRuntimeFingerprint(
108
108
  attempts: provider.attempts,
109
109
  })),
110
110
  language: config.language,
111
- timeoutMs: config.timeoutMs,
111
+ hardTimeoutSeconds: config.hardTimeoutSeconds,
112
+ sessionMaxConcurrency: config.sessionMaxConcurrency,
113
+ minAvailableSeconds: config.minAvailableSeconds,
112
114
  concurrency: config.concurrency,
113
115
  maxImageBytes: config.maxImageBytes,
114
116
  maxImagePixels: config.maxImagePixels,