@stabgan/openrouter-mcp-multimodal 4.6.2 → 4.8.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.md +108 -37
- package/dist/errors.d.ts +5 -20
- package/dist/errors.js +1 -10
- package/dist/index.js +7 -1
- package/dist/logger.js +54 -24
- package/dist/model-cache.d.ts +13 -0
- package/dist/model-cache.js +61 -5
- package/dist/openrouter-api.d.ts +14 -15
- package/dist/openrouter-api.js +68 -22
- package/dist/openrouter-openai-client.d.ts +9 -0
- package/dist/openrouter-openai-client.js +15 -0
- package/dist/tool-definitions.d.ts +24 -0
- package/dist/tool-definitions.js +283 -170
- package/dist/tool-descriptions.js +23 -15
- package/dist/tool-handlers/analyze-audio.js +4 -1
- package/dist/tool-handlers/analyze-image.js +10 -5
- package/dist/tool-handlers/analyze-video.js +9 -5
- package/dist/tool-handlers/async-chat.d.ts +17 -0
- package/dist/tool-handlers/async-chat.js +112 -31
- package/dist/tool-handlers/audio-utils.d.ts +19 -4
- package/dist/tool-handlers/audio-utils.js +170 -16
- package/dist/tool-handlers/cache.d.ts +3 -3
- package/dist/tool-handlers/cache.js +56 -4
- package/dist/tool-handlers/chat-completion.js +16 -7
- package/dist/tool-handlers/chat-request.d.ts +3 -0
- package/dist/tool-handlers/chat-request.js +28 -0
- package/dist/tool-handlers/completion-utils.d.ts +5 -11
- package/dist/tool-handlers/completion-utils.js +76 -47
- package/dist/tool-handlers/fetch-utils.d.ts +14 -0
- package/dist/tool-handlers/fetch-utils.js +331 -68
- package/dist/tool-handlers/generate-audio.d.ts +6 -51
- package/dist/tool-handlers/generate-audio.js +30 -76
- package/dist/tool-handlers/generate-image-dedicated.d.ts +2 -12
- package/dist/tool-handlers/generate-image-dedicated.js +79 -28
- package/dist/tool-handlers/generate-image.d.ts +3 -46
- package/dist/tool-handlers/generate-image.js +26 -43
- package/dist/tool-handlers/generate-video.d.ts +4 -3
- package/dist/tool-handlers/generate-video.js +51 -50
- package/dist/tool-handlers/get-model-info.js +1 -1
- package/dist/tool-handlers/health-check.js +39 -15
- package/dist/tool-handlers/image-utils.js +2 -2
- package/dist/tool-handlers/openrouter-errors.d.ts +2 -0
- package/dist/tool-handlers/openrouter-errors.js +138 -31
- package/dist/tool-handlers/path-safety.d.ts +6 -0
- package/dist/tool-handlers/path-safety.js +77 -14
- package/dist/tool-handlers/path-utils.d.ts +4 -0
- package/dist/tool-handlers/path-utils.js +20 -0
- package/dist/tool-handlers/provider-routing.d.ts +2 -0
- package/dist/tool-handlers/provider-routing.js +10 -0
- package/dist/tool-handlers/rerank.d.ts +1 -4
- package/dist/tool-handlers/rerank.js +43 -14
- package/dist/tool-handlers/search-models.js +3 -3
- package/dist/tool-handlers/speech-to-text.d.ts +1 -0
- package/dist/tool-handlers/speech-to-text.js +23 -56
- package/dist/tool-handlers/text-to-speech.d.ts +2 -12
- package/dist/tool-handlers/text-to-speech.js +29 -22
- package/dist/tool-handlers/tool-result-payload.d.ts +47 -0
- package/dist/tool-handlers/tool-result-payload.js +98 -0
- package/dist/tool-handlers/validate-model.js +1 -1
- package/dist/tool-handlers.d.ts +9 -0
- package/dist/tool-handlers.js +19 -10
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -3
package/dist/tool-definitions.js
CHANGED
|
@@ -1,4 +1,84 @@
|
|
|
1
|
-
import { TOOL_DESCRIPTIONS } from './tool-descriptions.js';
|
|
1
|
+
import { TOOL_DESCRIPTIONS, TOOL_NAMES } from './tool-descriptions.js';
|
|
2
|
+
/** Aspect ratios accepted by generate_image and generate_image_dedicated handlers. */
|
|
3
|
+
export const IMAGE_ASPECT_RATIOS = [
|
|
4
|
+
'1:1',
|
|
5
|
+
'2:3',
|
|
6
|
+
'3:2',
|
|
7
|
+
'3:4',
|
|
8
|
+
'4:3',
|
|
9
|
+
'4:5',
|
|
10
|
+
'5:4',
|
|
11
|
+
'9:16',
|
|
12
|
+
'16:9',
|
|
13
|
+
'21:9',
|
|
14
|
+
'1:4',
|
|
15
|
+
'4:1',
|
|
16
|
+
'1:8',
|
|
17
|
+
'8:1',
|
|
18
|
+
];
|
|
19
|
+
export const IMAGE_SIZES = ['0.5K', '1K', '2K', '4K'];
|
|
20
|
+
export const IMAGE_DEDICATED_RESOLUTIONS = ['512', '0.5K', '1K', '2K', '4K'];
|
|
21
|
+
export const IMAGE_DEDICATED_QUALITIES = ['auto', 'low', 'medium', 'high'];
|
|
22
|
+
export const IMAGE_OUTPUT_FORMATS = ['png', 'jpeg', 'webp', 'svg'];
|
|
23
|
+
/** generate_audio handler VALID_FORMATS */
|
|
24
|
+
export const GENERATE_AUDIO_FORMATS = ['wav', 'mp3', 'flac', 'opus', 'pcm16'];
|
|
25
|
+
/** text_to_speech handler VALID_FORMATS */
|
|
26
|
+
export const TTS_RESPONSE_FORMATS = ['mp3', 'opus', 'aac', 'flac', 'wav', 'pcm'];
|
|
27
|
+
/** speech_to_text handler VALID_RESPONSE_FORMATS */
|
|
28
|
+
export const STT_RESPONSE_FORMATS = ['json', 'text', 'srt', 'verbose_json', 'vtt'];
|
|
29
|
+
export const CHAT_MESSAGE_ROLES = ['system', 'user', 'assistant'];
|
|
30
|
+
export const PROVIDER_SORT_VALUES = ['price', 'throughput', 'latency'];
|
|
31
|
+
export const PROVIDER_DATA_COLLECTION = ['allow', 'deny'];
|
|
32
|
+
/** Tools whose handlers accept save_path (binary artifact output). */
|
|
33
|
+
export const TOOLS_WITH_SAVE_PATH = [
|
|
34
|
+
'generate_image',
|
|
35
|
+
'generate_image_dedicated',
|
|
36
|
+
'generate_audio',
|
|
37
|
+
'text_to_speech',
|
|
38
|
+
'generate_video',
|
|
39
|
+
'generate_video_from_image',
|
|
40
|
+
'get_video_status',
|
|
41
|
+
];
|
|
42
|
+
/** Shared save_path fragment for binary-output tools. */
|
|
43
|
+
export const SAVE_PATH_PROPERTY = {
|
|
44
|
+
type: 'string',
|
|
45
|
+
description: 'Write the artifact under OPENROUTER_OUTPUT_DIR (path-sandboxed). When set, the tool result is text-only with _meta.save_path — no inline media block. ' +
|
|
46
|
+
'When unset, inline image/audio (default 1 MiB) or video (default 10 MiB) is returned only if under the per-kind ceiling: ' +
|
|
47
|
+
'OPENROUTER_IMAGE_INLINE_MAX_BYTES, OPENROUTER_AUDIO_INLINE_MAX_BYTES, OPENROUTER_VIDEO_INLINE_MAX_BYTES ' +
|
|
48
|
+
'(global fallback OPENROUTER_INLINE_MAX_BYTES). See .env.example.',
|
|
49
|
+
};
|
|
50
|
+
const SAVE_PATH_WITH_PREFIX = (prefix) => ({
|
|
51
|
+
...SAVE_PATH_PROPERTY,
|
|
52
|
+
description: `${prefix} ${SAVE_PATH_PROPERTY.description}`,
|
|
53
|
+
});
|
|
54
|
+
const CHAT_MESSAGE_SCHEMA = {
|
|
55
|
+
type: 'array',
|
|
56
|
+
minItems: 1,
|
|
57
|
+
items: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
role: { type: 'string', enum: [...CHAT_MESSAGE_ROLES] },
|
|
61
|
+
content: {
|
|
62
|
+
oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'object' } }],
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
required: ['role', 'content'],
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
const CACHE_PROPERTIES = {
|
|
69
|
+
cache: {
|
|
70
|
+
type: 'boolean',
|
|
71
|
+
description: 'Enable OpenRouter response caching via `X-OpenRouter-Cache: true`. Server default: `OPENROUTER_CACHE_RESPONSES=1`.',
|
|
72
|
+
},
|
|
73
|
+
cache_ttl: {
|
|
74
|
+
type: 'string',
|
|
75
|
+
description: 'Cache TTL as integer seconds (1–86400) or a duration string such as "30s", "5m", or "1h". Sent upstream as seconds.',
|
|
76
|
+
},
|
|
77
|
+
cache_clear: {
|
|
78
|
+
type: 'boolean',
|
|
79
|
+
description: 'Bust the cache entry for this exact request.',
|
|
80
|
+
},
|
|
81
|
+
};
|
|
2
82
|
export const TOOL_DEFINITIONS = [
|
|
3
83
|
{
|
|
4
84
|
name: 'chat_completion',
|
|
@@ -15,26 +95,10 @@ export const TOOL_DEFINITIONS = [
|
|
|
15
95
|
properties: {
|
|
16
96
|
model: {
|
|
17
97
|
type: 'string',
|
|
18
|
-
description: 'Model ID (optional, uses default). Append `:nitro`
|
|
19
|
-
'`:floor` for the cheapest, `:free` for the free tier, `:online` for web search, ' +
|
|
20
|
-
'or `:exacto` for the best tool-calling accuracy. ' +
|
|
21
|
-
'Example: `openai/gpt-4o:nitro`. Or pass `online: true` for programmatic web search control.',
|
|
22
|
-
},
|
|
23
|
-
messages: {
|
|
24
|
-
type: 'array',
|
|
25
|
-
minItems: 1,
|
|
26
|
-
items: {
|
|
27
|
-
type: 'object',
|
|
28
|
-
properties: {
|
|
29
|
-
role: { type: 'string', enum: ['system', 'user', 'assistant'] },
|
|
30
|
-
content: {
|
|
31
|
-
oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'object' } }],
|
|
32
|
-
},
|
|
33
|
-
},
|
|
34
|
-
required: ['role', 'content'],
|
|
35
|
-
},
|
|
98
|
+
description: 'Model ID (optional, uses server default). Append `:nitro` (fastest), `:floor` (cheapest), `:free`, `:online` (web search), or `:exacto` (tool accuracy). Example: `openai/gpt-4o:nitro`. Or pass `online: true` for programmatic web search.',
|
|
36
99
|
},
|
|
37
|
-
|
|
100
|
+
messages: CHAT_MESSAGE_SCHEMA,
|
|
101
|
+
temperature: { type: 'number', minimum: 0, maximum: 2, description: 'Default: 1.' },
|
|
38
102
|
max_tokens: {
|
|
39
103
|
type: 'number',
|
|
40
104
|
minimum: 1,
|
|
@@ -42,8 +106,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
42
106
|
},
|
|
43
107
|
provider: {
|
|
44
108
|
type: 'object',
|
|
45
|
-
description: 'OpenRouter provider-routing overrides. Merges on top of `OPENROUTER_PROVIDER_*` env defaults. '
|
|
46
|
-
'See https://openrouter.ai/docs/features/provider-routing',
|
|
109
|
+
description: 'OpenRouter provider-routing overrides. Merges on top of `OPENROUTER_PROVIDER_*` env defaults. See https://openrouter.ai/docs/features/provider-routing',
|
|
47
110
|
properties: {
|
|
48
111
|
quantizations: {
|
|
49
112
|
type: 'array',
|
|
@@ -55,19 +118,16 @@ export const TOOL_DEFINITIONS = [
|
|
|
55
118
|
items: { type: 'string' },
|
|
56
119
|
description: 'Exclude these provider slugs.',
|
|
57
120
|
},
|
|
58
|
-
sort: {
|
|
59
|
-
type: 'string',
|
|
60
|
-
enum: ['price', 'throughput', 'latency'],
|
|
61
|
-
},
|
|
121
|
+
sort: { type: 'string', enum: [...PROVIDER_SORT_VALUES] },
|
|
62
122
|
order: { type: 'array', items: { type: 'string' } },
|
|
63
123
|
require_parameters: { type: 'boolean' },
|
|
64
|
-
data_collection: { type: 'string', enum: [
|
|
124
|
+
data_collection: { type: 'string', enum: [...PROVIDER_DATA_COLLECTION] },
|
|
65
125
|
allow_fallbacks: { type: 'boolean' },
|
|
66
126
|
},
|
|
67
127
|
},
|
|
68
128
|
include_reasoning: {
|
|
69
129
|
type: 'boolean',
|
|
70
|
-
description: "Surface the model's chain-of-thought on `_meta.reasoning` for R1 / Opus
|
|
130
|
+
description: "Surface the model's chain-of-thought on `_meta.reasoning` for R1 / Opus / Gemini Thinking models.",
|
|
71
131
|
},
|
|
72
132
|
online: {
|
|
73
133
|
type: 'boolean',
|
|
@@ -78,19 +138,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
78
138
|
minimum: 1,
|
|
79
139
|
description: 'Max web-search results when `online: true` (default 5).',
|
|
80
140
|
},
|
|
81
|
-
|
|
82
|
-
type: 'boolean',
|
|
83
|
-
description: 'Enable OpenRouter response caching via `X-OpenRouter-Cache: true`. ' +
|
|
84
|
-
'Server-wide default settable via `OPENROUTER_CACHE_RESPONSES=1`.',
|
|
85
|
-
},
|
|
86
|
-
cache_ttl: {
|
|
87
|
-
type: 'string',
|
|
88
|
-
description: 'Cache TTL (e.g. `"5m"`, `"1h"`, `"24h"`; 1s-24h range).',
|
|
89
|
-
},
|
|
90
|
-
cache_clear: {
|
|
91
|
-
type: 'boolean',
|
|
92
|
-
description: 'Bust the cache entry for this exact request.',
|
|
93
|
-
},
|
|
141
|
+
...CACHE_PROPERTIES,
|
|
94
142
|
},
|
|
95
143
|
required: ['messages'],
|
|
96
144
|
},
|
|
@@ -108,30 +156,15 @@ export const TOOL_DEFINITIONS = [
|
|
|
108
156
|
inputSchema: {
|
|
109
157
|
type: 'object',
|
|
110
158
|
properties: {
|
|
111
|
-
model: { type: 'string', description: 'Model ID (same as chat_completion).' },
|
|
112
|
-
messages:
|
|
113
|
-
type: 'array',
|
|
114
|
-
minItems: 1,
|
|
115
|
-
items: {
|
|
116
|
-
type: 'object',
|
|
117
|
-
properties: {
|
|
118
|
-
role: { type: 'string', enum: ['system', 'user', 'assistant'] },
|
|
119
|
-
content: {
|
|
120
|
-
oneOf: [{ type: 'string' }, { type: 'array', items: { type: 'object' } }],
|
|
121
|
-
},
|
|
122
|
-
},
|
|
123
|
-
required: ['role', 'content'],
|
|
124
|
-
},
|
|
125
|
-
},
|
|
159
|
+
model: { type: 'string', description: 'Model ID (same options as chat_completion).' },
|
|
160
|
+
messages: CHAT_MESSAGE_SCHEMA,
|
|
126
161
|
temperature: { type: 'number', minimum: 0, maximum: 2 },
|
|
127
162
|
max_tokens: { type: 'number', minimum: 1 },
|
|
128
163
|
provider: { type: 'object' },
|
|
129
164
|
include_reasoning: { type: 'boolean' },
|
|
130
165
|
online: { type: 'boolean' },
|
|
131
166
|
web_max_results: { type: 'number', minimum: 1 },
|
|
132
|
-
|
|
133
|
-
cache_ttl: { type: 'string' },
|
|
134
|
-
cache_clear: { type: 'boolean' },
|
|
167
|
+
...CACHE_PROPERTIES,
|
|
135
168
|
},
|
|
136
169
|
required: ['messages'],
|
|
137
170
|
},
|
|
@@ -172,23 +205,21 @@ export const TOOL_DEFINITIONS = [
|
|
|
172
205
|
properties: {
|
|
173
206
|
image_path: {
|
|
174
207
|
type: 'string',
|
|
175
|
-
description: '
|
|
176
|
-
'Good: `"photo.jpg"`. Bad: `"url": "..."` (wrong key), `"/etc/passwd"` (UNSAFE_PATH).',
|
|
208
|
+
description: 'Local path (OPENROUTER_INPUT_DIR sandbox), https URL, or data URL. Good: `"photo.jpg"`. Bad: `"url": "..."` (wrong key), `"/etc/passwd"` (UNSAFE_PATH).',
|
|
177
209
|
},
|
|
178
210
|
question: {
|
|
179
211
|
type: 'string',
|
|
180
|
-
description: 'Optional question
|
|
181
|
-
|
|
212
|
+
description: 'Optional question. Default: "What\'s in this image?". Bad: using `prompt` (wrong key for this tool).',
|
|
213
|
+
},
|
|
214
|
+
model: {
|
|
215
|
+
type: 'string',
|
|
216
|
+
description: 'Vision model ID (optional; server default is a free multimodal model).',
|
|
182
217
|
},
|
|
183
|
-
model: { type: 'string' },
|
|
184
218
|
cache_input: {
|
|
185
219
|
type: 'boolean',
|
|
186
|
-
description: 'Attach `cache_control: ephemeral` to the image block
|
|
187
|
-
'Repeat questions about the same image save ~10x on Anthropic.',
|
|
220
|
+
description: 'Attach `cache_control: ephemeral` to the image block for Anthropic / Gemini prompt caching.',
|
|
188
221
|
},
|
|
189
|
-
|
|
190
|
-
cache_ttl: { type: 'string' },
|
|
191
|
-
cache_clear: { type: 'boolean' },
|
|
222
|
+
...CACHE_PROPERTIES,
|
|
192
223
|
},
|
|
193
224
|
required: ['image_path'],
|
|
194
225
|
},
|
|
@@ -208,18 +239,18 @@ export const TOOL_DEFINITIONS = [
|
|
|
208
239
|
properties: {
|
|
209
240
|
audio_path: {
|
|
210
241
|
type: 'string',
|
|
211
|
-
description: 'Local file path (sandboxed
|
|
212
|
-
'http(s) URL, or data URL (base64-encoded audio)',
|
|
242
|
+
description: 'Local file path (sandboxed), http(s) URL, or data URL (base64-encoded audio).',
|
|
213
243
|
},
|
|
214
244
|
question: {
|
|
215
245
|
type: 'string',
|
|
216
|
-
description: 'Question or instruction
|
|
246
|
+
description: 'Question or instruction. Default: "Please transcribe and analyze this audio file."',
|
|
247
|
+
},
|
|
248
|
+
model: {
|
|
249
|
+
type: 'string',
|
|
250
|
+
description: 'Multimodal model ID (default: google/gemini-2.5-flash).',
|
|
217
251
|
},
|
|
218
|
-
model: { type: 'string' },
|
|
219
252
|
cache_input: { type: 'boolean' },
|
|
220
|
-
|
|
221
|
-
cache_ttl: { type: 'string' },
|
|
222
|
-
cache_clear: { type: 'boolean' },
|
|
253
|
+
...CACHE_PROPERTIES,
|
|
223
254
|
},
|
|
224
255
|
required: ['audio_path'],
|
|
225
256
|
},
|
|
@@ -239,15 +270,18 @@ export const TOOL_DEFINITIONS = [
|
|
|
239
270
|
properties: {
|
|
240
271
|
video_path: {
|
|
241
272
|
type: 'string',
|
|
242
|
-
description: 'Local file path (sandboxed
|
|
243
|
-
|
|
273
|
+
description: 'Local file path (sandboxed), http(s) URL, or base64 data URL. Supported containers: mp4, mpeg, mov, webm.',
|
|
274
|
+
},
|
|
275
|
+
question: {
|
|
276
|
+
type: 'string',
|
|
277
|
+
description: 'Optional question. Default: "Describe what happens in this video, step by step."',
|
|
278
|
+
},
|
|
279
|
+
model: {
|
|
280
|
+
type: 'string',
|
|
281
|
+
description: 'Video-capable model ID (default: google/gemini-2.5-flash).',
|
|
244
282
|
},
|
|
245
|
-
question: { type: 'string' },
|
|
246
|
-
model: { type: 'string' },
|
|
247
283
|
cache_input: { type: 'boolean' },
|
|
248
|
-
|
|
249
|
-
cache_ttl: { type: 'string' },
|
|
250
|
-
cache_clear: { type: 'boolean' },
|
|
284
|
+
...CACHE_PROPERTIES,
|
|
251
285
|
},
|
|
252
286
|
required: ['video_path'],
|
|
253
287
|
},
|
|
@@ -265,8 +299,11 @@ export const TOOL_DEFINITIONS = [
|
|
|
265
299
|
inputSchema: {
|
|
266
300
|
type: 'object',
|
|
267
301
|
properties: {
|
|
268
|
-
query: { type: 'string' },
|
|
269
|
-
provider: {
|
|
302
|
+
query: { type: 'string', description: 'Substring match against model id or name.' },
|
|
303
|
+
provider: {
|
|
304
|
+
type: 'string',
|
|
305
|
+
description: 'Filter by provider slug prefix (e.g. `google`).',
|
|
306
|
+
},
|
|
270
307
|
capabilities: {
|
|
271
308
|
type: 'object',
|
|
272
309
|
properties: {
|
|
@@ -275,8 +312,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
275
312
|
video: { type: 'boolean' },
|
|
276
313
|
},
|
|
277
314
|
},
|
|
278
|
-
limit: {
|
|
279
|
-
|
|
315
|
+
limit: {
|
|
316
|
+
type: 'number',
|
|
317
|
+
minimum: 1,
|
|
318
|
+
maximum: 50,
|
|
319
|
+
description: 'Page size (default 20, max 50).',
|
|
320
|
+
},
|
|
321
|
+
offset: { type: 'number', minimum: 0, description: 'Pagination offset (default 0).' },
|
|
280
322
|
},
|
|
281
323
|
},
|
|
282
324
|
outputSchema: {
|
|
@@ -304,7 +346,12 @@ export const TOOL_DEFINITIONS = [
|
|
|
304
346
|
},
|
|
305
347
|
inputSchema: {
|
|
306
348
|
type: 'object',
|
|
307
|
-
properties: {
|
|
349
|
+
properties: {
|
|
350
|
+
model: {
|
|
351
|
+
type: 'string',
|
|
352
|
+
description: 'Full OpenRouter model slug (e.g. `openai/gpt-4o`).',
|
|
353
|
+
},
|
|
354
|
+
},
|
|
308
355
|
required: ['model'],
|
|
309
356
|
},
|
|
310
357
|
outputSchema: {
|
|
@@ -330,7 +377,9 @@ export const TOOL_DEFINITIONS = [
|
|
|
330
377
|
},
|
|
331
378
|
inputSchema: {
|
|
332
379
|
type: 'object',
|
|
333
|
-
properties: {
|
|
380
|
+
properties: {
|
|
381
|
+
model: { type: 'string', description: 'Full OpenRouter model slug to check.' },
|
|
382
|
+
},
|
|
334
383
|
required: ['model'],
|
|
335
384
|
},
|
|
336
385
|
outputSchema: {
|
|
@@ -355,32 +404,33 @@ export const TOOL_DEFINITIONS = [
|
|
|
355
404
|
inputSchema: {
|
|
356
405
|
type: 'object',
|
|
357
406
|
properties: {
|
|
358
|
-
prompt: { type: 'string' },
|
|
359
|
-
model: {
|
|
407
|
+
prompt: { type: 'string', description: 'Text prompt describing the image to generate.' },
|
|
408
|
+
model: {
|
|
409
|
+
type: 'string',
|
|
410
|
+
description: 'Image model ID (default: google/gemini-2.5-flash-image). Chat-completions route.',
|
|
411
|
+
},
|
|
360
412
|
aspect_ratio: {
|
|
361
413
|
type: 'string',
|
|
362
|
-
enum: [
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
input_images: { type: 'array', items: { type: 'string' } },
|
|
383
|
-
modalities: { type: 'array', items: { type: 'string' } },
|
|
414
|
+
enum: [...IMAGE_ASPECT_RATIOS],
|
|
415
|
+
description: 'Optional aspect ratio (provider-dependent).',
|
|
416
|
+
},
|
|
417
|
+
image_size: {
|
|
418
|
+
type: 'string',
|
|
419
|
+
enum: [...IMAGE_SIZES],
|
|
420
|
+
description: 'Optional resolution tier for supported models.',
|
|
421
|
+
},
|
|
422
|
+
max_tokens: { type: 'number', minimum: 1, description: 'Optional completion token cap.' },
|
|
423
|
+
save_path: SAVE_PATH_PROPERTY,
|
|
424
|
+
input_images: {
|
|
425
|
+
type: 'array',
|
|
426
|
+
items: { type: 'string' },
|
|
427
|
+
description: 'Reference images (local path, URL, or data URL) for style/identity conditioning.',
|
|
428
|
+
},
|
|
429
|
+
modalities: {
|
|
430
|
+
type: 'array',
|
|
431
|
+
items: { type: 'string' },
|
|
432
|
+
description: 'Response modalities (default: `["image","text"]`).',
|
|
433
|
+
},
|
|
384
434
|
},
|
|
385
435
|
required: ['prompt'],
|
|
386
436
|
},
|
|
@@ -404,46 +454,45 @@ export const TOOL_DEFINITIONS = [
|
|
|
404
454
|
},
|
|
405
455
|
model: {
|
|
406
456
|
type: 'string',
|
|
407
|
-
description: 'Image model ID. Default: google/gemini-2.5-flash-image. Browse
|
|
457
|
+
description: 'Image model ID. Default: google/gemini-2.5-flash-image. Browse: https://openrouter.ai/collections/image-models',
|
|
408
458
|
},
|
|
409
459
|
resolution: {
|
|
410
460
|
type: 'string',
|
|
411
|
-
enum: [
|
|
461
|
+
enum: [...IMAGE_DEDICATED_RESOLUTIONS],
|
|
412
462
|
description: 'Normalized resolution tier. Provider maps to closest supported size.',
|
|
413
463
|
},
|
|
414
464
|
aspect_ratio: {
|
|
415
465
|
type: 'string',
|
|
416
|
-
|
|
466
|
+
enum: [...IMAGE_ASPECT_RATIOS],
|
|
467
|
+
description: 'Aspect ratio (same enum as generate_image).',
|
|
417
468
|
},
|
|
418
469
|
quality: {
|
|
419
470
|
type: 'string',
|
|
420
|
-
enum: [
|
|
421
|
-
description: 'Image quality level.',
|
|
471
|
+
enum: [...IMAGE_DEDICATED_QUALITIES],
|
|
472
|
+
description: 'Image quality level (default: auto).',
|
|
422
473
|
},
|
|
423
474
|
output_format: {
|
|
424
475
|
type: 'string',
|
|
425
|
-
enum: [
|
|
476
|
+
enum: [...IMAGE_OUTPUT_FORMATS],
|
|
426
477
|
description: 'Output image format.',
|
|
427
478
|
},
|
|
428
479
|
n: {
|
|
429
480
|
type: 'number',
|
|
430
481
|
minimum: 1,
|
|
431
482
|
maximum: 10,
|
|
432
|
-
description: 'Number of images to
|
|
483
|
+
description: 'Number of images to request (default 1; only images[0] is saved/inlined).',
|
|
433
484
|
},
|
|
434
485
|
input_references: {
|
|
435
486
|
type: 'array',
|
|
436
487
|
items: { type: 'string' },
|
|
437
|
-
description: 'Reference images for image-to-image
|
|
488
|
+
description: 'Reference images for image-to-image. Each entry: local path, http(s) URL, or data URL.',
|
|
438
489
|
},
|
|
439
|
-
save_path:
|
|
490
|
+
save_path: SAVE_PATH_WITH_PREFIX('Save generated image to this path.'),
|
|
440
491
|
provider: {
|
|
441
492
|
type: 'object',
|
|
442
493
|
description: 'Provider routing overrides (order, sort, allow_fallbacks, etc.).',
|
|
443
494
|
},
|
|
444
|
-
|
|
445
|
-
cache_ttl: { type: 'string' },
|
|
446
|
-
cache_clear: { type: 'boolean' },
|
|
495
|
+
...CACHE_PROPERTIES,
|
|
447
496
|
},
|
|
448
497
|
required: ['prompt'],
|
|
449
498
|
},
|
|
@@ -461,11 +510,21 @@ export const TOOL_DEFINITIONS = [
|
|
|
461
510
|
inputSchema: {
|
|
462
511
|
type: 'object',
|
|
463
512
|
properties: {
|
|
464
|
-
prompt: { type: 'string' },
|
|
465
|
-
model: {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
513
|
+
prompt: { type: 'string', description: 'Text prompt for speech or music generation.' },
|
|
514
|
+
model: {
|
|
515
|
+
type: 'string',
|
|
516
|
+
description: 'Chat-completions audio model (default: openai/gpt-audio).',
|
|
517
|
+
},
|
|
518
|
+
voice: {
|
|
519
|
+
type: 'string',
|
|
520
|
+
description: 'Voice ID (default: alloy). Model-specific.',
|
|
521
|
+
},
|
|
522
|
+
format: {
|
|
523
|
+
type: 'string',
|
|
524
|
+
enum: [...GENERATE_AUDIO_FORMATS],
|
|
525
|
+
description: 'Output audio format (default: pcm16, auto-wrapped as WAV when needed).',
|
|
526
|
+
},
|
|
527
|
+
save_path: SAVE_PATH_PROPERTY,
|
|
469
528
|
},
|
|
470
529
|
required: ['prompt'],
|
|
471
530
|
},
|
|
@@ -497,23 +556,21 @@ export const TOOL_DEFINITIONS = [
|
|
|
497
556
|
},
|
|
498
557
|
response_format: {
|
|
499
558
|
type: 'string',
|
|
500
|
-
enum: [
|
|
559
|
+
enum: [...TTS_RESPONSE_FORMATS],
|
|
501
560
|
description: 'Output audio format. Default: mp3.',
|
|
502
561
|
},
|
|
503
562
|
speed: {
|
|
504
563
|
type: 'number',
|
|
505
564
|
minimum: 0.25,
|
|
506
565
|
maximum: 4.0,
|
|
507
|
-
description: 'Speed of speech (0.25
|
|
566
|
+
description: 'Speed of speech (0.25–4.0). Default: 1.0.',
|
|
508
567
|
},
|
|
509
568
|
instructions: {
|
|
510
569
|
type: 'string',
|
|
511
570
|
description: 'Tone/style instructions (e.g. "speak in a warm, friendly tone"). OpenAI models only.',
|
|
512
571
|
},
|
|
513
|
-
save_path:
|
|
514
|
-
|
|
515
|
-
cache_ttl: { type: 'string' },
|
|
516
|
-
cache_clear: { type: 'boolean' },
|
|
572
|
+
save_path: SAVE_PATH_WITH_PREFIX('Save audio to this path.'),
|
|
573
|
+
...CACHE_PROPERTIES,
|
|
517
574
|
},
|
|
518
575
|
required: ['input'],
|
|
519
576
|
},
|
|
@@ -533,7 +590,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
533
590
|
properties: {
|
|
534
591
|
audio_path: {
|
|
535
592
|
type: 'string',
|
|
536
|
-
description: 'Audio file: local path (sandboxed), http(s) URL, or base64 data URL. Formats: mp3, wav, flac, ogg, webm, mp4.',
|
|
593
|
+
description: 'Audio file: local path (sandboxed), http(s) URL, or base64 data URL. Formats: mp3, wav, flac, ogg, webm, mp4, m4a.',
|
|
537
594
|
},
|
|
538
595
|
model: {
|
|
539
596
|
type: 'string',
|
|
@@ -545,18 +602,16 @@ export const TOOL_DEFINITIONS = [
|
|
|
545
602
|
},
|
|
546
603
|
response_format: {
|
|
547
604
|
type: 'string',
|
|
548
|
-
enum: [
|
|
605
|
+
enum: [...STT_RESPONSE_FORMATS],
|
|
549
606
|
description: 'Output format for transcription. Default: json.',
|
|
550
607
|
},
|
|
551
608
|
temperature: {
|
|
552
609
|
type: 'number',
|
|
553
610
|
minimum: 0,
|
|
554
611
|
maximum: 1,
|
|
555
|
-
description: 'Sampling temperature for transcription (0
|
|
612
|
+
description: 'Sampling temperature for transcription (0–1).',
|
|
556
613
|
},
|
|
557
|
-
|
|
558
|
-
cache_ttl: { type: 'string' },
|
|
559
|
-
cache_clear: { type: 'boolean' },
|
|
614
|
+
...CACHE_PROPERTIES,
|
|
560
615
|
},
|
|
561
616
|
required: ['audio_path'],
|
|
562
617
|
},
|
|
@@ -574,19 +629,50 @@ export const TOOL_DEFINITIONS = [
|
|
|
574
629
|
inputSchema: {
|
|
575
630
|
type: 'object',
|
|
576
631
|
properties: {
|
|
577
|
-
prompt: { type: 'string' },
|
|
578
|
-
model: {
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
632
|
+
prompt: { type: 'string', description: 'Text prompt describing the video to generate.' },
|
|
633
|
+
model: {
|
|
634
|
+
type: 'string',
|
|
635
|
+
description: 'Video model ID (default: google/veo-3.1). Passed through to provider.',
|
|
636
|
+
},
|
|
637
|
+
resolution: {
|
|
638
|
+
type: 'string',
|
|
639
|
+
description: 'Provider-specific resolution (e.g. "720p", "1080p"). No server-side enum.',
|
|
640
|
+
},
|
|
641
|
+
aspect_ratio: {
|
|
642
|
+
type: 'string',
|
|
643
|
+
description: 'Provider-specific aspect ratio (e.g. "16:9", "9:16"). No server-side enum.',
|
|
644
|
+
},
|
|
645
|
+
duration: {
|
|
646
|
+
type: 'number',
|
|
647
|
+
minimum: 1,
|
|
648
|
+
description: 'Clip duration in seconds (provider-dependent).',
|
|
649
|
+
},
|
|
650
|
+
seed: { type: 'number', description: 'Optional reproducibility seed.' },
|
|
651
|
+
first_frame_image: {
|
|
652
|
+
type: 'string',
|
|
653
|
+
description: 'Optional first-frame image (path, URL, or data URL).',
|
|
654
|
+
},
|
|
655
|
+
last_frame_image: {
|
|
656
|
+
type: 'string',
|
|
657
|
+
description: 'Optional last-frame image (path, URL, or data URL).',
|
|
658
|
+
},
|
|
659
|
+
reference_images: {
|
|
660
|
+
type: 'array',
|
|
661
|
+
items: { type: 'string' },
|
|
662
|
+
description: 'Optional reference images for style/subject guidance.',
|
|
663
|
+
},
|
|
664
|
+
provider: { type: 'object', description: 'Provider routing overrides.' },
|
|
665
|
+
save_path: SAVE_PATH_PROPERTY,
|
|
666
|
+
max_wait_ms: {
|
|
667
|
+
type: 'number',
|
|
668
|
+
minimum: 100,
|
|
669
|
+
description: 'Max time to poll before returning JOB_STILL_RUNNING (ms). Default: 600000 (10 min) via OPENROUTER_VIDEO_MAX_WAIT_MS.',
|
|
670
|
+
},
|
|
671
|
+
poll_interval_ms: {
|
|
672
|
+
type: 'number',
|
|
673
|
+
minimum: 50,
|
|
674
|
+
description: 'Poll interval while waiting (ms). Default: 15000 via OPENROUTER_VIDEO_POLL_INTERVAL_MS.',
|
|
675
|
+
},
|
|
590
676
|
},
|
|
591
677
|
required: ['prompt'],
|
|
592
678
|
},
|
|
@@ -608,15 +694,23 @@ export const TOOL_DEFINITIONS = [
|
|
|
608
694
|
type: 'string',
|
|
609
695
|
description: 'First-frame image (path, URL, or data URL). Required.',
|
|
610
696
|
},
|
|
611
|
-
prompt: { type: 'string' },
|
|
612
|
-
model: { type: 'string' },
|
|
613
|
-
resolution: { type: 'string' },
|
|
614
|
-
aspect_ratio: { type: 'string' },
|
|
615
|
-
duration: { type: 'number', minimum: 1 },
|
|
616
|
-
seed: { type: 'number' },
|
|
617
|
-
save_path:
|
|
618
|
-
max_wait_ms: {
|
|
619
|
-
|
|
697
|
+
prompt: { type: 'string', description: 'Motion/scene prompt describing the video.' },
|
|
698
|
+
model: { type: 'string', description: 'Video model ID (default: google/veo-3.1).' },
|
|
699
|
+
resolution: { type: 'string', description: 'Provider-specific resolution.' },
|
|
700
|
+
aspect_ratio: { type: 'string', description: 'Provider-specific aspect ratio.' },
|
|
701
|
+
duration: { type: 'number', minimum: 1, description: 'Clip duration in seconds.' },
|
|
702
|
+
seed: { type: 'number', description: 'Optional reproducibility seed.' },
|
|
703
|
+
save_path: SAVE_PATH_PROPERTY,
|
|
704
|
+
max_wait_ms: {
|
|
705
|
+
type: 'number',
|
|
706
|
+
minimum: 100,
|
|
707
|
+
description: 'Max poll wait (ms) before JOB_STILL_RUNNING. Default: 600000.',
|
|
708
|
+
},
|
|
709
|
+
poll_interval_ms: {
|
|
710
|
+
type: 'number',
|
|
711
|
+
minimum: 50,
|
|
712
|
+
description: 'Poll interval (ms). Default: 15000.',
|
|
713
|
+
},
|
|
620
714
|
},
|
|
621
715
|
required: ['image', 'prompt'],
|
|
622
716
|
},
|
|
@@ -634,8 +728,11 @@ export const TOOL_DEFINITIONS = [
|
|
|
634
728
|
inputSchema: {
|
|
635
729
|
type: 'object',
|
|
636
730
|
properties: {
|
|
637
|
-
video_id: {
|
|
638
|
-
|
|
731
|
+
video_id: {
|
|
732
|
+
type: 'string',
|
|
733
|
+
description: 'Video job id from generate_video / generate_video_from_image.',
|
|
734
|
+
},
|
|
735
|
+
save_path: SAVE_PATH_PROPERTY,
|
|
639
736
|
},
|
|
640
737
|
required: ['video_id'],
|
|
641
738
|
},
|
|
@@ -653,11 +750,17 @@ export const TOOL_DEFINITIONS = [
|
|
|
653
750
|
inputSchema: {
|
|
654
751
|
type: 'object',
|
|
655
752
|
properties: {
|
|
656
|
-
query: { type: 'string' },
|
|
753
|
+
query: { type: 'string', description: 'Search query to rank documents against.' },
|
|
657
754
|
documents: { type: 'array', items: { type: 'string' }, minItems: 1 },
|
|
658
|
-
model: {
|
|
659
|
-
|
|
660
|
-
|
|
755
|
+
model: {
|
|
756
|
+
type: 'string',
|
|
757
|
+
description: 'Reranker model (default: cohere/rerank-english-v3.0).',
|
|
758
|
+
},
|
|
759
|
+
top_n: { type: 'number', minimum: 1, description: 'Return only the top N results.' },
|
|
760
|
+
return_documents: {
|
|
761
|
+
type: 'boolean',
|
|
762
|
+
description: 'When true, include original document text in each result.',
|
|
763
|
+
},
|
|
661
764
|
},
|
|
662
765
|
required: ['query', 'documents'],
|
|
663
766
|
},
|
|
@@ -706,3 +809,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
706
809
|
},
|
|
707
810
|
},
|
|
708
811
|
];
|
|
812
|
+
/** Tool names from definitions — must match TOOL_NAMES in tool-descriptions.ts. */
|
|
813
|
+
export const TOOL_DEFINITION_NAMES = TOOL_DEFINITIONS.map((t) => t.name);
|
|
814
|
+
if (TOOL_DEFINITION_NAMES.length !== TOOL_NAMES.length) {
|
|
815
|
+
throw new Error(`Tool count mismatch: definitions=${TOOL_DEFINITION_NAMES.length} descriptions=${TOOL_NAMES.length}`);
|
|
816
|
+
}
|
|
817
|
+
for (const name of TOOL_NAMES) {
|
|
818
|
+
if (!TOOL_DEFINITION_NAMES.includes(name)) {
|
|
819
|
+
throw new Error(`Missing tool definition for ${name}`);
|
|
820
|
+
}
|
|
821
|
+
}
|