@mengruo/dsh-vision-toolkit 0.1.2 → 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
@@ -77,8 +77,10 @@ export interface VisionProviderConfig {
77
77
  anthropicThinking?: 'omit' | 'disabled' | 'adaptive'
78
78
  /** Outbound User-Agent for provider requests and connection tests. */
79
79
  userAgent?: string
80
- /** Per-provider single request timeout in milliseconds. */
81
- 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
82
84
  /** Per-provider maximum input image bytes; larger images skip to a later provider or are compressed. */
83
85
  maxImageBytes?: number
84
86
  /** Per-provider maximum decoded pixel count per input image. */
@@ -109,13 +111,17 @@ export interface VisionToolkitConfig {
109
111
  providers?: VisionProviderConfig[]
110
112
  /** Vision output language (`zh` or `en`). */
111
113
  language?: 'zh' | 'en'
112
- /** Single remote/upstream call budget in milliseconds. */
113
- 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
114
120
  /** Maximum input image size in bytes; larger images are auto-compressed (lossless first). */
115
121
  maxImageBytes?: number
116
122
  /** Maximum decoded pixel count per input image; larger images are auto-downscaled to fit. */
117
123
  maxImagePixels?: number
118
- /** In-flight tool execution cap per session. */
124
+ /** Default per-model in-flight request cap inherited by providers that do not set their own. */
119
125
  concurrency?: number
120
126
  runtime?: {
121
127
  /** `managed` uses the packaged snapshot and isolated venv; `external` uses a clean pinned checkout. */
@@ -179,14 +185,17 @@ export const Config: Schema<VisionToolkitConfig> = z.object({
179
185
  protocol: z.union(['openai', 'anthropic'] as const).default('openai'),
180
186
  anthropicThinking: z.union(['omit', 'disabled', 'adaptive'] as const).default('omit'),
181
187
  userAgent: z.string(),
182
- timeoutMs: z.number(),
188
+ t1Seconds: z.number(),
189
+ t2Seconds: z.number(),
183
190
  maxImageBytes: z.number(),
184
191
  maxImagePixels: z.number(),
185
192
  concurrency: z.number(),
186
193
  attempts: z.number(),
187
194
  })).default([]),
188
195
  language: z.union(['zh', 'en'] as const).default('zh'),
189
- timeoutMs: z.number().default(30000),
196
+ hardTimeoutSeconds: z.number().default(180),
197
+ sessionMaxConcurrency: z.number().default(6),
198
+ minAvailableSeconds: z.number().default(20),
190
199
  maxImageBytes: z.number().default(4194304),
191
200
  maxImagePixels: z.number().default(20000000),
192
201
  concurrency: z.number().default(4),
@@ -216,7 +225,8 @@ export interface ResolvedProvider {
216
225
  protocol: 'openai' | 'anthropic'
217
226
  anthropicThinking: 'omit' | 'disabled' | 'adaptive'
218
227
  userAgent: string
219
- timeoutMs: number
228
+ t1Seconds: number
229
+ t2Seconds: number
220
230
  maxImageBytes: number
221
231
  maxImagePixels: number
222
232
  concurrency: number
@@ -236,7 +246,9 @@ export interface ResolvedVisionToolkitConfig {
236
246
  /** Ordered failover pool; array order is the priority, highest first. */
237
247
  providers: ResolvedProvider[]
238
248
  language: 'zh' | 'en'
239
- timeoutMs: number
249
+ hardTimeoutSeconds: number
250
+ sessionMaxConcurrency: number
251
+ minAvailableSeconds: number
240
252
  maxImageBytes: number
241
253
  maxImagePixels: number
242
254
  concurrency: number
@@ -254,7 +266,7 @@ export interface ResolvedVisionToolkitConfig {
254
266
  }
255
267
  }
256
268
 
257
- const MAX_TIMEOUT_MS = 600000
269
+ const MAX_TIMEOUT_SECONDS = 600
258
270
  const MAX_IMAGE_BYTES = 268435456
259
271
  const MAX_IMAGE_PIXELS = 268435456
260
272
  const MAX_CONCURRENCY = 16
@@ -264,7 +276,6 @@ const DEFAULT_PROVIDER_ATTEMPTS = 3
264
276
 
265
277
  /** Global limits a provider inherits when it does not set its own. */
266
278
  interface ProviderDefaults {
267
- timeoutMs: number
268
279
  maxImageBytes: number
269
280
  maxImagePixels: number
270
281
  concurrency: number
@@ -343,9 +354,13 @@ function resolveProvider(
343
354
  if (userAgent.length === 0) {
344
355
  throw new VisionToolkitError('config', `${label}.userAgent must not be empty`)
345
356
  }
346
- const timeoutMs = input.timeoutMs ?? defaults.timeoutMs
347
- if (!Number.isInteger(timeoutMs) || timeoutMs < 1000 || timeoutMs > MAX_TIMEOUT_MS) {
348
- 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}`)
349
364
  }
350
365
  const maxImageBytes = input.maxImageBytes ?? defaults.maxImageBytes
351
366
  if (!Number.isInteger(maxImageBytes) || maxImageBytes < 1024 || maxImageBytes > MAX_IMAGE_BYTES) {
@@ -375,7 +390,8 @@ function resolveProvider(
375
390
  protocol,
376
391
  anthropicThinking,
377
392
  userAgent,
378
- timeoutMs,
393
+ t1Seconds,
394
+ t2Seconds,
379
395
  maxImageBytes,
380
396
  maxImagePixels,
381
397
  concurrency,
@@ -397,9 +413,17 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
397
413
  if (language !== 'zh' && language !== 'en') {
398
414
  throw new VisionToolkitError('config', 'language must be "zh" or "en"')
399
415
  }
400
- const timeoutMs = config.timeoutMs ?? 30000
401
- if (!Number.isInteger(timeoutMs) || timeoutMs < 1000 || timeoutMs > MAX_TIMEOUT_MS) {
402
- 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}`)
403
427
  }
404
428
  const maxImageBytes = config.maxImageBytes ?? 4194304
405
429
  if (!Number.isInteger(maxImageBytes) || maxImageBytes < 1024 || maxImageBytes > MAX_IMAGE_BYTES) {
@@ -436,7 +460,7 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
436
460
  const variantProviders = (imageInputVariants.providers ?? [])
437
461
  .map(provider => provider.trim())
438
462
  .filter(provider => provider.length > 0)
439
- const providerDefaults: ProviderDefaults = { timeoutMs, maxImageBytes, maxImagePixels, concurrency }
463
+ const providerDefaults: ProviderDefaults = { maxImageBytes, maxImagePixels, concurrency }
440
464
  const configuredProviders = config.providers ?? []
441
465
  if (configuredProviders.length > MAX_PROVIDERS) {
442
466
  throw new VisionToolkitError('config', `providers must have at most ${MAX_PROVIDERS} entries`)
@@ -457,7 +481,9 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
457
481
  },
458
482
  providers,
459
483
  language,
460
- timeoutMs,
484
+ hardTimeoutSeconds,
485
+ sessionMaxConcurrency,
486
+ minAvailableSeconds,
461
487
  maxImageBytes,
462
488
  maxImagePixels,
463
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,