@mengruo/dsh-vision-toolkit 0.1.2 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.i18n.yaml +2 -2
- package/README.md +4 -0
- package/README.zh.md +4 -0
- package/docs/requirements-traceability/README.i18n.yaml +2 -2
- package/docs/requirements-traceability/README.md +1 -1
- package/docs/requirements-traceability/README.zh.md +1 -1
- package/lib/artifact-access.js +20 -2
- package/lib/artifact-access.js.map +1 -1
- package/lib/client.js +59 -19
- package/lib/client.js.map +1 -1
- package/lib/config.js +64 -12
- package/lib/config.js.map +1 -1
- package/lib/errors.js +25 -1
- package/lib/errors.js.map +1 -1
- package/lib/evidence-cache.js +5 -2
- package/lib/evidence-cache.js.map +1 -1
- package/lib/exposure.js +14 -1
- package/lib/exposure.js.map +1 -1
- package/lib/image-input-variants.js +22 -12
- package/lib/image-input-variants.js.map +1 -1
- package/lib/index.js +53 -6
- package/lib/index.js.map +1 -1
- package/lib/paste-images.js +67 -19
- package/lib/paste-images.js.map +1 -1
- package/lib/paths.js +214 -28
- package/lib/paths.js.map +1 -1
- package/lib/runtime-manager.js +76 -10
- package/lib/runtime-manager.js.map +1 -1
- package/lib/runtime.js +389 -104
- package/lib/runtime.js.map +1 -1
- package/lib/storage-history.js +154 -0
- package/lib/storage-history.js.map +1 -0
- package/lib/tools.js +68 -35
- package/lib/tools.js.map +1 -1
- package/lib/types/artifact-access.d.ts.map +1 -1
- package/lib/types/client/index.d.ts +23 -5
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/paste-images.d.ts +2 -0
- package/lib/types/client/paste-images.d.ts.map +1 -1
- package/lib/types/config.d.ts +38 -7
- package/lib/types/config.d.ts.map +1 -1
- package/lib/types/errors.d.ts +18 -2
- package/lib/types/errors.d.ts.map +1 -1
- package/lib/types/evidence-cache.d.ts +1 -1
- package/lib/types/evidence-cache.d.ts.map +1 -1
- package/lib/types/exposure.d.ts.map +1 -1
- package/lib/types/image-input-variants.d.ts +5 -3
- package/lib/types/image-input-variants.d.ts.map +1 -1
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/paste-images.d.ts +12 -4
- package/lib/types/paste-images.d.ts.map +1 -1
- package/lib/types/paths.d.ts +31 -5
- package/lib/types/paths.d.ts.map +1 -1
- package/lib/types/runtime-manager.d.ts +28 -4
- package/lib/types/runtime-manager.d.ts.map +1 -1
- package/lib/types/runtime.d.ts +75 -12
- package/lib/types/runtime.d.ts.map +1 -1
- package/lib/types/storage-history.d.ts +63 -0
- package/lib/types/storage-history.d.ts.map +1 -0
- package/lib/types/tools.d.ts +1 -0
- package/lib/types/tools.d.ts.map +1 -1
- package/lib/types/upstream.d.ts.map +1 -1
- package/lib/types/web.d.ts.map +1 -1
- package/lib/upstream.js +31 -8
- package/lib/upstream.js.map +1 -1
- package/lib/web.js +9 -3
- package/lib/web.js.map +1 -1
- package/package.json +1 -1
- package/src/artifact-access.ts +22 -2
- package/src/client/index.tsx +72 -20
- package/src/client/paste-images.tsx +14 -4
- package/src/config.ts +107 -20
- package/src/errors.ts +25 -1
- package/src/evidence-cache.ts +5 -2
- package/src/exposure.ts +16 -2
- package/src/image-input-variants.ts +21 -6
- package/src/index.ts +65 -6
- package/src/paste-images.ts +81 -19
- package/src/paths.ts +249 -28
- package/src/runtime-manager.ts +93 -10
- package/src/runtime.ts +431 -115
- package/src/storage-history.ts +172 -0
- package/src/tools.ts +78 -46
- package/src/upstream.ts +33 -8
- package/src/web.ts +9 -2
package/src/client/index.tsx
CHANGED
|
@@ -71,15 +71,28 @@ const en = {
|
|
|
71
71
|
anthropicThinkingHint: 'omit has the broadest compatibility. Use disabled or adaptive only when the selected model documents that mode; restore omit first after HTTP 400.',
|
|
72
72
|
userAgent: 'User-Agent',
|
|
73
73
|
language: 'Output language',
|
|
74
|
-
limits: '
|
|
74
|
+
limits: 'Timeout and concurrency',
|
|
75
75
|
timeout: 'Request timeout (ms)',
|
|
76
|
+
t1: 't1 — single-request hedge (s)',
|
|
77
|
+
t1Hint: 'When a single request exceeds t1, keep it running and start the next model in parallel.',
|
|
78
|
+
t2: 't2 — cumulative cutoff (s)',
|
|
79
|
+
t2Hint: 'Terminate the model once its accumulated request time reaches t2.',
|
|
80
|
+
hardTimeout: 'Global hard timeout (s)',
|
|
81
|
+
hardTimeoutHint: 'Wall-clock ceiling for one tool call; on timeout every request is cancelled.',
|
|
82
|
+
sessionConcurrency: 'Per-session max concurrency',
|
|
83
|
+
sessionConcurrencyHint: 'Maximum concurrent vision calls in one session; excess is rejected immediately.',
|
|
84
|
+
minAvailable: 'Minimum available time (s)',
|
|
85
|
+
minAvailableHint: 'No new request is issued once the remaining budget drops below this value.',
|
|
76
86
|
maxBytes: 'Maximum image bytes',
|
|
77
87
|
maxPixels: 'Maximum image pixels',
|
|
78
|
-
concurrency: '
|
|
88
|
+
concurrency: 'Concurrency (per model)',
|
|
79
89
|
runtime: 'Runtime',
|
|
80
90
|
runtimeMode: 'Runtime mode',
|
|
81
91
|
toolkitPath: 'Pinned checkout path',
|
|
82
92
|
python: 'Python override',
|
|
93
|
+
storage: 'Local files',
|
|
94
|
+
storageDir: 'Default save directory',
|
|
95
|
+
storageDirHint: 'Leave blank to keep .dsh-vision-toolkit in each workspace. On POSIX systems, set an absolute shared root such as /tmp/dsh-vision-toolkit to store artifacts, pasted images, and caches in an automatically generated per-user, workspace-specific child directory. Windows shared roots remain disabled until their ACLs can be verified safely.',
|
|
83
96
|
allowedDirs: 'Additional allowed directories',
|
|
84
97
|
allowedDirsHint: 'One path per line. The session workspace is always allowed.',
|
|
85
98
|
save: 'Save and apply',
|
|
@@ -102,7 +115,7 @@ const en = {
|
|
|
102
115
|
connectionHint: 'The API connection test only queries GET /models. The vision model test sends the bundled diagnostic image and verifies one real multimodal request.',
|
|
103
116
|
saveBeforeTesting: 'Save service changes before testing the connection.',
|
|
104
117
|
advanced: 'Advanced settings',
|
|
105
|
-
advancedHint: 'Credential name, provider compatibility, output language, resource limits, runtime source, Python, and additional readable directories.',
|
|
118
|
+
advancedHint: 'Credential name, provider compatibility, output language, resource limits, default save directory, runtime source, Python, and additional readable directories.',
|
|
106
119
|
imageInput: 'Image input',
|
|
107
120
|
hiddenVariants: 'Transparent variant routing',
|
|
108
121
|
hiddenVariantsLabel: 'Keep the original model names and enable images automatically',
|
|
@@ -274,15 +287,28 @@ const zh: Record<LocaleKey, string> = {
|
|
|
274
287
|
anthropicThinkingHint: 'omit 兼容性最好。仅当所选模型明确支持时使用 disabled 或 adaptive;遇到 HTTP 400 时先恢复 omit。',
|
|
275
288
|
userAgent: 'User-Agent',
|
|
276
289
|
language: '结果语言',
|
|
277
|
-
limits: '
|
|
290
|
+
limits: '超时与并发限制',
|
|
278
291
|
timeout: '单次请求超时(毫秒)',
|
|
292
|
+
t1: 't1(单次请求阈值,秒)',
|
|
293
|
+
t1Hint: '单次请求超过 t1 时,继续请求并并行调用下一个模型。',
|
|
294
|
+
t2: 't2(累计终止阈值,秒)',
|
|
295
|
+
t2Hint: '该模型累计请求时长达到 t2 时终止。',
|
|
296
|
+
hardTimeout: '全局硬超时(秒)',
|
|
297
|
+
hardTimeoutHint: '单次工具调用的墙钟上限,超时终止所有任务。',
|
|
298
|
+
sessionConcurrency: '单会话最大并发',
|
|
299
|
+
sessionConcurrencyHint: '同一会话最多同时运行的视觉任务数,超出直接返回错误。',
|
|
300
|
+
minAvailable: '最低可用时间(秒)',
|
|
301
|
+
minAvailableHint: '剩余时间额度低于该值时不再发起新请求。',
|
|
279
302
|
maxBytes: '单张图片大小上限(字节)',
|
|
280
303
|
maxPixels: '单张图片最大像素数',
|
|
281
|
-
concurrency: '
|
|
304
|
+
concurrency: '并发(每个模型)',
|
|
282
305
|
runtime: '工具运行环境',
|
|
283
306
|
runtimeMode: '环境来源',
|
|
284
307
|
toolkitPath: 'agent-vision-toolkit 目录',
|
|
285
308
|
python: 'Python 解释器(可选)',
|
|
309
|
+
storage: '本地文件',
|
|
310
|
+
storageDir: '默认保存目录',
|
|
311
|
+
storageDirHint: '留空时继续在各工作区创建 .dsh-vision-toolkit。在 POSIX 系统上填写绝对路径共享根目录(例如 /tmp/dsh-vision-toolkit)后,产物、粘贴图片和缓存会保存到自动生成的当前用户、工作区专属子目录中。Windows 共享目录会保持禁用,直到能够安全校验 ACL 权限。',
|
|
286
312
|
allowedDirs: '允许读取的其他目录',
|
|
287
313
|
allowedDirsHint: '每行填写一个目录。当前会话的工作目录始终可以读取,无需重复填写。',
|
|
288
314
|
save: '保存设置',
|
|
@@ -305,7 +331,7 @@ const zh: Record<LocaleKey, string> = {
|
|
|
305
331
|
connectionHint: '“测试 API 连接”只请求 GET /models;“测试视觉模型”会发送插件自带的诊断图片,验证一次真实多模态调用。',
|
|
306
332
|
saveBeforeTesting: '修改服务配置后,请先保存,再执行 API 或视觉模型测试。',
|
|
307
333
|
advanced: '高级设置',
|
|
308
|
-
advancedHint: '
|
|
334
|
+
advancedHint: '凭据名称、服务兼容参数、结果语言、资源限制、默认保存目录、运行环境来源、Python 和额外可读目录。一般无需修改。',
|
|
309
335
|
imageInput: '图片输入',
|
|
310
336
|
hiddenVariants: '透明变体路由',
|
|
311
337
|
hiddenVariantsLabel: '保留原模型名并自动启用图片能力',
|
|
@@ -501,7 +527,8 @@ interface ProviderValue {
|
|
|
501
527
|
protocol?: 'openai' | 'anthropic'
|
|
502
528
|
anthropicThinking?: 'omit' | 'disabled' | 'adaptive'
|
|
503
529
|
userAgent?: string
|
|
504
|
-
|
|
530
|
+
t1Seconds?: number
|
|
531
|
+
t2Seconds?: number
|
|
505
532
|
maxImageBytes?: number
|
|
506
533
|
maxImagePixels?: number
|
|
507
534
|
concurrency?: number
|
|
@@ -519,11 +546,15 @@ interface SettingsValue {
|
|
|
519
546
|
}
|
|
520
547
|
providers?: ProviderValue[]
|
|
521
548
|
language?: 'zh' | 'en'
|
|
522
|
-
|
|
549
|
+
hardTimeoutSeconds?: number
|
|
550
|
+
sessionMaxConcurrency?: number
|
|
551
|
+
minAvailableSeconds?: number
|
|
523
552
|
maxImageBytes?: number
|
|
524
553
|
maxImagePixels?: number
|
|
525
554
|
concurrency?: number
|
|
526
555
|
runtime?: { mode?: 'managed' | 'external'; agentVisionToolkitPath?: string; python?: string }
|
|
556
|
+
storageDir?: string
|
|
557
|
+
storageHistory?: string[]
|
|
527
558
|
allowedDirs?: string[]
|
|
528
559
|
imageInputVariants?: {
|
|
529
560
|
enabled?: boolean
|
|
@@ -1140,7 +1171,8 @@ interface ProviderDraft {
|
|
|
1140
1171
|
protocol: 'openai' | 'anthropic'
|
|
1141
1172
|
anthropicThinking: 'omit' | 'disabled' | 'adaptive'
|
|
1142
1173
|
userAgent: string
|
|
1143
|
-
|
|
1174
|
+
t1Seconds: string
|
|
1175
|
+
t2Seconds: string
|
|
1144
1176
|
maxImageBytes: string
|
|
1145
1177
|
maxImagePixels: string
|
|
1146
1178
|
concurrency: string
|
|
@@ -1150,13 +1182,16 @@ interface ProviderDraft {
|
|
|
1150
1182
|
interface Draft {
|
|
1151
1183
|
providers: ProviderDraft[]
|
|
1152
1184
|
language: 'zh' | 'en'
|
|
1153
|
-
|
|
1185
|
+
hardTimeoutSeconds: string
|
|
1186
|
+
sessionMaxConcurrency: string
|
|
1187
|
+
minAvailableSeconds: string
|
|
1154
1188
|
maxImageBytes: string
|
|
1155
1189
|
maxImagePixels: string
|
|
1156
1190
|
concurrency: string
|
|
1157
1191
|
runtimeMode: 'managed' | 'external'
|
|
1158
1192
|
toolkitPath: string
|
|
1159
1193
|
python: string
|
|
1194
|
+
storageDir: string
|
|
1160
1195
|
allowedDirs: string
|
|
1161
1196
|
hiddenVariants: boolean
|
|
1162
1197
|
variantEnabled: boolean
|
|
@@ -1179,7 +1214,6 @@ function autoCredentialName(id: string): string {
|
|
|
1179
1214
|
}
|
|
1180
1215
|
|
|
1181
1216
|
interface ProviderDefaults {
|
|
1182
|
-
timeoutMs: number
|
|
1183
1217
|
maxImageBytes: number
|
|
1184
1218
|
maxImagePixels: number
|
|
1185
1219
|
concurrency: number
|
|
@@ -1197,7 +1231,8 @@ function emptyProviderDraft(defaults: ProviderDefaults): ProviderDraft {
|
|
|
1197
1231
|
protocol: 'openai',
|
|
1198
1232
|
anthropicThinking: 'omit',
|
|
1199
1233
|
userAgent: DEFAULT_USER_AGENT,
|
|
1200
|
-
|
|
1234
|
+
t1Seconds: '90',
|
|
1235
|
+
t2Seconds: '90',
|
|
1201
1236
|
maxImageBytes: String(defaults.maxImageBytes),
|
|
1202
1237
|
maxImagePixels: String(defaults.maxImagePixels),
|
|
1203
1238
|
concurrency: String(defaults.concurrency),
|
|
@@ -1218,7 +1253,8 @@ function providerDraftOf(value: ProviderValue | undefined, fallback: ProviderVal
|
|
|
1218
1253
|
protocol: source?.protocol ?? 'openai',
|
|
1219
1254
|
anthropicThinking: source?.anthropicThinking ?? 'omit',
|
|
1220
1255
|
userAgent: source?.userAgent ?? DEFAULT_USER_AGENT,
|
|
1221
|
-
|
|
1256
|
+
t1Seconds: String(source?.t1Seconds ?? 90),
|
|
1257
|
+
t2Seconds: String(source?.t2Seconds ?? 90),
|
|
1222
1258
|
maxImageBytes: String(source?.maxImageBytes ?? defaults.maxImageBytes),
|
|
1223
1259
|
maxImagePixels: String(source?.maxImagePixels ?? defaults.maxImagePixels),
|
|
1224
1260
|
concurrency: String(source?.concurrency ?? defaults.concurrency),
|
|
@@ -1228,7 +1264,6 @@ function providerDraftOf(value: ProviderValue | undefined, fallback: ProviderVal
|
|
|
1228
1264
|
|
|
1229
1265
|
function draftOf(value: SettingsValue): Draft {
|
|
1230
1266
|
const globalDefaults: ProviderDefaults = {
|
|
1231
|
-
timeoutMs: value.timeoutMs ?? 30000,
|
|
1232
1267
|
maxImageBytes: value.maxImageBytes ?? 4194304,
|
|
1233
1268
|
maxImagePixels: value.maxImagePixels ?? 20000000,
|
|
1234
1269
|
concurrency: value.concurrency ?? 4,
|
|
@@ -1240,13 +1275,16 @@ function draftOf(value: SettingsValue): Draft {
|
|
|
1240
1275
|
return {
|
|
1241
1276
|
providers,
|
|
1242
1277
|
language: value.language ?? 'zh',
|
|
1243
|
-
|
|
1278
|
+
hardTimeoutSeconds: String(value.hardTimeoutSeconds ?? 180),
|
|
1279
|
+
sessionMaxConcurrency: String(value.sessionMaxConcurrency ?? 6),
|
|
1280
|
+
minAvailableSeconds: String(value.minAvailableSeconds ?? 20),
|
|
1244
1281
|
maxImageBytes: String(globalDefaults.maxImageBytes),
|
|
1245
1282
|
maxImagePixels: String(globalDefaults.maxImagePixels),
|
|
1246
1283
|
concurrency: String(globalDefaults.concurrency),
|
|
1247
1284
|
runtimeMode: value.runtime?.mode ?? 'managed',
|
|
1248
1285
|
toolkitPath: value.runtime?.agentVisionToolkitPath ?? '',
|
|
1249
1286
|
python: value.runtime?.python ?? '',
|
|
1287
|
+
storageDir: value.storageDir ?? '',
|
|
1250
1288
|
allowedDirs: (value.allowedDirs ?? []).join('\n'),
|
|
1251
1289
|
hiddenVariants: value.imageInputVariants?.hidden ?? true,
|
|
1252
1290
|
variantEnabled: value.imageInputVariants?.enabled ?? true,
|
|
@@ -1282,7 +1320,8 @@ function valueOf(draft: Draft, t: Translate): SettingsValue {
|
|
|
1282
1320
|
protocol: provider.protocol,
|
|
1283
1321
|
anthropicThinking: provider.anthropicThinking,
|
|
1284
1322
|
...(provider.userAgent.trim() === DEFAULT_USER_AGENT ? {} : { userAgent: provider.userAgent.trim() }),
|
|
1285
|
-
...(provider.
|
|
1323
|
+
...(provider.t1Seconds.trim().length === 0 ? {} : { t1Seconds: positiveInteger(provider.t1Seconds, t('t1'), t) }),
|
|
1324
|
+
...(provider.t2Seconds.trim().length === 0 ? {} : { t2Seconds: positiveInteger(provider.t2Seconds, t('t2'), t) }),
|
|
1286
1325
|
...(provider.maxImageBytes.trim().length === 0 ? {} : { maxImageBytes: positiveInteger(provider.maxImageBytes, t('maxBytes'), t) }),
|
|
1287
1326
|
...(provider.maxImagePixels.trim().length === 0 ? {} : { maxImagePixels: positiveInteger(provider.maxImagePixels, t('maxPixels'), t) }),
|
|
1288
1327
|
...(provider.concurrency.trim().length === 0 ? {} : { concurrency: positiveInteger(provider.concurrency, t('concurrency'), t) }),
|
|
@@ -1300,7 +1339,9 @@ function valueOf(draft: Draft, t: Translate): SettingsValue {
|
|
|
1300
1339
|
},
|
|
1301
1340
|
providers,
|
|
1302
1341
|
language: draft.language,
|
|
1303
|
-
|
|
1342
|
+
hardTimeoutSeconds: positiveInteger(draft.hardTimeoutSeconds, t('hardTimeout'), t),
|
|
1343
|
+
sessionMaxConcurrency: positiveInteger(draft.sessionMaxConcurrency, t('sessionConcurrency'), t),
|
|
1344
|
+
minAvailableSeconds: positiveInteger(draft.minAvailableSeconds, t('minAvailable'), t),
|
|
1304
1345
|
maxImageBytes: positiveInteger(draft.maxImageBytes, t('maxBytes'), t),
|
|
1305
1346
|
maxImagePixels: positiveInteger(draft.maxImagePixels, t('maxPixels'), t),
|
|
1306
1347
|
concurrency: positiveInteger(draft.concurrency, t('concurrency'), t),
|
|
@@ -1309,6 +1350,7 @@ function valueOf(draft: Draft, t: Translate): SettingsValue {
|
|
|
1309
1350
|
...(draft.runtimeMode === 'external' ? { agentVisionToolkitPath: draft.toolkitPath.trim() } : {}),
|
|
1310
1351
|
...(draft.python.trim().length === 0 ? {} : { python: draft.python.trim() }),
|
|
1311
1352
|
},
|
|
1353
|
+
...(draft.storageDir.trim().length === 0 ? {} : { storageDir: draft.storageDir.trim() }),
|
|
1312
1354
|
allowedDirs: draft.allowedDirs.split(/\r?\n/).map(entry => entry.trim()).filter(Boolean),
|
|
1313
1355
|
imageInputVariants: {
|
|
1314
1356
|
...(draft.variantEnabled ? {} : { enabled: false }),
|
|
@@ -1488,7 +1530,6 @@ function LoadedSettings({ controller, t }: SettingsInjected) {
|
|
|
1488
1530
|
setDraft(current => current === undefined ? current : {
|
|
1489
1531
|
...current,
|
|
1490
1532
|
providers: [...current.providers, emptyProviderDraft({
|
|
1491
|
-
timeoutMs: Number(current.timeoutMs) || 30000,
|
|
1492
1533
|
maxImageBytes: Number(current.maxImageBytes) || 4194304,
|
|
1493
1534
|
maxImagePixels: Number(current.maxImagePixels) || 20000000,
|
|
1494
1535
|
concurrency: Number(current.concurrency) || 4,
|
|
@@ -1626,7 +1667,8 @@ function LoadedSettings({ controller, t }: SettingsInjected) {
|
|
|
1626
1667
|
<Field label={t('protocol')}><select aria-label={t('protocol')} disabled={!snapshot.writable || busy} value={selectedProvider.protocol} onChange={(event) => { updateProvider(selectedIndex, 'protocol', event.target.value as 'openai' | 'anthropic') }}><option value="openai">OpenAI Chat Completions</option><option value="anthropic">Anthropic Messages</option></select></Field>
|
|
1627
1668
|
{selectedProvider.protocol === 'anthropic' ? <Field label={t('anthropicThinking')} hint={t('anthropicThinkingHint')}><select aria-label={t('anthropicThinking')} value={selectedProvider.anthropicThinking} onChange={(event) => { updateProvider(selectedIndex, 'anthropicThinking', event.target.value as 'omit' | 'disabled' | 'adaptive') }}><option value="omit">omit (widest compatibility)</option><option value="disabled">disabled (model support required)</option><option value="adaptive">adaptive (model support required)</option></select></Field> : null}
|
|
1628
1669
|
<Field label={t('userAgent')}><Input aria-label={t('userAgent')} value={selectedProvider.userAgent} onChange={(event) => { updateProvider(selectedIndex, 'userAgent', event.target.value) }} /></Field>
|
|
1629
|
-
<Field label={t('
|
|
1670
|
+
<Field label={t('t1')} hint={t('t1Hint')}><Input aria-label={t('t1')} inputMode="numeric" value={selectedProvider.t1Seconds} onChange={(event) => { updateProvider(selectedIndex, 't1Seconds', event.target.value) }} /></Field>
|
|
1671
|
+
<Field label={t('t2')} hint={t('t2Hint')}><Input aria-label={t('t2')} inputMode="numeric" value={selectedProvider.t2Seconds} onChange={(event) => { updateProvider(selectedIndex, 't2Seconds', event.target.value) }} /></Field>
|
|
1630
1672
|
<Field label={t('maxBytes')}><Input aria-label={t('maxBytes')} inputMode="numeric" value={selectedProvider.maxImageBytes} onChange={(event) => { updateProvider(selectedIndex, 'maxImageBytes', event.target.value) }} /></Field>
|
|
1631
1673
|
<Field label={t('maxPixels')}><Input aria-label={t('maxPixels')} inputMode="numeric" value={selectedProvider.maxImagePixels} onChange={(event) => { updateProvider(selectedIndex, 'maxImagePixels', event.target.value) }} /></Field>
|
|
1632
1674
|
<Field label={t('concurrency')}><Input aria-label={t('concurrency')} inputMode="numeric" value={selectedProvider.concurrency} onChange={(event) => { updateProvider(selectedIndex, 'concurrency', event.target.value) }} /></Field>
|
|
@@ -1703,13 +1745,23 @@ function LoadedSettings({ controller, t }: SettingsInjected) {
|
|
|
1703
1745
|
<Field label={t('language')}><select value={draft.language} onChange={(event) => { update('language', event.target.value as 'zh' | 'en') }}><option value="zh">中文</option><option value="en">English</option></select></Field>
|
|
1704
1746
|
</div></section>
|
|
1705
1747
|
|
|
1748
|
+
<section className="dvt-panel"><div className="dvt-panel-title"><h3>{t('limits')}</h3></div><div className="dvt-form-grid">
|
|
1749
|
+
<Field label={t('hardTimeout')} hint={t('hardTimeoutHint')}><Input aria-label={t('hardTimeout')} inputMode="numeric" value={draft.hardTimeoutSeconds} onChange={(event) => { update('hardTimeoutSeconds', event.target.value) }} /></Field>
|
|
1750
|
+
<Field label={t('sessionConcurrency')} hint={t('sessionConcurrencyHint')}><Input aria-label={t('sessionConcurrency')} inputMode="numeric" value={draft.sessionMaxConcurrency} onChange={(event) => { update('sessionMaxConcurrency', event.target.value) }} /></Field>
|
|
1751
|
+
<Field label={t('minAvailable')} hint={t('minAvailableHint')}><Input aria-label={t('minAvailable')} inputMode="numeric" value={draft.minAvailableSeconds} onChange={(event) => { update('minAvailableSeconds', event.target.value) }} /></Field>
|
|
1752
|
+
</div></section>
|
|
1753
|
+
|
|
1706
1754
|
<section className="dvt-panel"><div className="dvt-panel-title"><h3>{t('imageInput')}</h3></div><label className="dvt-check"><input type="checkbox" checked={draft.hiddenVariants} disabled={!snapshot.writable || busy} onChange={(event) => { update('hiddenVariants', event.target.checked) }} /><span>{t('hiddenVariantsLabel')}</span><small>{t('hiddenVariantsHint')}</small></label></section>
|
|
1707
1755
|
|
|
1756
|
+
<section className="dvt-panel"><div className="dvt-panel-title"><h3>{t('storage')}</h3></div><div className="dvt-form-grid">
|
|
1757
|
+
<Field label={t('storageDir')} hint={t('storageDirHint')}><Input aria-label={t('storageDir')} placeholder="/tmp/dsh-vision-toolkit" value={draft.storageDir} onChange={(event) => { update('storageDir', event.target.value) }} /></Field>
|
|
1758
|
+
<Field label={t('allowedDirs')} hint={t('allowedDirsHint')}><textarea rows={3} value={draft.allowedDirs} onChange={(event) => { update('allowedDirs', event.target.value) }} /></Field>
|
|
1759
|
+
</div></section>
|
|
1760
|
+
|
|
1708
1761
|
<section className="dvt-panel"><div className="dvt-panel-title"><h3>{t('runtime')}</h3><span className={`dvt-badge ${snapshot.runtime.ready ? 'ok' : 'error'}`}>{snapshot.runtime.ready ? snapshot.runtime.upstream?.source === 'managed' ? t('runtimeManaged') : snapshot.runtime.upstream?.source === 'external' ? t('runtimeExternal') : t('runtimeReady') : t('runtimeUnavailable')}</span></div><div className="dvt-form-grid">
|
|
1709
1762
|
<Field label={t('runtimeMode')}><select value={draft.runtimeMode} onChange={(event) => { update('runtimeMode', event.target.value as 'managed' | 'external') }}><option value="managed">{t('runtimeManaged')}</option><option value="external">{t('runtimeExternal')}</option></select></Field>
|
|
1710
1763
|
{draft.runtimeMode === 'external' ? <Field label={t('toolkitPath')}><Input value={draft.toolkitPath} onChange={(event) => { update('toolkitPath', event.target.value) }} /></Field> : null}
|
|
1711
1764
|
<Field label={t('python')}><Input placeholder="python3" value={draft.python} onChange={(event) => { update('python', event.target.value) }} /></Field>
|
|
1712
|
-
<Field label={t('allowedDirs')} hint={t('allowedDirsHint')}><textarea rows={3} value={draft.allowedDirs} onChange={(event) => { update('allowedDirs', event.target.value) }} /></Field>
|
|
1713
1765
|
</div>
|
|
1714
1766
|
{snapshot.runtime.upstream === undefined ? null : <div className="dvt-runtime-facts"><code>{snapshot.runtime.upstream.path}</code><code>{snapshot.runtime.upstream.python} · {snapshot.runtime.upstream.pythonVersion}</code><code>{snapshot.runtime.upstream.runtimeHome}</code></div>}
|
|
1715
1767
|
</section>
|
|
@@ -58,6 +58,8 @@ interface PasteOccurrence {
|
|
|
58
58
|
source: string
|
|
59
59
|
ref: string
|
|
60
60
|
offset: number
|
|
61
|
+
/** DSH rc.8+ stores the full @label text; older releases used one placeholder. */
|
|
62
|
+
length?: number
|
|
61
63
|
label: string
|
|
62
64
|
}
|
|
63
65
|
|
|
@@ -166,6 +168,10 @@ function pasteLabel(file: File, index: number): string {
|
|
|
166
168
|
return file.name.trim() || `clipboard-image-${index + 1}`
|
|
167
169
|
}
|
|
168
170
|
|
|
171
|
+
function occurrenceEnd(occurrence: PasteOccurrence): number {
|
|
172
|
+
return occurrence.offset + (occurrence.length ?? 1)
|
|
173
|
+
}
|
|
174
|
+
|
|
169
175
|
/** Owns browser File objects until DSH serializes the corresponding text references. */
|
|
170
176
|
export class PasteImageController {
|
|
171
177
|
private readonly records = new Map<string, PasteRecord>()
|
|
@@ -241,6 +247,7 @@ export class PasteImageController {
|
|
|
241
247
|
if (before !== '' && !/\s$/u.test(before)) cursor = this.insertText(input, ' ', cursor)
|
|
242
248
|
for (const [index, file] of files.entries()) {
|
|
243
249
|
const ref = id()
|
|
250
|
+
const label = pasteLabel(file, index)
|
|
244
251
|
const record: PasteRecord = { ref, file, batch, status: 'ready' }
|
|
245
252
|
batch.records.push(record)
|
|
246
253
|
this.records.set(ref, record)
|
|
@@ -248,11 +255,14 @@ export class PasteImageController {
|
|
|
248
255
|
const accepted = input.insertReference({
|
|
249
256
|
source: SOURCE,
|
|
250
257
|
ref,
|
|
251
|
-
label
|
|
252
|
-
clipboardText: `[pasted image: ${
|
|
258
|
+
label,
|
|
259
|
+
clipboardText: `[pasted image: ${label}]`,
|
|
253
260
|
}, { start: cursor, end: cursor, draftRev: snapshot.draftRev })
|
|
254
261
|
if (!accepted) throw new Error('The composer changed before pasted images could be inserted')
|
|
255
|
-
|
|
262
|
+
const inserted = input.state.getSnapshot().occurrences.find(occurrence =>
|
|
263
|
+
occurrence.source === SOURCE && occurrence.ref === ref)
|
|
264
|
+
if (inserted === undefined) throw new Error('The pasted image reference was not present after insertion')
|
|
265
|
+
cursor = occurrenceEnd(inserted)
|
|
256
266
|
const hasNext = index + 1 < files.length
|
|
257
267
|
const suffix = input.state.getSnapshot().draft.slice(cursor)
|
|
258
268
|
if (hasNext || (suffix !== '' && !/^\s/u.test(suffix))) cursor = this.insertText(input, ' ', cursor)
|
|
@@ -594,7 +604,7 @@ export class PasteImageController {
|
|
|
594
604
|
insertText: (text: string, span: { start: number; end: number; draftRev: number }) => boolean
|
|
595
605
|
}).insertText('', {
|
|
596
606
|
start: current.offset,
|
|
597
|
-
end: current
|
|
607
|
+
end: occurrenceEnd(current),
|
|
598
608
|
draftRev: snapshot.draftRev,
|
|
599
609
|
})
|
|
600
610
|
if (!accepted) return
|
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
|
-
/**
|
|
81
|
-
|
|
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
|
-
/**
|
|
113
|
-
|
|
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
|
-
/**
|
|
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. */
|
|
@@ -125,6 +131,14 @@ export interface VisionToolkitConfig {
|
|
|
125
131
|
/** Optional Python 3.11+ bootstrap/interpreter override. */
|
|
126
132
|
python?: string
|
|
127
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Optional shared storage root. When set, every workspace gets an isolated,
|
|
136
|
+
* automatically generated child directory below this root instead of writing
|
|
137
|
+
* `.dsh-vision-toolkit` into the workspace.
|
|
138
|
+
*/
|
|
139
|
+
storageDir?: string
|
|
140
|
+
/** Internal read-only history used to keep persisted paths valid after storage moves. */
|
|
141
|
+
storageHistory?: string[]
|
|
128
142
|
/** Extra directories (besides the workspace) inputs may come from. */
|
|
129
143
|
allowedDirs?: string[]
|
|
130
144
|
/**
|
|
@@ -179,14 +193,17 @@ export const Config: Schema<VisionToolkitConfig> = z.object({
|
|
|
179
193
|
protocol: z.union(['openai', 'anthropic'] as const).default('openai'),
|
|
180
194
|
anthropicThinking: z.union(['omit', 'disabled', 'adaptive'] as const).default('omit'),
|
|
181
195
|
userAgent: z.string(),
|
|
182
|
-
|
|
196
|
+
t1Seconds: z.number(),
|
|
197
|
+
t2Seconds: z.number(),
|
|
183
198
|
maxImageBytes: z.number(),
|
|
184
199
|
maxImagePixels: z.number(),
|
|
185
200
|
concurrency: z.number(),
|
|
186
201
|
attempts: z.number(),
|
|
187
202
|
})).default([]),
|
|
188
203
|
language: z.union(['zh', 'en'] as const).default('zh'),
|
|
189
|
-
|
|
204
|
+
hardTimeoutSeconds: z.number().default(180),
|
|
205
|
+
sessionMaxConcurrency: z.number().default(6),
|
|
206
|
+
minAvailableSeconds: z.number().default(20),
|
|
190
207
|
maxImageBytes: z.number().default(4194304),
|
|
191
208
|
maxImagePixels: z.number().default(20000000),
|
|
192
209
|
concurrency: z.number().default(4),
|
|
@@ -195,6 +212,8 @@ export const Config: Schema<VisionToolkitConfig> = z.object({
|
|
|
195
212
|
agentVisionToolkitPath: z.string(),
|
|
196
213
|
python: z.string(),
|
|
197
214
|
}),
|
|
215
|
+
storageDir: z.string(),
|
|
216
|
+
storageHistory: z.array(z.string()).default([]),
|
|
198
217
|
allowedDirs: z.array(z.string()).default([]),
|
|
199
218
|
imageInputVariants: z.object({
|
|
200
219
|
enabled: z.boolean().default(true),
|
|
@@ -216,7 +235,8 @@ export interface ResolvedProvider {
|
|
|
216
235
|
protocol: 'openai' | 'anthropic'
|
|
217
236
|
anthropicThinking: 'omit' | 'disabled' | 'adaptive'
|
|
218
237
|
userAgent: string
|
|
219
|
-
|
|
238
|
+
t1Seconds: number
|
|
239
|
+
t2Seconds: number
|
|
220
240
|
maxImageBytes: number
|
|
221
241
|
maxImagePixels: number
|
|
222
242
|
concurrency: number
|
|
@@ -236,7 +256,9 @@ export interface ResolvedVisionToolkitConfig {
|
|
|
236
256
|
/** Ordered failover pool; array order is the priority, highest first. */
|
|
237
257
|
providers: ResolvedProvider[]
|
|
238
258
|
language: 'zh' | 'en'
|
|
239
|
-
|
|
259
|
+
hardTimeoutSeconds: number
|
|
260
|
+
sessionMaxConcurrency: number
|
|
261
|
+
minAvailableSeconds: number
|
|
240
262
|
maxImageBytes: number
|
|
241
263
|
maxImagePixels: number
|
|
242
264
|
concurrency: number
|
|
@@ -245,6 +267,8 @@ export interface ResolvedVisionToolkitConfig {
|
|
|
245
267
|
agentVisionToolkitPath?: string
|
|
246
268
|
python?: string
|
|
247
269
|
}
|
|
270
|
+
storageDir?: string
|
|
271
|
+
storageHistory: string[]
|
|
248
272
|
allowedDirs: string[]
|
|
249
273
|
imageInputVariants: {
|
|
250
274
|
enabled: boolean
|
|
@@ -254,7 +278,7 @@ export interface ResolvedVisionToolkitConfig {
|
|
|
254
278
|
}
|
|
255
279
|
}
|
|
256
280
|
|
|
257
|
-
const
|
|
281
|
+
const MAX_TIMEOUT_SECONDS = 600
|
|
258
282
|
const MAX_IMAGE_BYTES = 268435456
|
|
259
283
|
const MAX_IMAGE_PIXELS = 268435456
|
|
260
284
|
const MAX_CONCURRENCY = 16
|
|
@@ -264,7 +288,6 @@ const DEFAULT_PROVIDER_ATTEMPTS = 3
|
|
|
264
288
|
|
|
265
289
|
/** Global limits a provider inherits when it does not set its own. */
|
|
266
290
|
interface ProviderDefaults {
|
|
267
|
-
timeoutMs: number
|
|
268
291
|
maxImageBytes: number
|
|
269
292
|
maxImagePixels: number
|
|
270
293
|
concurrency: number
|
|
@@ -343,9 +366,13 @@ function resolveProvider(
|
|
|
343
366
|
if (userAgent.length === 0) {
|
|
344
367
|
throw new VisionToolkitError('config', `${label}.userAgent must not be empty`)
|
|
345
368
|
}
|
|
346
|
-
const
|
|
347
|
-
if (!Number.isInteger(
|
|
348
|
-
throw new VisionToolkitError('config', `${label}.
|
|
369
|
+
const t1Seconds = input.t1Seconds ?? 90
|
|
370
|
+
if (!Number.isInteger(t1Seconds) || t1Seconds < 1 || t1Seconds > MAX_TIMEOUT_SECONDS) {
|
|
371
|
+
throw new VisionToolkitError('config', `${label}.t1Seconds must be an integer between 1 and ${MAX_TIMEOUT_SECONDS}`)
|
|
372
|
+
}
|
|
373
|
+
const t2Seconds = input.t2Seconds ?? 90
|
|
374
|
+
if (!Number.isInteger(t2Seconds) || t2Seconds < 1 || t2Seconds > MAX_TIMEOUT_SECONDS) {
|
|
375
|
+
throw new VisionToolkitError('config', `${label}.t2Seconds must be an integer between 1 and ${MAX_TIMEOUT_SECONDS}`)
|
|
349
376
|
}
|
|
350
377
|
const maxImageBytes = input.maxImageBytes ?? defaults.maxImageBytes
|
|
351
378
|
if (!Number.isInteger(maxImageBytes) || maxImageBytes < 1024 || maxImageBytes > MAX_IMAGE_BYTES) {
|
|
@@ -375,7 +402,8 @@ function resolveProvider(
|
|
|
375
402
|
protocol,
|
|
376
403
|
anthropicThinking,
|
|
377
404
|
userAgent,
|
|
378
|
-
|
|
405
|
+
t1Seconds,
|
|
406
|
+
t2Seconds,
|
|
379
407
|
maxImageBytes,
|
|
380
408
|
maxImagePixels,
|
|
381
409
|
concurrency,
|
|
@@ -397,9 +425,17 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
|
|
|
397
425
|
if (language !== 'zh' && language !== 'en') {
|
|
398
426
|
throw new VisionToolkitError('config', 'language must be "zh" or "en"')
|
|
399
427
|
}
|
|
400
|
-
const
|
|
401
|
-
if (!Number.isInteger(
|
|
402
|
-
throw new VisionToolkitError('config', `
|
|
428
|
+
const hardTimeoutSeconds = config.hardTimeoutSeconds ?? 180
|
|
429
|
+
if (!Number.isInteger(hardTimeoutSeconds) || hardTimeoutSeconds < 1 || hardTimeoutSeconds > MAX_TIMEOUT_SECONDS) {
|
|
430
|
+
throw new VisionToolkitError('config', `hardTimeoutSeconds must be an integer between 1 and ${MAX_TIMEOUT_SECONDS}`)
|
|
431
|
+
}
|
|
432
|
+
const sessionMaxConcurrency = config.sessionMaxConcurrency ?? 6
|
|
433
|
+
if (!Number.isInteger(sessionMaxConcurrency) || sessionMaxConcurrency < 1 || sessionMaxConcurrency > MAX_CONCURRENCY) {
|
|
434
|
+
throw new VisionToolkitError('config', `sessionMaxConcurrency must be an integer between 1 and ${MAX_CONCURRENCY}`)
|
|
435
|
+
}
|
|
436
|
+
const minAvailableSeconds = config.minAvailableSeconds ?? 20
|
|
437
|
+
if (!Number.isInteger(minAvailableSeconds) || minAvailableSeconds < 1 || minAvailableSeconds > MAX_TIMEOUT_SECONDS) {
|
|
438
|
+
throw new VisionToolkitError('config', `minAvailableSeconds must be an integer between 1 and ${MAX_TIMEOUT_SECONDS}`)
|
|
403
439
|
}
|
|
404
440
|
const maxImageBytes = config.maxImageBytes ?? 4194304
|
|
405
441
|
if (!Number.isInteger(maxImageBytes) || maxImageBytes < 1024 || maxImageBytes > MAX_IMAGE_BYTES) {
|
|
@@ -431,12 +467,16 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
|
|
|
431
467
|
if (python !== undefined && python.length === 0) {
|
|
432
468
|
throw new VisionToolkitError('config', 'runtime.python must not be empty')
|
|
433
469
|
}
|
|
470
|
+
const storageDir = config.storageDir?.trim()
|
|
471
|
+
const storageHistory = [...new Set((config.storageHistory ?? [])
|
|
472
|
+
.map(dir => dir.trim())
|
|
473
|
+
.filter(dir => dir.length > 0 && dir !== storageDir))]
|
|
434
474
|
const allowedDirs = (config.allowedDirs ?? []).map(dir => dir.trim()).filter(dir => dir.length > 0)
|
|
435
475
|
const imageInputVariants = config.imageInputVariants ?? {}
|
|
436
476
|
const variantProviders = (imageInputVariants.providers ?? [])
|
|
437
477
|
.map(provider => provider.trim())
|
|
438
478
|
.filter(provider => provider.length > 0)
|
|
439
|
-
const providerDefaults: ProviderDefaults = {
|
|
479
|
+
const providerDefaults: ProviderDefaults = { maxImageBytes, maxImagePixels, concurrency }
|
|
440
480
|
const configuredProviders = config.providers ?? []
|
|
441
481
|
if (configuredProviders.length > MAX_PROVIDERS) {
|
|
442
482
|
throw new VisionToolkitError('config', `providers must have at most ${MAX_PROVIDERS} entries`)
|
|
@@ -457,7 +497,9 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
|
|
|
457
497
|
},
|
|
458
498
|
providers,
|
|
459
499
|
language,
|
|
460
|
-
|
|
500
|
+
hardTimeoutSeconds,
|
|
501
|
+
sessionMaxConcurrency,
|
|
502
|
+
minAvailableSeconds,
|
|
461
503
|
maxImageBytes,
|
|
462
504
|
maxImagePixels,
|
|
463
505
|
concurrency,
|
|
@@ -466,6 +508,8 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
|
|
|
466
508
|
...(toolkitPath !== undefined ? { agentVisionToolkitPath: toolkitPath } : {}),
|
|
467
509
|
...(python !== undefined ? { python } : {}),
|
|
468
510
|
},
|
|
511
|
+
...(storageDir === undefined || storageDir.length === 0 ? {} : { storageDir }),
|
|
512
|
+
storageHistory,
|
|
469
513
|
allowedDirs,
|
|
470
514
|
imageInputVariants: {
|
|
471
515
|
enabled: imageInputVariants.enabled ?? true,
|
|
@@ -476,6 +520,49 @@ export function resolveConfig(config: VisionToolkitConfig = {}): ResolvedVisionT
|
|
|
476
520
|
}
|
|
477
521
|
}
|
|
478
522
|
|
|
523
|
+
/** Merge prior storage roots into the next resolved generation's read-only history. */
|
|
524
|
+
export function retainedStorageHistory(
|
|
525
|
+
next: VisionToolkitConfig,
|
|
526
|
+
previous: VisionToolkitConfig,
|
|
527
|
+
): string[] {
|
|
528
|
+
const resolvedNext = resolveConfig(next)
|
|
529
|
+
const resolvedPrevious = resolveConfig(previous)
|
|
530
|
+
return [...new Set([
|
|
531
|
+
...resolvedPrevious.storageHistory,
|
|
532
|
+
...resolvedNext.storageHistory,
|
|
533
|
+
...(resolvedPrevious.storageDir === undefined ? [] : [resolvedPrevious.storageDir]),
|
|
534
|
+
])].filter(storageDir => storageDir !== resolvedNext.storageDir)
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
export interface WatchedSettingsGeneration {
|
|
538
|
+
/** Configuration to activate now; omitted after a successful history writeback. */
|
|
539
|
+
config?: VisionToolkitConfig
|
|
540
|
+
/** Whether the derived history still needs plugin-owned durable persistence. */
|
|
541
|
+
requiresDurableStorageHistory?: boolean
|
|
542
|
+
/** Non-fatal internal-history persistence error. */
|
|
543
|
+
persistenceError?: unknown
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** Prepare one live Settings generation without letting internal history writeback block activation. */
|
|
547
|
+
export async function prepareWatchedSettingsGeneration(
|
|
548
|
+
next: VisionToolkitConfig,
|
|
549
|
+
previous: VisionToolkitConfig,
|
|
550
|
+
writable: boolean,
|
|
551
|
+
persistStorageHistory: (storageHistory: string[]) => Promise<void>,
|
|
552
|
+
): Promise<WatchedSettingsGeneration> {
|
|
553
|
+
const storageHistory = retainedStorageHistory(next, previous)
|
|
554
|
+
if (JSON.stringify(storageHistory) === JSON.stringify(resolveConfig(next).storageHistory)) return { config: next }
|
|
555
|
+
|
|
556
|
+
const config = { ...next, storageHistory }
|
|
557
|
+
if (!writable) return { config, requiresDurableStorageHistory: true }
|
|
558
|
+
try {
|
|
559
|
+
await persistStorageHistory(storageHistory)
|
|
560
|
+
return {}
|
|
561
|
+
} catch (persistenceError) {
|
|
562
|
+
return { config, requiresDurableStorageHistory: true, persistenceError }
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
479
566
|
/** Whether a resolved provider should use the bundled public key instead of DSH credentials. */
|
|
480
567
|
export function isBuiltInFreeVisionProvider(provider: ResolvedVisionToolkitConfig['provider']): boolean {
|
|
481
568
|
return String(provider.credential) === BUILT_IN_FREE_VISION_CREDENTIAL
|
package/src/errors.ts
CHANGED
|
@@ -5,11 +5,35 @@
|
|
|
5
5
|
* @module dsh-vision-toolkit/errors
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
/**
|
|
8
|
+
/**
|
|
9
|
+
* Discriminant tag for every Vision Toolkit failure.
|
|
10
|
+
*
|
|
11
|
+
* The remote vision-provider failures are split by a machine-routable taxonomy
|
|
12
|
+
* so the failover loop can decide, per error, whether to retry the SAME
|
|
13
|
+
* provider or advance to the next one:
|
|
14
|
+
*
|
|
15
|
+
* - `auth` 401/403 — credential is wrong or unauthorized. Never retry.
|
|
16
|
+
* - `quota` 402 — account out of quota/unpaid. Never retry.
|
|
17
|
+
* - `rate_limit` 429 — throttled. Park and revisit, never retry in place.
|
|
18
|
+
* - `server` 5xx — transient provider fault. Worth a same-provider retry.
|
|
19
|
+
* - `network` connection refused / DNS / socket — transient. Worth a retry.
|
|
20
|
+
* - `region` provider unavailable in this region. Never retry.
|
|
21
|
+
* - `tos` rejected by content/safety policy. Never retry.
|
|
22
|
+
* - `invalid_request` 400/404/422 — bad request or unknown model. Never retry.
|
|
23
|
+
* - `service` fallback for unclassifiable remote failures. Never retry.
|
|
24
|
+
*/
|
|
9
25
|
export const VISION_TOOLKIT_ERROR_CODES = [
|
|
10
26
|
'config',
|
|
11
27
|
'input',
|
|
12
28
|
'capacity',
|
|
29
|
+
'auth',
|
|
30
|
+
'quota',
|
|
31
|
+
'rate_limit',
|
|
32
|
+
'server',
|
|
33
|
+
'network',
|
|
34
|
+
'region',
|
|
35
|
+
'tos',
|
|
36
|
+
'invalid_request',
|
|
13
37
|
'service',
|
|
14
38
|
'runtime',
|
|
15
39
|
'output',
|
package/src/evidence-cache.ts
CHANGED
|
@@ -73,7 +73,7 @@ function hash(value: string): string {
|
|
|
73
73
|
return createHash('sha256').update(value).digest('hex')
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
/** Fingerprint every runtime setting that can change
|
|
76
|
+
/** Fingerprint every runtime setting that can change generated evidence or its embedded path. */
|
|
77
77
|
export function evidenceRuntimeFingerprint(
|
|
78
78
|
config: ResolvedVisionToolkitConfig,
|
|
79
79
|
credentialSha256?: string,
|
|
@@ -108,11 +108,14 @@ export function evidenceRuntimeFingerprint(
|
|
|
108
108
|
attempts: provider.attempts,
|
|
109
109
|
})),
|
|
110
110
|
language: config.language,
|
|
111
|
-
|
|
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,
|
|
115
117
|
runtime: config.runtime,
|
|
118
|
+
storageDir: config.storageDir ?? null,
|
|
116
119
|
}))
|
|
117
120
|
}
|
|
118
121
|
|