opencode-pollinations-plugin 6.4.9 → 6.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.de.md +67 -54
- package/README.es.md +79 -66
- package/README.fr.md +70 -57
- package/README.it.md +78 -65
- package/README.md +33 -29
- package/README.zh.md +77 -64
- package/dist/locales/de.json +82 -47
- package/dist/locales/en.json +84 -49
- package/dist/locales/es.json +82 -47
- package/dist/locales/fr.json +81 -46
- package/dist/locales/it.json +82 -47
- package/dist/locales/zh.json +82 -47
- package/dist/server/commands.js +118 -178
- package/dist/server/config.d.ts +22 -6
- package/dist/server/config.js +45 -5
- package/dist/server/connect-response.js +7 -7
- package/dist/server/generate-config.js +2 -2
- package/dist/server/models/cache.d.ts +23 -10
- package/dist/server/models/cache.js +46 -24
- package/dist/server/models/fetcher.js +2 -0
- package/dist/server/models/types.d.ts +2 -0
- package/dist/server/models/worker.js +4 -4
- package/dist/server/proxy.d.ts +6 -0
- package/dist/server/proxy.js +318 -218
- package/dist/server/quota.d.ts +23 -32
- package/dist/server/quota.js +44 -184
- package/dist/server/scripts/pollinations_pricing.js +6 -3
- package/dist/server/status.js +1 -2
- package/dist/server/toast.js +1 -1
- package/dist/tools/index.d.ts +2 -1
- package/dist/tools/index.js +3 -1
- package/dist/tools/pollinations/artifact-core.d.ts +53 -0
- package/dist/tools/pollinations/artifact-core.js +159 -0
- package/dist/tools/pollinations/beta_discovery.js +2 -1
- package/dist/tools/pollinations/cost-guard.d.ts +2 -2
- package/dist/tools/pollinations/error-parser.d.ts +38 -0
- package/dist/tools/pollinations/error-parser.js +112 -0
- package/dist/tools/pollinations/gen_3d.d.ts +17 -0
- package/dist/tools/pollinations/gen_3d.js +207 -0
- package/dist/tools/pollinations/gen_image.js +29 -10
- package/dist/tools/pollinations/gen_music.js +3 -2
- package/dist/tools/pollinations/gen_video.js +13 -2
- package/dist/tools/pollinations/polli_config.js +15 -21
- package/dist/tools/pollinations/polli_gen_confirm.js +2 -0
- package/dist/tools/pollinations/shared.d.ts +1 -1
- package/dist/tools/pollinations/shared.js +53 -142
- package/dist/tools/pollinations/timeout-policy.d.ts +80 -0
- package/dist/tools/pollinations/timeout-policy.js +124 -0
- package/dist/tools/pollinations/tool-capability-registry.d.ts +51 -0
- package/dist/tools/pollinations/tool-capability-registry.js +215 -0
- package/dist/tools/pollinations/transcribe_audio.js +5 -24
- package/package.json +64 -62
- package/dist/server/tier-info.d.ts +0 -36
- package/dist/server/tier-info.js +0 -107
|
@@ -6,6 +6,7 @@ import { polliGenImageTool } from './gen_image.js';
|
|
|
6
6
|
import { polliGenVideoTool } from './gen_video.js';
|
|
7
7
|
import { polliGenAudioTool } from './gen_audio.js';
|
|
8
8
|
import { polliGenMusicTool } from './gen_music.js';
|
|
9
|
+
import { polliGen3dTool } from './gen_3d.js';
|
|
9
10
|
import { polliWebSearchTool } from './polli_web_search.js';
|
|
10
11
|
import { formatCost } from './shared.js';
|
|
11
12
|
export const polliGenConfirmTool = tool({
|
|
@@ -30,6 +31,7 @@ export const polliGenConfirmTool = tool({
|
|
|
30
31
|
'polli_gen_video': polliGenVideoTool,
|
|
31
32
|
'polli_gen_audio': polliGenAudioTool,
|
|
32
33
|
'polli_gen_music': polliGenMusicTool,
|
|
34
|
+
'polli_gen_3d': polliGen3dTool,
|
|
33
35
|
'polli_web_search': polliWebSearchTool,
|
|
34
36
|
};
|
|
35
37
|
const targetTool = toolRegistry[pendingReq.toolName];
|
|
@@ -105,7 +105,7 @@ export declare function getMusicModel(): Record<string, {
|
|
|
105
105
|
params: string[];
|
|
106
106
|
duration: [number, number];
|
|
107
107
|
}>;
|
|
108
|
-
export declare function httpsGet(url: string, headers?: Record<string, string
|
|
108
|
+
export declare function httpsGet(url: string, headers?: Record<string, string>, timeoutMs?: number): Promise<{
|
|
109
109
|
data: Buffer;
|
|
110
110
|
headers: Record<string, string>;
|
|
111
111
|
}>;
|
|
@@ -50,7 +50,8 @@ export function getPaidImageModels() {
|
|
|
50
50
|
}
|
|
51
51
|
return result;
|
|
52
52
|
}
|
|
53
|
-
|
|
53
|
+
// v6.5: offline fallback is served by the Model Registry STATIC_FALLBACK.
|
|
54
|
+
return {};
|
|
54
55
|
}
|
|
55
56
|
/**
|
|
56
57
|
* Dynamic Video Models accessor.
|
|
@@ -65,9 +66,11 @@ export function getVideoModels() {
|
|
|
65
66
|
result[m.name] = {
|
|
66
67
|
desc: m.description,
|
|
67
68
|
cost: costStr,
|
|
68
|
-
|
|
69
|
+
// wan is I2V-only upstream; manual.ts patches [image,text] +
|
|
70
|
+
// a T2V dummy-image hack, so t2v stays enabled for it.
|
|
71
|
+
t2v: !(m.supportsI2X && m.input_modalities.length === 1 && m.input_modalities[0] === 'image'),
|
|
69
72
|
i2v: m.supportsI2X,
|
|
70
|
-
audio: !
|
|
73
|
+
audio: !VIDEO_NO_AUDIO_MODELS.has(m.name),
|
|
71
74
|
duration: m.durationRange || [1, 10],
|
|
72
75
|
aspectRatios: m.aspectRatios || ['16:9'],
|
|
73
76
|
costHeader: m.costHeader || 'x-usage-completion-video-seconds',
|
|
@@ -76,7 +79,7 @@ export function getVideoModels() {
|
|
|
76
79
|
}
|
|
77
80
|
return result;
|
|
78
81
|
}
|
|
79
|
-
return
|
|
82
|
+
return {};
|
|
80
83
|
}
|
|
81
84
|
/**
|
|
82
85
|
* Dynamic Audio Models accessor.
|
|
@@ -99,7 +102,7 @@ export function getAudioModels() {
|
|
|
99
102
|
}
|
|
100
103
|
return result;
|
|
101
104
|
}
|
|
102
|
-
return
|
|
105
|
+
return {};
|
|
103
106
|
}
|
|
104
107
|
/**
|
|
105
108
|
* Text Model accessor
|
|
@@ -144,9 +147,15 @@ export function getMusicModel() {
|
|
|
144
147
|
// getPaidImageModels(), getVideoModels(), getAudioModels(), getMusicModel()
|
|
145
148
|
// For direct model lookup: use ModelRegistry.getByNameOrAlias()
|
|
146
149
|
// ─── Private Static Fallback Data ─────────────────────────────────────────
|
|
147
|
-
//
|
|
148
|
-
|
|
149
|
-
|
|
150
|
+
// v6.5: the three big model tables (_STATIC_PAID_IMAGE_MODELS,
|
|
151
|
+
// _STATIC_VIDEO_MODELS, _STATIC_AUDIO_MODELS) and the empty
|
|
152
|
+
// _STATIC_I2V_ONLY set were removed — the Model Registry serves the
|
|
153
|
+
// offline fallback (STATIC_FALLBACK in models/cache.ts) and the live
|
|
154
|
+
// catalog provides paid_only/modalities/voices dynamically.
|
|
155
|
+
// What remains are transport rules the catalogue does NOT expose.
|
|
156
|
+
// Video models without audio output (mirrors manual.ts; the catalogue's
|
|
157
|
+
// video_capabilities.audio_output is not yet mapped to PollinationsModel).
|
|
158
|
+
const VIDEO_NO_AUDIO_MODELS = new Set(['seedance', 'seedance-pro']);
|
|
150
159
|
const _STATIC_AUDIO_ENDPOINTS = {
|
|
151
160
|
'openai-audio': '/v1/chat/completions',
|
|
152
161
|
'elevenlabs': '/audio/{text}',
|
|
@@ -154,101 +163,6 @@ const _STATIC_AUDIO_ENDPOINTS = {
|
|
|
154
163
|
'scribe': '/v1/audio/transcriptions',
|
|
155
164
|
'elevenmusic': '/audio/{text}',
|
|
156
165
|
};
|
|
157
|
-
const _STATIC_PAID_IMAGE_MODELS = {
|
|
158
|
-
'flux': { desc: 'Flux Schnell', cost: '0.0002 🌻', t2i: true, i2i: false, params: ['width', 'height'] },
|
|
159
|
-
'sana': { desc: 'Sana (Efficient)', cost: '0.0002 🌻', t2i: true, i2i: false, params: ['width', 'height'] },
|
|
160
|
-
'zimage': { desc: 'Z-Image Turbo (6B Flux 2x)', cost: '0.0002 🌻', t2i: true, i2i: false, params: ['width', 'height'] },
|
|
161
|
-
'imagen-4': { desc: 'Imagen 4 (alpha)', cost: '0.0025 🌻', t2i: true, i2i: false, params: ['width', 'height'] },
|
|
162
|
-
'klein': { desc: 'FLUX.2 Klein 4B', cost: '0.008 🌻', t2i: true, i2i: true, params: ['width', 'height', 'image'] },
|
|
163
|
-
'klein-large': { desc: 'FLUX.2 Klein 9B', cost: '0.012 🌻', t2i: true, i2i: true, params: ['width', 'height', 'image'] },
|
|
164
|
-
'gptimage': { desc: 'GPT Image 1 Mini (OpenAI)', cost: 'tokens', t2i: true, i2i: false, params: ['width', 'height', 'quality', 'transparent'] },
|
|
165
|
-
'gptimage-large': { desc: 'GPT Image 1.5 (Advanced)', cost: 'tokens', t2i: true, i2i: false, params: ['width', 'height', 'quality', 'transparent'] },
|
|
166
|
-
'kontext': { desc: 'FLUX.1 Kontext', cost: '0.04 🌻 💎', t2i: true, i2i: true, params: ['width', 'height', 'image'], notes: 'In-Context Editing' },
|
|
167
|
-
'seedream': { desc: 'Seedream 4.0 (ByteDance ARK)', cost: '0.03 🌻', t2i: true, i2i: true, params: ['width', 'height', 'image'] },
|
|
168
|
-
'seedream-pro': { desc: 'Seedream 4.5 Pro (ARK 4K)', cost: '0.04 🌻 💎', t2i: true, i2i: true, params: ['width', 'height', 'image'], notes: '4K, Multi-Image' },
|
|
169
|
-
'nanobanana': { desc: 'NanoBanana (Gemini 2.5 Flash)', cost: 'tokens', t2i: true, i2i: true, params: ['width', 'height', 'image'] },
|
|
170
|
-
'nanobanana-pro': { desc: 'NanoBanana Pro (Gemini 3 Pro)', cost: 'tokens', t2i: true, i2i: true, params: ['width', 'height', 'image'], notes: 'Thinking Model' },
|
|
171
|
-
};
|
|
172
|
-
const _STATIC_VIDEO_MODELS = {
|
|
173
|
-
'grok-video': {
|
|
174
|
-
desc: 'Grok Video (alpha)',
|
|
175
|
-
cost: '0.0025/sec',
|
|
176
|
-
t2v: true, i2v: false, audio: true,
|
|
177
|
-
duration: [1, 15],
|
|
178
|
-
aspectRatios: ['16:9', '9:16', '1:1', '4:3'],
|
|
179
|
-
costHeader: 'x-usage-completion-video-seconds',
|
|
180
|
-
genTime: '~10s'
|
|
181
|
-
},
|
|
182
|
-
'ltx-2': {
|
|
183
|
-
desc: 'LTX-2 (Lightricks)',
|
|
184
|
-
cost: '0.01/sec',
|
|
185
|
-
t2v: true, i2v: false, audio: true,
|
|
186
|
-
duration: [5, 20],
|
|
187
|
-
aspectRatios: ['16:9'],
|
|
188
|
-
costHeader: 'x-usage-completion-video-seconds',
|
|
189
|
-
genTime: '~35s'
|
|
190
|
-
},
|
|
191
|
-
'wan': {
|
|
192
|
-
desc: 'Wan 2.6 (Alibaba)',
|
|
193
|
-
cost: '0.025/sec',
|
|
194
|
-
t2v: false, i2v: true, audio: true,
|
|
195
|
-
duration: [5, 15],
|
|
196
|
-
aspectRatios: ['16:9', '9:16', '1:1', '4:3'],
|
|
197
|
-
costHeader: 'x-usage-completion-video-seconds',
|
|
198
|
-
genTime: '~30s'
|
|
199
|
-
},
|
|
200
|
-
'veo': {
|
|
201
|
-
desc: 'Veo 3.1 Fast (Google)',
|
|
202
|
-
cost: '0.15/sec 💎',
|
|
203
|
-
t2v: true, i2v: true, audio: true,
|
|
204
|
-
duration: [4, 8],
|
|
205
|
-
aspectRatios: ['16:9', '9:16', '1:1'],
|
|
206
|
-
costHeader: 'x-usage-completion-video-seconds',
|
|
207
|
-
genTime: '~45-68s',
|
|
208
|
-
},
|
|
209
|
-
'seedance': {
|
|
210
|
-
desc: 'Seedance Lite (BytePlus)',
|
|
211
|
-
cost: 'tokens',
|
|
212
|
-
t2v: true, i2v: true, audio: false,
|
|
213
|
-
duration: [4, 12],
|
|
214
|
-
aspectRatios: ['16:9', '9:16', '1:1'],
|
|
215
|
-
costHeader: 'x-usage-completion-video-tokens',
|
|
216
|
-
genTime: '~30s'
|
|
217
|
-
},
|
|
218
|
-
'seedance-pro': {
|
|
219
|
-
desc: 'Seedance Pro-Fast (BytePlus)',
|
|
220
|
-
cost: 'tokens',
|
|
221
|
-
t2v: true, i2v: true, audio: false,
|
|
222
|
-
duration: [4, 12],
|
|
223
|
-
aspectRatios: ['16:9', '9:16', '1:1'],
|
|
224
|
-
costHeader: 'x-usage-completion-video-tokens',
|
|
225
|
-
genTime: '~30s'
|
|
226
|
-
},
|
|
227
|
-
};
|
|
228
|
-
const _STATIC_AUDIO_MODELS = {
|
|
229
|
-
'openai-audio': {
|
|
230
|
-
desc: 'GPT-4o Audio Preview',
|
|
231
|
-
type: 'both',
|
|
232
|
-
endpoint: '/v1/chat/completions',
|
|
233
|
-
params: ['voice', 'format'],
|
|
234
|
-
voices: ['alloy', 'echo', 'fable', 'onyx', 'nova', 'shimmer'],
|
|
235
|
-
notes: 'DEFAULT - least expensive'
|
|
236
|
-
},
|
|
237
|
-
'elevenlabs': {
|
|
238
|
-
desc: 'ElevenLabs v3',
|
|
239
|
-
type: 'tts',
|
|
240
|
-
endpoint: '/audio/{text}',
|
|
241
|
-
params: ['voice', 'response_format'],
|
|
242
|
-
voices: ['rachel', 'domi', 'bella', 'elli', 'charlotte', 'dorothy', 'sarah', 'emily', 'lily', 'matilda', 'adam', 'antoni', 'arnold', 'josh', 'sam', 'daniel', 'charlie', 'james', 'fin', 'callum', 'liam', 'george', 'brian', 'bill', 'ash', 'ballad', 'coral', 'sage', 'verse'],
|
|
243
|
-
},
|
|
244
|
-
'whisper': {
|
|
245
|
-
desc: 'OpenAI Whisper v3',
|
|
246
|
-
type: 'stt',
|
|
247
|
-
endpoint: '/v1/audio/transcriptions',
|
|
248
|
-
params: ['file'],
|
|
249
|
-
notes: 'POST ONLY (multipart)'
|
|
250
|
-
},
|
|
251
|
-
};
|
|
252
166
|
const _STATIC_MUSIC_MODEL = {
|
|
253
167
|
'elevenmusic': {
|
|
254
168
|
desc: 'ElevenLabs Music',
|
|
@@ -292,7 +206,7 @@ function detectAudioType(m) {
|
|
|
292
206
|
return 'tts';
|
|
293
207
|
}
|
|
294
208
|
// ─── HTTP Helpers ─────────────────────────────────────────────────────────
|
|
295
|
-
export function httpsGet(url, headers = {}) {
|
|
209
|
+
export function httpsGet(url, headers = {}, timeoutMs = 300000) {
|
|
296
210
|
return new Promise((resolve, reject) => {
|
|
297
211
|
const parsedUrl = new URL(url);
|
|
298
212
|
const options = {
|
|
@@ -305,9 +219,9 @@ export function httpsGet(url, headers = {}) {
|
|
|
305
219
|
},
|
|
306
220
|
};
|
|
307
221
|
const req = https.request(options, (res) => {
|
|
308
|
-
// Handle redirects
|
|
222
|
+
// Handle redirects (propagate the timeout on follow)
|
|
309
223
|
if ([301, 302, 307].includes(res.statusCode || 0) && res.headers.location) {
|
|
310
|
-
httpsGet(res.headers.location, headers).then(resolve).catch(reject);
|
|
224
|
+
httpsGet(res.headers.location, headers, timeoutMs).then(resolve).catch(reject);
|
|
311
225
|
return;
|
|
312
226
|
}
|
|
313
227
|
const chunks = [];
|
|
@@ -342,9 +256,11 @@ export function httpsGet(url, headers = {}) {
|
|
|
342
256
|
});
|
|
343
257
|
});
|
|
344
258
|
req.on('error', reject);
|
|
345
|
-
|
|
259
|
+
// v6.5: per-call timeout (clamped to 1h absolute max), no blind replay.
|
|
260
|
+
const clamped = Math.min(Math.max(timeoutMs, 10000), 3600000);
|
|
261
|
+
req.setTimeout(clamped, () => {
|
|
346
262
|
req.destroy();
|
|
347
|
-
reject(new Error(
|
|
263
|
+
reject(new Error(`Timeout (${Math.round(clamped / 1000)}s)`));
|
|
348
264
|
});
|
|
349
265
|
req.end();
|
|
350
266
|
});
|
|
@@ -587,39 +503,36 @@ export function per1pollen(cost) {
|
|
|
587
503
|
return `${x.toFixed(1)}`;
|
|
588
504
|
}
|
|
589
505
|
export function estimateImageCost(model) {
|
|
590
|
-
// Try ModelRegistry first
|
|
506
|
+
// Try ModelRegistry first (averageCost or catalogue pricing)
|
|
591
507
|
if (ModelRegistry.isReady()) {
|
|
592
508
|
const m = ModelRegistry.getByNameOrAlias('image', model);
|
|
593
|
-
if (m
|
|
594
|
-
|
|
509
|
+
if (m) {
|
|
510
|
+
if (m.averageCost !== undefined)
|
|
511
|
+
return m.averageCost;
|
|
512
|
+
if (m.pricing.completionImageTokens !== undefined)
|
|
513
|
+
return m.pricing.completionImageTokens;
|
|
595
514
|
}
|
|
596
515
|
}
|
|
597
|
-
|
|
598
|
-
const info = _STATIC_PAID_IMAGE_MODELS[model];
|
|
599
|
-
if (!info)
|
|
600
|
-
return 0.0002;
|
|
601
|
-
const costMatch = info.cost.match(/[\d.]+/);
|
|
602
|
-
return costMatch ? parseFloat(costMatch[0]) : 0.0002;
|
|
516
|
+
return 0.0002;
|
|
603
517
|
}
|
|
604
518
|
export function estimateVideoCost(model, duration) {
|
|
605
|
-
// Try ModelRegistry first
|
|
519
|
+
// Try ModelRegistry first (averageCost or catalogue pricing)
|
|
606
520
|
if (ModelRegistry.isReady()) {
|
|
607
521
|
const m = ModelRegistry.getByNameOrAlias('video', model);
|
|
608
|
-
if (m
|
|
609
|
-
|
|
522
|
+
if (m) {
|
|
523
|
+
if (m.averageCost !== undefined)
|
|
524
|
+
return m.averageCost;
|
|
525
|
+
if (m.pricing.completionVideoSeconds !== undefined) {
|
|
526
|
+
return duration * m.pricing.completionVideoSeconds;
|
|
527
|
+
}
|
|
528
|
+
if (m.pricing.completionVideoTokens !== undefined) {
|
|
529
|
+
// seedance-style token billing (~21780 tokens/s observed)
|
|
530
|
+
const tokensPerSecond = 21780;
|
|
531
|
+
return (duration * tokensPerSecond) * m.pricing.completionVideoTokens;
|
|
532
|
+
}
|
|
610
533
|
}
|
|
611
534
|
}
|
|
612
|
-
|
|
613
|
-
const info = _STATIC_VIDEO_MODELS[model];
|
|
614
|
-
if (!info)
|
|
615
|
-
return duration * 0.01;
|
|
616
|
-
if (info.costHeader === 'x-usage-completion-video-tokens') {
|
|
617
|
-
const tokensPerSecond = 21780;
|
|
618
|
-
return (duration * tokensPerSecond) * 0.00001;
|
|
619
|
-
}
|
|
620
|
-
const costMatch = info.cost.match(/[\d.]+/);
|
|
621
|
-
const perSecond = costMatch ? parseFloat(costMatch[0]) : 0.01;
|
|
622
|
-
return duration * perSecond;
|
|
535
|
+
return duration * 0.01;
|
|
623
536
|
}
|
|
624
537
|
export function estimateTtsCost(textLength) {
|
|
625
538
|
// Try ModelRegistry first
|
|
@@ -707,8 +620,7 @@ export function supportsI2I(model) {
|
|
|
707
620
|
const m = ModelRegistry.getByNameOrAlias('image', model);
|
|
708
621
|
return m?.supportsI2X === true;
|
|
709
622
|
}
|
|
710
|
-
|
|
711
|
-
return info?.i2i === true;
|
|
623
|
+
return false;
|
|
712
624
|
}
|
|
713
625
|
/**
|
|
714
626
|
* Check if video model supports Image-to-Video
|
|
@@ -718,21 +630,22 @@ export function supportsI2V(model) {
|
|
|
718
630
|
const m = ModelRegistry.getByNameOrAlias('video', model);
|
|
719
631
|
return m?.supportsI2X === true;
|
|
720
632
|
}
|
|
721
|
-
|
|
722
|
-
return info?.i2v === true;
|
|
633
|
+
return false;
|
|
723
634
|
}
|
|
724
635
|
/**
|
|
725
636
|
* Check if video model requires Image-to-Video (no T2V)
|
|
726
637
|
*/
|
|
727
638
|
export function requiresI2V(model) {
|
|
639
|
+
// v6.5: I2V-only = accepts image input and no text input. manual.ts
|
|
640
|
+
// patches wan with [image,text] + T2V dummy-image hack, so no current
|
|
641
|
+
// model is strictly I2V-only.
|
|
728
642
|
if (ModelRegistry.isReady()) {
|
|
729
643
|
const m = ModelRegistry.getByNameOrAlias('video', model);
|
|
730
644
|
if (m) {
|
|
731
|
-
return
|
|
645
|
+
return m.input_modalities.includes('image') && !m.input_modalities.includes('text');
|
|
732
646
|
}
|
|
733
647
|
}
|
|
734
|
-
|
|
735
|
-
return info?.t2v === false && info?.i2v === true;
|
|
648
|
+
return false;
|
|
736
649
|
}
|
|
737
650
|
/**
|
|
738
651
|
* Validate aspect ratio for video model
|
|
@@ -742,8 +655,7 @@ export function validateAspectRatio(model, ratio) {
|
|
|
742
655
|
const m = ModelRegistry.getByNameOrAlias('video', model);
|
|
743
656
|
return m?.aspectRatios?.includes(ratio) ?? false;
|
|
744
657
|
}
|
|
745
|
-
|
|
746
|
-
return info?.aspectRatios.includes(ratio) ?? false;
|
|
658
|
+
return false;
|
|
747
659
|
}
|
|
748
660
|
/**
|
|
749
661
|
* Get valid duration range for video model
|
|
@@ -753,6 +665,5 @@ export function getDurationRange(model) {
|
|
|
753
665
|
const m = ModelRegistry.getByNameOrAlias('video', model);
|
|
754
666
|
return m?.durationRange ?? [1, 10];
|
|
755
667
|
}
|
|
756
|
-
|
|
757
|
-
return info?.duration ?? [1, 10];
|
|
668
|
+
return [1, 10];
|
|
758
669
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout Policy (v6.5) — single source of truth for operation timeouts.
|
|
3
|
+
*
|
|
4
|
+
* Hierarchy (highest precedence first):
|
|
5
|
+
* per-call (timeout_seconds?)
|
|
6
|
+
* > model/backend override
|
|
7
|
+
* > capability
|
|
8
|
+
* > global default
|
|
9
|
+
*
|
|
10
|
+
* Clamp: minimum 10s, absolute maximum = hierarchy.max (3600s).
|
|
11
|
+
* Values based on the Phase 2/2.2 execution audit (server-side limits:
|
|
12
|
+
* trellis ~5 min, hyper3d ~10 min, wan-pro ~15 min, veo/grok ~3 min).
|
|
13
|
+
*/
|
|
14
|
+
export interface TimeoutHierarchy {
|
|
15
|
+
/** Global default for all remote operations (s). */
|
|
16
|
+
default: number;
|
|
17
|
+
/** Default for long-running generations (s). */
|
|
18
|
+
longRunning: number;
|
|
19
|
+
/** Absolute ceiling (s). */
|
|
20
|
+
max: number;
|
|
21
|
+
/** Capability floors (s). */
|
|
22
|
+
capabilities: Record<string, number>;
|
|
23
|
+
/** Model/backend overrides (s). `"name"` exact match, `"prefix-*"` wildcard. */
|
|
24
|
+
overrides: Record<string, number>;
|
|
25
|
+
}
|
|
26
|
+
export declare const DEFAULT_TIMEOUT_HIERARCHY: TimeoutHierarchy;
|
|
27
|
+
export declare const MIN_TIMEOUT_SECONDS = 10;
|
|
28
|
+
export declare const MAX_TIMEOUT_SECONDS = 3600;
|
|
29
|
+
/** Raw user timeout config (config.timeouts) — merged OVER the built-ins. */
|
|
30
|
+
export interface UserTimeoutConfig {
|
|
31
|
+
default?: number;
|
|
32
|
+
longRunning?: number;
|
|
33
|
+
max?: number;
|
|
34
|
+
capabilities?: Record<string, number>;
|
|
35
|
+
overrides?: Record<string, number>;
|
|
36
|
+
}
|
|
37
|
+
export interface ResolveTimeoutOptions {
|
|
38
|
+
perCall?: number;
|
|
39
|
+
model?: string;
|
|
40
|
+
capability?: string;
|
|
41
|
+
longRunning?: boolean;
|
|
42
|
+
hierarchy?: TimeoutHierarchy;
|
|
43
|
+
}
|
|
44
|
+
export interface TimeoutValidation {
|
|
45
|
+
ok: boolean;
|
|
46
|
+
seconds?: number;
|
|
47
|
+
reason?: string;
|
|
48
|
+
}
|
|
49
|
+
/** Validate a per-call timeout_seconds value (>= 10s, <= 3600s). */
|
|
50
|
+
export declare function validateTimeoutSeconds(value: number | undefined): TimeoutValidation;
|
|
51
|
+
/** Exact or wildcard ("prefix-*") override lookup. */
|
|
52
|
+
export declare function lookupTimeoutOverride(overrides: Record<string, number> | undefined, model: string | undefined): number | undefined;
|
|
53
|
+
/**
|
|
54
|
+
* Resolve the effective timeout with the full v6.5 precedence:
|
|
55
|
+
*
|
|
56
|
+
* per-call (timeout_seconds)
|
|
57
|
+
* > USER model override (config.timeouts.overrides[model])
|
|
58
|
+
* > USER capability override (config.timeouts.capabilities[cap])
|
|
59
|
+
* > built-in model default (DEFAULT hierarchy overrides)
|
|
60
|
+
* > built-in capability default (DEFAULT hierarchy capabilities)
|
|
61
|
+
* > global (longRunning | default)
|
|
62
|
+
*
|
|
63
|
+
* Clamped to [10, user.max || builtin.max].
|
|
64
|
+
*/
|
|
65
|
+
export declare function resolveEffectiveTimeout(opts: {
|
|
66
|
+
perCall?: number;
|
|
67
|
+
model?: string;
|
|
68
|
+
capabilityKey?: string;
|
|
69
|
+
longRunning?: boolean;
|
|
70
|
+
user?: UserTimeoutConfig | null;
|
|
71
|
+
builtin?: TimeoutHierarchy;
|
|
72
|
+
}): number;
|
|
73
|
+
/**
|
|
74
|
+
* Legacy resolution used by the TCR capability timeout test surface:
|
|
75
|
+
* per-call > model override > capability > (longRunning ? longRunning : default).
|
|
76
|
+
* Result is clamped to [10, hierarchy.max].
|
|
77
|
+
*/
|
|
78
|
+
export declare function resolveTimeoutSeconds(opts: ResolveTimeoutOptions): number;
|
|
79
|
+
/** Merge user-configured timeout overrides (from config) onto the defaults. */
|
|
80
|
+
export declare function mergeTimeoutHierarchy(user?: Partial<TimeoutHierarchy>): TimeoutHierarchy;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Timeout Policy (v6.5) — single source of truth for operation timeouts.
|
|
3
|
+
*
|
|
4
|
+
* Hierarchy (highest precedence first):
|
|
5
|
+
* per-call (timeout_seconds?)
|
|
6
|
+
* > model/backend override
|
|
7
|
+
* > capability
|
|
8
|
+
* > global default
|
|
9
|
+
*
|
|
10
|
+
* Clamp: minimum 10s, absolute maximum = hierarchy.max (3600s).
|
|
11
|
+
* Values based on the Phase 2/2.2 execution audit (server-side limits:
|
|
12
|
+
* trellis ~5 min, hyper3d ~10 min, wan-pro ~15 min, veo/grok ~3 min).
|
|
13
|
+
*/
|
|
14
|
+
export const DEFAULT_TIMEOUT_HIERARCHY = {
|
|
15
|
+
default: 300,
|
|
16
|
+
longRunning: 900,
|
|
17
|
+
max: 3600,
|
|
18
|
+
capabilities: {
|
|
19
|
+
image: 600,
|
|
20
|
+
video: 1800,
|
|
21
|
+
audio: 600,
|
|
22
|
+
threeD: 1800,
|
|
23
|
+
realtime: 300,
|
|
24
|
+
embed: 60,
|
|
25
|
+
},
|
|
26
|
+
overrides: {
|
|
27
|
+
'trellis-2': 1200, // server ~5 min max
|
|
28
|
+
'hyper3d-rodin': 1800, // server ~10 min max
|
|
29
|
+
'seedance-*': 900, // server ~6 min
|
|
30
|
+
'wan-pro': 1800, // server ~15 min
|
|
31
|
+
},
|
|
32
|
+
};
|
|
33
|
+
export const MIN_TIMEOUT_SECONDS = 10;
|
|
34
|
+
export const MAX_TIMEOUT_SECONDS = 3600;
|
|
35
|
+
/** Validate a per-call timeout_seconds value (>= 10s, <= 3600s). */
|
|
36
|
+
export function validateTimeoutSeconds(value) {
|
|
37
|
+
if (value === undefined)
|
|
38
|
+
return { ok: true };
|
|
39
|
+
if (typeof value !== 'number' || !isFinite(value)) {
|
|
40
|
+
return { ok: false, reason: `timeout_seconds must be a finite number of seconds (got ${value})` };
|
|
41
|
+
}
|
|
42
|
+
if (value < MIN_TIMEOUT_SECONDS) {
|
|
43
|
+
return { ok: false, reason: `timeout_seconds must be >= ${MIN_TIMEOUT_SECONDS}s (got ${value}s)` };
|
|
44
|
+
}
|
|
45
|
+
if (value > MAX_TIMEOUT_SECONDS) {
|
|
46
|
+
return { ok: false, reason: `timeout_seconds must be <= ${MAX_TIMEOUT_SECONDS}s (got ${value}s)` };
|
|
47
|
+
}
|
|
48
|
+
return { ok: true, seconds: value };
|
|
49
|
+
}
|
|
50
|
+
/** Exact or wildcard ("prefix-*") override lookup. */
|
|
51
|
+
export function lookupTimeoutOverride(overrides, model) {
|
|
52
|
+
if (!model || !overrides)
|
|
53
|
+
return undefined;
|
|
54
|
+
if (overrides[model] !== undefined)
|
|
55
|
+
return overrides[model];
|
|
56
|
+
for (const [pattern, value] of Object.entries(overrides)) {
|
|
57
|
+
if (pattern.endsWith('*') && model.startsWith(pattern.slice(0, -1))) {
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return undefined;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Resolve the effective timeout with the full v6.5 precedence:
|
|
65
|
+
*
|
|
66
|
+
* per-call (timeout_seconds)
|
|
67
|
+
* > USER model override (config.timeouts.overrides[model])
|
|
68
|
+
* > USER capability override (config.timeouts.capabilities[cap])
|
|
69
|
+
* > built-in model default (DEFAULT hierarchy overrides)
|
|
70
|
+
* > built-in capability default (DEFAULT hierarchy capabilities)
|
|
71
|
+
* > global (longRunning | default)
|
|
72
|
+
*
|
|
73
|
+
* Clamped to [10, user.max || builtin.max].
|
|
74
|
+
*/
|
|
75
|
+
export function resolveEffectiveTimeout(opts) {
|
|
76
|
+
const builtin = opts.builtin ?? DEFAULT_TIMEOUT_HIERARCHY;
|
|
77
|
+
let t = opts.perCall; // 1. per-call
|
|
78
|
+
if (t === undefined)
|
|
79
|
+
t = lookupTimeoutOverride(opts.user?.overrides, opts.model); // 2. user model override
|
|
80
|
+
if (t === undefined && opts.capabilityKey)
|
|
81
|
+
t = opts.user?.capabilities?.[opts.capabilityKey]; // 3. user capability
|
|
82
|
+
if (t === undefined)
|
|
83
|
+
t = lookupTimeoutOverride(builtin.overrides, opts.model); // 4. built-in model default
|
|
84
|
+
if (t === undefined && opts.capabilityKey)
|
|
85
|
+
t = builtin.capabilities[opts.capabilityKey]; // 5. built-in capability
|
|
86
|
+
if (t === undefined)
|
|
87
|
+
t = opts.longRunning ? builtin.longRunning : builtin.default; // 6. global
|
|
88
|
+
const ceiling = opts.user?.max ?? builtin.max;
|
|
89
|
+
return Math.min(Math.max(t, MIN_TIMEOUT_SECONDS), ceiling);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Legacy resolution used by the TCR capability timeout test surface:
|
|
93
|
+
* per-call > model override > capability > (longRunning ? longRunning : default).
|
|
94
|
+
* Result is clamped to [10, hierarchy.max].
|
|
95
|
+
*/
|
|
96
|
+
export function resolveTimeoutSeconds(opts) {
|
|
97
|
+
const hierarchy = opts.hierarchy ?? DEFAULT_TIMEOUT_HIERARCHY;
|
|
98
|
+
let t = opts.longRunning ? hierarchy.longRunning : hierarchy.default;
|
|
99
|
+
if (opts.capability) {
|
|
100
|
+
const capVal = hierarchy.capabilities[opts.capability];
|
|
101
|
+
if (capVal !== undefined)
|
|
102
|
+
t = capVal;
|
|
103
|
+
}
|
|
104
|
+
if (opts.model) {
|
|
105
|
+
const modelOverride = lookupTimeoutOverride(hierarchy.overrides, opts.model);
|
|
106
|
+
if (modelOverride !== undefined)
|
|
107
|
+
t = modelOverride;
|
|
108
|
+
}
|
|
109
|
+
if (opts.perCall !== undefined)
|
|
110
|
+
t = opts.perCall;
|
|
111
|
+
return Math.min(Math.max(t, MIN_TIMEOUT_SECONDS), hierarchy.max);
|
|
112
|
+
}
|
|
113
|
+
/** Merge user-configured timeout overrides (from config) onto the defaults. */
|
|
114
|
+
export function mergeTimeoutHierarchy(user) {
|
|
115
|
+
if (!user)
|
|
116
|
+
return { ...DEFAULT_TIMEOUT_HIERARCHY };
|
|
117
|
+
return {
|
|
118
|
+
default: user.default ?? DEFAULT_TIMEOUT_HIERARCHY.default,
|
|
119
|
+
longRunning: user.longRunning ?? DEFAULT_TIMEOUT_HIERARCHY.longRunning,
|
|
120
|
+
max: user.max ?? DEFAULT_TIMEOUT_HIERARCHY.max,
|
|
121
|
+
capabilities: { ...DEFAULT_TIMEOUT_HIERARCHY.capabilities, ...(user.capabilities || {}) },
|
|
122
|
+
overrides: { ...DEFAULT_TIMEOUT_HIERARCHY.overrides, ...(user.overrides || {}) },
|
|
123
|
+
};
|
|
124
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool Capability Registry (v6.5) — capability-centric, declarative.
|
|
3
|
+
*
|
|
4
|
+
* Answers "HOW to execute this capability" — endpoint, transport, execution
|
|
5
|
+
* mode, timeouts, retry/recovery policy, artifact format, backend overrides.
|
|
6
|
+
*
|
|
7
|
+
* The Model Registry remains model-centric ("WHAT exists"). The TCR
|
|
8
|
+
* references/enriches it and does NOT duplicate model fields (pricing,
|
|
9
|
+
* paid_only, modalities stay in the Model Registry).
|
|
10
|
+
*
|
|
11
|
+
* Overrides that the live catalog does NOT expose (special headers, endpoint
|
|
12
|
+
* quirks, token formats) live here — never as `if (model === ...)` scattered
|
|
13
|
+
* across tools.
|
|
14
|
+
*/
|
|
15
|
+
import { type UserTimeoutConfig } from './timeout-policy.js';
|
|
16
|
+
export type ExecutionMode = 'SHORT_REQUEST' | 'LONG_BLOCKING' | 'ASYNC_JOB' | 'STREAMING' | 'LOCAL';
|
|
17
|
+
export type RetryPolicy = 'NO_AUTOMATIC_RETRY' | 'SAFE_READ_ONLY' | 'RECOVER_SAME_REQUEST' | 'REPOLL_JOB';
|
|
18
|
+
export interface ToolModelCapability {
|
|
19
|
+
capability: string;
|
|
20
|
+
backend: string;
|
|
21
|
+
modelId?: string;
|
|
22
|
+
transport: {
|
|
23
|
+
endpoint: string;
|
|
24
|
+
method: 'GET' | 'POST' | 'WS';
|
|
25
|
+
mode: ExecutionMode;
|
|
26
|
+
};
|
|
27
|
+
inputs?: Record<string, unknown>;
|
|
28
|
+
outputs?: Record<string, unknown>;
|
|
29
|
+
constraints?: Record<string, unknown>;
|
|
30
|
+
pricing?: Record<string, unknown>;
|
|
31
|
+
execution: {
|
|
32
|
+
defaultTimeoutSeconds?: number;
|
|
33
|
+
maxTimeoutSeconds?: number;
|
|
34
|
+
retryPolicy: RetryPolicy;
|
|
35
|
+
idempotency?: 'SERVER_DEDUP' | 'NONE';
|
|
36
|
+
pollIntervalSeconds?: number;
|
|
37
|
+
supportsCancel?: boolean;
|
|
38
|
+
recovery?: string;
|
|
39
|
+
};
|
|
40
|
+
backendOverrides?: Record<string, unknown>;
|
|
41
|
+
}
|
|
42
|
+
export declare const TOOL_CAPABILITIES: ToolModelCapability[];
|
|
43
|
+
/** Resolve a capability declaration by name (optionally filtered by backend). */
|
|
44
|
+
export declare function resolveCapability(capability: string, backend?: string): ToolModelCapability | undefined;
|
|
45
|
+
export declare function listCapabilities(): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Effective timeout for a capability+model with full v6.5 precedence:
|
|
48
|
+
* per-call > USER model override > USER capability override
|
|
49
|
+
* > built-in model default > built-in capability > global.
|
|
50
|
+
*/
|
|
51
|
+
export declare function resolveCapabilityTimeout(capability: string, model?: string, perCall?: number, userConfig?: UserTimeoutConfig | null): number;
|