@runpod/ai-sdk-provider 0.8.1 → 0.10.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/CHANGELOG.md +12 -0
- package/README.md +368 -362
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +8 -2
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @runpod/ai-sdk-provider
|
|
2
2
|
|
|
3
|
+
## 0.10.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 3ca13ef: Add support for deepcogito/cogito-671b-v2.1-fp8 model. Users can now use either `deepcogito/cogito-671b-v2.1-fp8` (lowercase) or `deepcogito/cogito-671b-v2.1-FP8` (uppercase) - both will work correctly. The model supports text generation, streaming, object generation, and tool calling.
|
|
8
|
+
|
|
9
|
+
## 0.9.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 77439af: Add support for `openai/gpt-oss-120b` model. Users can now use this 120B parameter open-source GPT model for text generation and tool calling. The model supports streaming and tool usage.
|
|
14
|
+
|
|
3
15
|
## 0.8.1
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,362 +1,368 @@
|
|
|
1
|
-
# Runpod AI SDK Provider
|
|
2
|
-
|
|
3
|
-
The **Runpod provider** for the [AI SDK](https://ai-sdk.dev/docs) contains language model and image generation support for [Runpod's](https://runpod.io) public endpoints.
|
|
4
|
-
|
|
5
|
-
## Setup
|
|
6
|
-
|
|
7
|
-
The Runpod provider is available in the `@runpod/ai-sdk-provider` module. You can install it with:
|
|
8
|
-
|
|
9
|
-
```bash
|
|
10
|
-
# npm
|
|
11
|
-
npm install @runpod/ai-sdk-provider
|
|
12
|
-
|
|
13
|
-
# pnpm
|
|
14
|
-
pnpm add @runpod/ai-sdk-provider
|
|
15
|
-
|
|
16
|
-
# yarn
|
|
17
|
-
yarn add @runpod/ai-sdk-provider
|
|
18
|
-
|
|
19
|
-
# bun
|
|
20
|
-
bun add @runpod/ai-sdk-provider
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Provider Instance
|
|
24
|
-
|
|
25
|
-
You can import the default provider instance `runpod` from `@runpod/ai-sdk-provider`:
|
|
26
|
-
|
|
27
|
-
```ts
|
|
28
|
-
import { runpod } from '@runpod/ai-sdk-provider';
|
|
29
|
-
```
|
|
30
|
-
|
|
31
|
-
If you need a customized setup, you can import `createRunpod` and create a provider instance with your settings:
|
|
32
|
-
|
|
33
|
-
```ts
|
|
34
|
-
import { createRunpod } from '@runpod/ai-sdk-provider';
|
|
35
|
-
|
|
36
|
-
const runpod = createRunpod({
|
|
37
|
-
apiKey: 'your-api-key', // optional, defaults to RUNPOD_API_KEY environment variable
|
|
38
|
-
baseURL: 'custom-url', // optional, for custom endpoints
|
|
39
|
-
headers: {
|
|
40
|
-
/* custom headers */
|
|
41
|
-
}, // optional
|
|
42
|
-
});
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
You can use the following optional settings to customize the Runpod provider instance:
|
|
46
|
-
|
|
47
|
-
- **baseURL** _string_
|
|
48
|
-
|
|
49
|
-
Use a different URL prefix for API calls, e.g. to use proxy servers or custom endpoints.
|
|
50
|
-
Supports vLLM deployments, SGLang servers, and any OpenAI-compatible API.
|
|
51
|
-
The default prefix is `https://api.runpod.ai/v2`.
|
|
52
|
-
|
|
53
|
-
- **apiKey** _string_
|
|
54
|
-
|
|
55
|
-
API key that is being sent using the `Authorization` header.
|
|
56
|
-
It defaults to the `RUNPOD_API_KEY` environment variable.
|
|
57
|
-
You can obtain your api key from the [Runpod Console](https://console.runpod.io/user/settings) under "API Keys".
|
|
58
|
-
|
|
59
|
-
- **headers** _Record<string,string>_
|
|
60
|
-
|
|
61
|
-
Custom headers to include in the requests.
|
|
62
|
-
|
|
63
|
-
- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
|
|
64
|
-
|
|
65
|
-
Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
|
|
66
|
-
You can use it as a middleware to intercept requests,
|
|
67
|
-
or to provide a custom fetch implementation for e.g. testing.
|
|
68
|
-
|
|
69
|
-
## Language Models
|
|
70
|
-
|
|
71
|
-
You can create language models using the provider instance. The first argument is the model ID:
|
|
72
|
-
|
|
73
|
-
```ts
|
|
74
|
-
import { runpod } from '@runpod/ai-sdk-provider';
|
|
75
|
-
import { generateText } from 'ai';
|
|
76
|
-
|
|
77
|
-
const { text } = await generateText({
|
|
78
|
-
model: runpod('qwen/qwen3-32b-awq'),
|
|
79
|
-
prompt: 'What is the capital of Germany?',
|
|
80
|
-
});
|
|
81
|
-
```
|
|
82
|
-
|
|
83
|
-
**Returns:**
|
|
84
|
-
|
|
85
|
-
- `text` - Generated text string
|
|
86
|
-
- `finishReason` - Why generation stopped ('stop', 'length', etc.)
|
|
87
|
-
- `usage` - Token usage information (prompt, completion, total tokens)
|
|
88
|
-
|
|
89
|
-
### Streaming
|
|
90
|
-
|
|
91
|
-
```ts
|
|
92
|
-
import { runpod } from '@runpod/ai-sdk-provider';
|
|
93
|
-
import { streamText } from 'ai';
|
|
94
|
-
|
|
95
|
-
const { textStream } = await streamText({
|
|
96
|
-
model: runpod('qwen/qwen3-32b-awq'),
|
|
97
|
-
prompt:
|
|
98
|
-
'Write a short poem about artificial intelligence in exactly 4 lines.',
|
|
99
|
-
temperature: 0.7,
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
for await (const delta of textStream) {
|
|
103
|
-
process.stdout.write(delta);
|
|
104
|
-
}
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
### Model Capabilities
|
|
108
|
-
|
|
109
|
-
| Model ID
|
|
110
|
-
|
|
|
111
|
-
| `qwen/qwen3-32b-awq`
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
|
228
|
-
|
|
|
229
|
-
| `
|
|
230
|
-
| `
|
|
231
|
-
| `
|
|
232
|
-
| `
|
|
233
|
-
| `
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
|
353
|
-
|
|
|
354
|
-
| `
|
|
355
|
-
| `
|
|
356
|
-
| `
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
1
|
+
# Runpod AI SDK Provider
|
|
2
|
+
|
|
3
|
+
The **Runpod provider** for the [AI SDK](https://ai-sdk.dev/docs) contains language model and image generation support for [Runpod's](https://runpod.io) public endpoints.
|
|
4
|
+
|
|
5
|
+
## Setup
|
|
6
|
+
|
|
7
|
+
The Runpod provider is available in the `@runpod/ai-sdk-provider` module. You can install it with:
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# npm
|
|
11
|
+
npm install @runpod/ai-sdk-provider
|
|
12
|
+
|
|
13
|
+
# pnpm
|
|
14
|
+
pnpm add @runpod/ai-sdk-provider
|
|
15
|
+
|
|
16
|
+
# yarn
|
|
17
|
+
yarn add @runpod/ai-sdk-provider
|
|
18
|
+
|
|
19
|
+
# bun
|
|
20
|
+
bun add @runpod/ai-sdk-provider
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Provider Instance
|
|
24
|
+
|
|
25
|
+
You can import the default provider instance `runpod` from `@runpod/ai-sdk-provider`:
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
If you need a customized setup, you can import `createRunpod` and create a provider instance with your settings:
|
|
32
|
+
|
|
33
|
+
```ts
|
|
34
|
+
import { createRunpod } from '@runpod/ai-sdk-provider';
|
|
35
|
+
|
|
36
|
+
const runpod = createRunpod({
|
|
37
|
+
apiKey: 'your-api-key', // optional, defaults to RUNPOD_API_KEY environment variable
|
|
38
|
+
baseURL: 'custom-url', // optional, for custom endpoints
|
|
39
|
+
headers: {
|
|
40
|
+
/* custom headers */
|
|
41
|
+
}, // optional
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
You can use the following optional settings to customize the Runpod provider instance:
|
|
46
|
+
|
|
47
|
+
- **baseURL** _string_
|
|
48
|
+
|
|
49
|
+
Use a different URL prefix for API calls, e.g. to use proxy servers or custom endpoints.
|
|
50
|
+
Supports vLLM deployments, SGLang servers, and any OpenAI-compatible API.
|
|
51
|
+
The default prefix is `https://api.runpod.ai/v2`.
|
|
52
|
+
|
|
53
|
+
- **apiKey** _string_
|
|
54
|
+
|
|
55
|
+
API key that is being sent using the `Authorization` header.
|
|
56
|
+
It defaults to the `RUNPOD_API_KEY` environment variable.
|
|
57
|
+
You can obtain your api key from the [Runpod Console](https://console.runpod.io/user/settings) under "API Keys".
|
|
58
|
+
|
|
59
|
+
- **headers** _Record<string,string>_
|
|
60
|
+
|
|
61
|
+
Custom headers to include in the requests.
|
|
62
|
+
|
|
63
|
+
- **fetch** _(input: RequestInfo, init?: RequestInit) => Promise<Response>_
|
|
64
|
+
|
|
65
|
+
Custom [fetch](https://developer.mozilla.org/en-US/docs/Web/API/fetch) implementation.
|
|
66
|
+
You can use it as a middleware to intercept requests,
|
|
67
|
+
or to provide a custom fetch implementation for e.g. testing.
|
|
68
|
+
|
|
69
|
+
## Language Models
|
|
70
|
+
|
|
71
|
+
You can create language models using the provider instance. The first argument is the model ID:
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
75
|
+
import { generateText } from 'ai';
|
|
76
|
+
|
|
77
|
+
const { text } = await generateText({
|
|
78
|
+
model: runpod('qwen/qwen3-32b-awq'),
|
|
79
|
+
prompt: 'What is the capital of Germany?',
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Returns:**
|
|
84
|
+
|
|
85
|
+
- `text` - Generated text string
|
|
86
|
+
- `finishReason` - Why generation stopped ('stop', 'length', etc.)
|
|
87
|
+
- `usage` - Token usage information (prompt, completion, total tokens)
|
|
88
|
+
|
|
89
|
+
### Streaming
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
93
|
+
import { streamText } from 'ai';
|
|
94
|
+
|
|
95
|
+
const { textStream } = await streamText({
|
|
96
|
+
model: runpod('qwen/qwen3-32b-awq'),
|
|
97
|
+
prompt:
|
|
98
|
+
'Write a short poem about artificial intelligence in exactly 4 lines.',
|
|
99
|
+
temperature: 0.7,
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
for await (const delta of textStream) {
|
|
103
|
+
process.stdout.write(delta);
|
|
104
|
+
}
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
### Model Capabilities
|
|
108
|
+
|
|
109
|
+
| Model ID | Description | Streaming | Object Generation | Tool Usage | Reasoning Notes |
|
|
110
|
+
| ------------------------------- | ------------------------------------------------------------------- | --------- | ----------------- | ---------- | ------------------------- |
|
|
111
|
+
| `qwen/qwen3-32b-awq` | 32B parameter multilingual model with strong reasoning capabilities | ✅ | ❌ | ✅ | Standard reasoning events |
|
|
112
|
+
| `openai/gpt-oss-120b` | 120B parameter open-source GPT model | ✅ | ❌ | ✅ | Standard reasoning events |
|
|
113
|
+
| `deepcogito/cogito-671b-v2.1-fp8` | 671B parameter Cogito model with FP8 quantization | ✅ | ❌ | ✅ | Standard reasoning events |
|
|
114
|
+
|
|
115
|
+
**Note:** This list is not complete. For a full list of all available models, see the [Runpod Public Endpoint Reference](https://docs.runpod.io/hub/public-endpoint-reference).
|
|
116
|
+
|
|
117
|
+
### Chat Conversations
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
const { text } = await generateText({
|
|
121
|
+
model: runpod('qwen/qwen3-32b-awq'),
|
|
122
|
+
messages: [
|
|
123
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
124
|
+
{ role: 'user', content: 'What is the capital of France?' },
|
|
125
|
+
],
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
### Tool Calling
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
import { generateText, tool } from 'ai';
|
|
133
|
+
import { z } from 'zod';
|
|
134
|
+
|
|
135
|
+
const { text, toolCalls } = await generateText({
|
|
136
|
+
model: runpod('openai/gpt-oss-120b'),
|
|
137
|
+
prompt: 'What is the weather like in San Francisco?',
|
|
138
|
+
tools: {
|
|
139
|
+
getWeather: tool({
|
|
140
|
+
description: 'Get weather information for a city',
|
|
141
|
+
inputSchema: z.object({
|
|
142
|
+
city: z.string().describe('The city name'),
|
|
143
|
+
}),
|
|
144
|
+
execute: async ({ city }) => {
|
|
145
|
+
return `The weather in ${city} is sunny.`;
|
|
146
|
+
},
|
|
147
|
+
}),
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Additional Returns:**
|
|
153
|
+
|
|
154
|
+
- `toolCalls` - Array of tool calls made by the model
|
|
155
|
+
- `toolResults` - Results from executed tools
|
|
156
|
+
|
|
157
|
+
### Structured output
|
|
158
|
+
|
|
159
|
+
Using `generateObject` to enforce structured ouput is not supported by two models that are part of this provider.
|
|
160
|
+
|
|
161
|
+
You can still return structured data by instructing the model to return JSON and validating it yourself.
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
165
|
+
import { generateText } from 'ai';
|
|
166
|
+
import { z } from 'zod';
|
|
167
|
+
|
|
168
|
+
const RecipeSchema = z.object({
|
|
169
|
+
name: z.string(),
|
|
170
|
+
ingredients: z.array(z.string()),
|
|
171
|
+
steps: z.array(z.string()),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
const { text } = await generateText({
|
|
175
|
+
model: runpod('qwen/qwen3-32b-awq'),
|
|
176
|
+
messages: [
|
|
177
|
+
{
|
|
178
|
+
role: 'system',
|
|
179
|
+
content:
|
|
180
|
+
'return ONLY valid JSON matching { name: string; ingredients: string[]; steps: string[] }',
|
|
181
|
+
},
|
|
182
|
+
{ role: 'user', content: 'generate a lasagna recipe.' },
|
|
183
|
+
],
|
|
184
|
+
temperature: 0,
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
const parsed = JSON.parse(text);
|
|
188
|
+
const result = RecipeSchema.safeParse(parsed);
|
|
189
|
+
|
|
190
|
+
if (!result.success) {
|
|
191
|
+
// handle invalid JSON shape
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
console.log(result.success ? result.data : parsed);
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## Image Models
|
|
198
|
+
|
|
199
|
+
You can create Runpod image models using the `.imageModel()` factory method.
|
|
200
|
+
|
|
201
|
+
### Basic Usage
|
|
202
|
+
|
|
203
|
+
```ts
|
|
204
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
205
|
+
import { experimental_generateImage as generateImage } from 'ai';
|
|
206
|
+
|
|
207
|
+
const { image } = await generateImage({
|
|
208
|
+
model: runpod.imageModel('qwen/qwen-image'),
|
|
209
|
+
prompt: 'A serene mountain landscape at sunset',
|
|
210
|
+
aspectRatio: '4:3',
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
// Save to filesystem
|
|
214
|
+
import { writeFileSync } from 'fs';
|
|
215
|
+
writeFileSync('landscape.jpg', image.uint8Array);
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
**Returns:**
|
|
219
|
+
|
|
220
|
+
- `image.uint8Array` - Binary image data (efficient for processing/saving)
|
|
221
|
+
- `image.base64` - Base64 encoded string (for web display)
|
|
222
|
+
- `image.mediaType` - MIME type ('image/jpeg' or 'image/png')
|
|
223
|
+
- `warnings` - Array of any warnings about unsupported parameters
|
|
224
|
+
|
|
225
|
+
### Model Capabilities
|
|
226
|
+
|
|
227
|
+
| Model ID | Description | Supported Aspect Ratios |
|
|
228
|
+
| -------------------------------------- | ------------------------------- | ------------------------------------- |
|
|
229
|
+
| `bytedance/seedream-3.0` | Advanced text-to-image model | 1:1, 4:3, 3:4 |
|
|
230
|
+
| `bytedance/seedream-4.0` | Text-to-image (v4) | 1:1 (supports 1024, 2048, 4096) |
|
|
231
|
+
| `bytedance/seedream-4.0-edit` | Image editing (v4, multi-image) | 1:1 (supports 1024, 1536, 2048, 4096) |
|
|
232
|
+
| `black-forest-labs/flux-1-schnell` | Fast image generation (4 steps) | 1:1, 4:3, 3:4 |
|
|
233
|
+
| `black-forest-labs/flux-1-dev` | High-quality image generation | 1:1, 4:3, 3:4 |
|
|
234
|
+
| `black-forest-labs/flux-1-kontext-dev` | Context-aware image generation | 1:1, 4:3, 3:4 |
|
|
235
|
+
| `qwen/qwen-image` | Text-to-image generation | 1:1, 4:3, 3:4 |
|
|
236
|
+
| `qwen/qwen-image-edit` | Image editing (prompt-guided) | 1:1, 4:3, 3:4 |
|
|
237
|
+
| `nano-banana-edit` | Image editing (multi-image) | 1:1, 4:3, 3:4 |
|
|
238
|
+
|
|
239
|
+
**Note**: The provider uses strict validation for image parameters. Unsupported aspect ratios (like `16:9`, `9:16`, `3:2`, `2:3`) will throw an `InvalidArgumentError` with a clear message about supported alternatives.
|
|
240
|
+
|
|
241
|
+
**Note:** This list is not complete. For a full list of all available models, see the [Runpod Public Endpoint Reference](https://docs.runpod.io/hub/public-endpoint-reference).
|
|
242
|
+
|
|
243
|
+
### Advanced Parameters
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
const { image } = await generateImage({
|
|
247
|
+
model: runpod.imageModel('bytedance/seedream-3.0'),
|
|
248
|
+
prompt: 'A sunset over mountains',
|
|
249
|
+
size: '1328x1328',
|
|
250
|
+
seed: 42,
|
|
251
|
+
providerOptions: {
|
|
252
|
+
runpod: {
|
|
253
|
+
negative_prompt: 'blurry, low quality',
|
|
254
|
+
enable_safety_checker: true,
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
});
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### Modify Image
|
|
261
|
+
|
|
262
|
+
Transform existing images using text prompts.
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
// Example: Transform existing image
|
|
266
|
+
const { image } = await generateImage({
|
|
267
|
+
model: runpod.imageModel('black-forest-labs/flux-1-kontext-dev'),
|
|
268
|
+
prompt: 'Transform this into a cyberpunk style with neon lights',
|
|
269
|
+
aspectRatio: '1:1',
|
|
270
|
+
providerOptions: {
|
|
271
|
+
runpod: {
|
|
272
|
+
image: 'https://example.com/input-image.jpg',
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
// Example: Using base64 encoded image
|
|
278
|
+
const { image } = await generateImage({
|
|
279
|
+
model: runpod.imageModel('black-forest-labs/flux-1-kontext-dev'),
|
|
280
|
+
prompt: 'Make this image look like a painting',
|
|
281
|
+
providerOptions: {
|
|
282
|
+
runpod: {
|
|
283
|
+
image: 'data:image/png;base64,iVBORw0KGgoAAAANS...',
|
|
284
|
+
},
|
|
285
|
+
},
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
// Example: Combine multiple images using Nano Banana edit
|
|
291
|
+
const { image } = await generateImage({
|
|
292
|
+
model: runpod.imageModel('nano-banana-edit'),
|
|
293
|
+
prompt:
|
|
294
|
+
'Combine these four images into a single realistic 3D character scene.',
|
|
295
|
+
// Defaults to 1:1; you can also set size: '1328x1328' or aspectRatio: '4:3'
|
|
296
|
+
providerOptions: {
|
|
297
|
+
runpod: {
|
|
298
|
+
images: [
|
|
299
|
+
'https://image.runpod.ai/uploads/0bz_xzhuLq/a2166199-5bd5-496b-b9ab-a8bae3f73bdc.jpg',
|
|
300
|
+
'https://image.runpod.ai/uploads/Yw86rhY6xi/2ff8435f-f416-4096-9a4d-2f8c838b2d53.jpg',
|
|
301
|
+
'https://image.runpod.ai/uploads/bpCCX9zLY8/3bc27605-6f9a-40ad-83e9-c29bed45fed9.jpg',
|
|
302
|
+
'https://image.runpod.ai/uploads/LPHEY6pyHp/f950ceb8-fafa-4800-bdf1-fd3fd684d843.jpg',
|
|
303
|
+
],
|
|
304
|
+
enable_safety_checker: true,
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
});
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
### Advanced Configuration
|
|
311
|
+
|
|
312
|
+
```ts
|
|
313
|
+
// Full control over generation parameters
|
|
314
|
+
const { image } = await generateImage({
|
|
315
|
+
model: runpod.imageModel('black-forest-labs/flux-1-dev'),
|
|
316
|
+
prompt: 'A majestic dragon breathing fire in a medieval castle',
|
|
317
|
+
size: '1328x1328',
|
|
318
|
+
seed: 42, // For reproducible results
|
|
319
|
+
providerOptions: {
|
|
320
|
+
runpod: {
|
|
321
|
+
negative_prompt: 'blurry, low quality, distorted, ugly, bad anatomy',
|
|
322
|
+
enable_safety_checker: true,
|
|
323
|
+
num_inference_steps: 50, // Higher quality (default: 28)
|
|
324
|
+
guidance: 3.5, // Stronger prompt adherence (default: 2)
|
|
325
|
+
output_format: 'png', // High quality format
|
|
326
|
+
// Polling settings for long generations
|
|
327
|
+
maxPollAttempts: 30,
|
|
328
|
+
pollIntervalMillis: 4000,
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
// Fast generation with minimal steps
|
|
334
|
+
const { image } = await generateImage({
|
|
335
|
+
model: runpod.imageModel('black-forest-labs/flux-1-schnell'),
|
|
336
|
+
prompt: 'A simple red apple',
|
|
337
|
+
aspectRatio: '1:1',
|
|
338
|
+
providerOptions: {
|
|
339
|
+
runpod: {
|
|
340
|
+
num_inference_steps: 2, // Even faster (default: 4)
|
|
341
|
+
guidance: 10, // Higher guidance for simple prompts
|
|
342
|
+
output_format: 'jpg', // Smaller file size
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
});
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### Provider Options
|
|
349
|
+
|
|
350
|
+
Runpod image models support flexible provider options through the `providerOptions.runpod` object:
|
|
351
|
+
|
|
352
|
+
| Option | Type | Default | Description |
|
|
353
|
+
| ----------------------- | ---------- | ------- | ------------------------------------------------------------------------ |
|
|
354
|
+
| `negative_prompt` | `string` | `""` | Text describing what you don't want in the image |
|
|
355
|
+
| `enable_safety_checker` | `boolean` | `true` | Enable content safety filtering |
|
|
356
|
+
| `image` | `string` | - | Single input image: URL or base64 data URI (Flux Kontext) |
|
|
357
|
+
| `images` | `string[]` | - | Multiple input images (e.g., for `nano-banana-edit` multi-image editing) |
|
|
358
|
+
| `num_inference_steps` | `number` | Auto | Number of denoising steps (Flux: 4 for schnell, 28 for others) |
|
|
359
|
+
| `guidance` | `number` | Auto | Guidance scale for prompt adherence (Flux: 7 for schnell, 2 for others) |
|
|
360
|
+
| `output_format` | `string` | `"png"` | Output image format ("png" or "jpg") |
|
|
361
|
+
| `maxPollAttempts` | `number` | `60` | Maximum polling attempts for async generation |
|
|
362
|
+
| `pollIntervalMillis` | `number` | `5000` | Polling interval in milliseconds (5 seconds) |
|
|
363
|
+
|
|
364
|
+
## About Runpod
|
|
365
|
+
|
|
366
|
+
[Runpod](https://runpod.io) is the foundation for developers to build, deploy, and scale custom AI systems.
|
|
367
|
+
|
|
368
|
+
Beyond some of the public endpoints you've seen above (+ more generative media APIs), Runpod offers private [serverless endpoints](https://docs.runpod.io/serverless/overview) / [pods](https://docs.runpod.io/pods/overview) / [instant clusters](https://docs.runpod.io/instant-clusters), so that you can train, fine-tune or run any open-source or private model on your terms.
|
package/dist/index.js
CHANGED
|
@@ -335,7 +335,11 @@ var runpodImageStatusSchema = import_zod2.z.object({
|
|
|
335
335
|
// src/runpod-provider.ts
|
|
336
336
|
var MODEL_ID_TO_ENDPOINT_URL = {
|
|
337
337
|
"qwen/qwen3-32b-awq": "https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1",
|
|
338
|
-
"ibm-granite/granite-4.0-h-small": "https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1"
|
|
338
|
+
"ibm-granite/granite-4.0-h-small": "https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1",
|
|
339
|
+
"gpt-oss-120b": "https://api.runpod.ai/v2/gpt-oss-120b/openai/v1",
|
|
340
|
+
"openai/gpt-oss-120b": "https://api.runpod.ai/v2/gpt-oss-120b/openai/v1",
|
|
341
|
+
"deepcogito/cogito-671b-v2.1-fp8": "https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1",
|
|
342
|
+
"deepcogito/cogito-671b-v2.1-FP8": "https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1"
|
|
339
343
|
};
|
|
340
344
|
var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
|
|
341
345
|
"qwen/qwen-image": "https://api.runpod.ai/v2/qwen-image-t2i",
|
|
@@ -351,7 +355,9 @@ var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
|
|
|
351
355
|
"nano-banana-edit": "https://api.runpod.ai/v2/nano-banana-edit"
|
|
352
356
|
};
|
|
353
357
|
var MODEL_ID_TO_OPENAI_NAME = {
|
|
354
|
-
"qwen/qwen3-32b-awq": "Qwen/Qwen3-32B-AWQ"
|
|
358
|
+
"qwen/qwen3-32b-awq": "Qwen/Qwen3-32B-AWQ",
|
|
359
|
+
"deepcogito/cogito-671b-v2.1-fp8": "deepcogito/cogito-671b-v2.1-FP8",
|
|
360
|
+
"deepcogito/cogito-671b-v2.1-FP8": "deepcogito/cogito-671b-v2.1-FP8"
|
|
355
361
|
};
|
|
356
362
|
function deriveEndpointURL(modelId) {
|
|
357
363
|
const normalized = modelId.replace(/\//g, "-");
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/runpod-provider.ts","../src/runpod-image-model.ts","../src/runpod-error.ts"],"sourcesContent":["export { createRunpod, runpod } from './runpod-provider';\nexport type { RunpodProvider, RunpodProviderSettings } from './runpod-provider';\nexport type { RunpodChatModelId } from './runpod-chat-options';\nexport type { RunpodCompletionModelId } from './runpod-completion-options';\nexport type { RunpodImageModelId } from './runpod-image-options';\nexport type { OpenAICompatibleErrorData as RunpodErrorData } from '@ai-sdk/openai-compatible';\nexport type { RunpodImageErrorData } from './runpod-error';\n","import { LanguageModelV2, ImageModelV2 } from '@ai-sdk/provider';\nimport {\n OpenAICompatibleChatLanguageModel,\n OpenAICompatibleCompletionLanguageModel,\n} from '@ai-sdk/openai-compatible';\nimport {\n FetchFunction,\n loadApiKey,\n withoutTrailingSlash,\n} from '@ai-sdk/provider-utils';\nimport { RunpodImageModel } from './runpod-image-model';\n\nexport interface RunpodProviderSettings {\n /**\nRunpod API key.\n*/\n apiKey?: string;\n /**\nCustom base URL for Runpod API. Use this to point to custom endpoints or different Runpod deployments.\nExample: 'https://api.runpod.ai/v2/your-endpoint-id/openai/v1'\n*/\n baseURL?: string;\n /**\nCustom headers to include in the requests.\n*/\n headers?: Record<string, string>;\n /**\nCustom fetch implementation. You can use it as a middleware to intercept requests,\nor to provide a custom fetch implementation for e.g. testing.\n*/\n fetch?: FetchFunction;\n}\n\nexport interface RunpodProvider {\n /**\nCreates a model for text generation.\n*/\n (modelId: string): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: string): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: string): LanguageModelV2;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: string): LanguageModelV2;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: string): ImageModelV2;\n}\n\n// Mapping of Runpod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n 'ibm-granite/granite-4.0-h-small': 'https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1',\n};\n\n// Mapping of Runpod image model IDs to their endpoint URLs\nconst IMAGE_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen-image': 'https://api.runpod.ai/v2/qwen-image-t2i',\n 'qwen/qwen-image-edit': 'https://api.runpod.ai/v2/qwen-image-edit',\n 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\n // Seadream v4 (t2i and edit)\n 'bytedance/seedream-4.0': 'https://api.runpod.ai/v2/seedream-v4-t2i',\n 'bytedance/seedream-4.0-edit': 'https://api.runpod.ai/v2/seedream-v4-edit',\n 'black-forest-labs/flux-1-kontext-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev',\n 'black-forest-labs/flux-1-schnell':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell',\n 'black-forest-labs/flux-1-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-dev',\n // Nano Banana (edit only)\n 'nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit',\n};\n\n// Mapping of Runpod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\n};\n\n/**\n * Derives the endpoint URL for a model by replacing slashes with hyphens.\n * Example: 'ibm-granite/granite-4.0-h-small' -> 'https://api.runpod.ai/v2/ibm-granite-granite-4.0-h-small/openai/v1'\n */\nfunction deriveEndpointURL(modelId: string): string {\n const normalized = modelId.replace(/\\//g, '-');\n return `https://api.runpod.ai/v2/${normalized}/openai/v1`;\n}\n\nexport function createRunpod(\n options: RunpodProviderSettings = {}\n): RunpodProvider {\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'RUNPOD_API_KEY',\n description: 'Runpod',\n })}`,\n ...options.headers,\n });\n\n const runpodFetch: FetchFunction = async (url, requestInit) => {\n if (requestInit?.body) {\n try {\n const body = JSON.parse(requestInit.body as string);\n if (body.stream === true && !body.stream_options) {\n body.stream_options = { include_usage: true };\n requestInit.body = JSON.stringify(body);\n }\n } catch {}\n }\n const fetchFn = options.fetch || fetch;\n return fetchFn(url, requestInit);\n };\n\n interface CommonModelConfig {\n provider: string;\n url: ({ path }: { path: string }) => string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n }\n\n const getModelConfig = (\n modelId: string,\n modelType: string\n ): CommonModelConfig => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: runpodFetch,\n };\n };\n\n const createChatModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'chat'),\n includeUsage: true,\n });\n };\n\n const createCompletionModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'completion'),\n includeUsage: true,\n });\n };\n\n const createImageModel = (modelId: string) => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL =\n IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return new RunpodImageModel(modelId, {\n provider: 'runpod.image',\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n });\n };\n\n const provider = (modelId: string) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n provider.imageModel = createImageModel;\n\n return provider;\n}\n\nexport const runpod = createRunpod();\n","import { ImageModelV2, ImageModelV2CallWarning } from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createJsonResponseHandler,\n createBinaryResponseHandler,\n FetchFunction,\n postJsonToApi,\n getFromApi,\n} from '@ai-sdk/provider-utils';\nimport { InvalidArgumentError } from '@ai-sdk/provider';\nimport { z } from 'zod';\nimport { runpodImageFailedResponseHandler } from './runpod-error';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ninterface RunpodImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\n// Runpod supported aspect ratios (only validated working sizes)\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\n '1:1': '1328*1328', // ✅ Native support\n '4:3': '1472*1140', // ✅ Native support\n '3:4': '1140*1472', // ✅ Native support\n};\n\n// Runpod supported sizes (validated working sizes)\nconst SUPPORTED_SIZES = new Set([\n // Native aspect ratio sizes\n '1328*1328', // 1:1\n '1472*1140', // 4:3\n '1140*1472', // 3:4\n // Additional validated sizes\n '512*512',\n '768*768',\n '1024*1024',\n '1536*1536',\n '2048*2048',\n '4096*4096',\n '512*768',\n '768*512',\n '1024*768',\n '768*1024',\n]);\n\nexport class RunpodImageModel implements ImageModelV2 {\n readonly specificationVersion = 'v2';\n readonly maxImagesPerCall = 1;\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private config: RunpodImageModelConfig\n ) {}\n\n async doGenerate({\n prompt,\n n = 1,\n size,\n aspectRatio,\n seed,\n providerOptions,\n headers,\n abortSignal,\n }: Parameters<ImageModelV2['doGenerate']>[0]): Promise<\n Awaited<ReturnType<ImageModelV2['doGenerate']>>\n > {\n const warnings: Array<ImageModelV2CallWarning> = [];\n\n // Determine the size to use\n let runpodSize: string;\n\n if (size) {\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\n const runpodSizeCandidate = size.replace('x', '*');\n\n // Validate size is supported\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\n throw new InvalidArgumentError({\n argument: 'size',\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\n SUPPORTED_SIZES\n )\n .map((s) => s.replace('*', 'x'))\n .join(', ')}`,\n });\n }\n\n runpodSize = runpodSizeCandidate;\n } else if (aspectRatio) {\n // Validate aspect ratio is supported\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\n throw new InvalidArgumentError({\n argument: 'aspectRatio',\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\n });\n }\n\n // Use supported aspect ratio mapping\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\n } else {\n // Default to square format\n runpodSize = '1328*1328';\n }\n\n // Handle multiple images warning\n if (n > 1) {\n warnings.push({\n type: 'unsupported-setting',\n setting: 'n',\n details:\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\n });\n }\n\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n // Runpod uses a different request format - /runsync endpoint with input wrapper\n const inputPayload = this.buildInputPayload(\n prompt,\n runpodSize,\n seed,\n providerOptions.runpod\n );\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url: `${this.config.baseURL}/runsync`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n input: inputPayload,\n },\n failedResponseHandler: runpodImageFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageResponseSchema as any\n ),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n // Handle both sync and async responses from Runpod\n const typedResponse = response as any;\n if (\n typedResponse.status === 'COMPLETED' &&\n (typedResponse.output?.result || typedResponse.output?.image_url)\n ) {\n // Sync response - image is ready\n // Different models use different response formats: result vs image_url\n const imageUrl =\n typedResponse.output.result || typedResponse.output.image_url;\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n cost: typedResponse.output?.cost,\n },\n ],\n },\n },\n };\n } else if (\n typedResponse.status === 'IN_QUEUE' ||\n typedResponse.status === 'IN_PROGRESS'\n ) {\n // Async response - need to poll for completion\n const pollOptions = {\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\n pollIntervalMillis: providerOptions.runpod\n ?.pollIntervalMillis as number,\n };\n const imageUrl = await this.pollForCompletion(\n typedResponse.id,\n abortSignal,\n pollOptions\n );\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n jobId: typedResponse.id,\n },\n ],\n },\n },\n };\n } else {\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\n }\n }\n\n private async downloadImage(\n imageUrl: string,\n abortSignal?: AbortSignal\n ): Promise<Uint8Array> {\n const { value: imageData } = await getFromApi({\n url: imageUrl,\n successfulResponseHandler: createBinaryResponseHandler(),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n return imageData;\n }\n\n private async pollForCompletion(\n jobId: string,\n abortSignal?: AbortSignal,\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number }\n ): Promise<string> {\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (abortSignal?.aborted) {\n throw new Error('Image generation was aborted');\n }\n\n const { value: statusResponse } = await getFromApi({\n url: `${this.config.baseURL}/status/${jobId}`,\n headers: this.config.headers(),\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageStatusSchema as any\n ),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n\n const typedStatusResponse = statusResponse as any;\n if (\n typedStatusResponse.status === 'COMPLETED' &&\n (typedStatusResponse.output?.result ||\n typedStatusResponse.output?.image_url)\n ) {\n return (\n typedStatusResponse.output.result ||\n typedStatusResponse.output.image_url\n );\n }\n\n if (typedStatusResponse.status === 'FAILED') {\n throw new Error(\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\n );\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new Error(\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\n );\n }\n\n private buildInputPayload(\n prompt: string,\n runpodSize: string,\n seed?: number,\n runpodOptions?: Record<string, unknown>\n ): Record<string, unknown> {\n // Check if this is a Flux model that uses different parameters\n const isFluxModel =\n this.modelId.includes('flux') ||\n this.modelId.includes('black-forest-labs');\n\n if (isFluxModel) {\n // Check if this is Flux Kontext (uses different parameters)\n const isKontext = this.modelId.includes('kontext');\n\n if (isKontext) {\n // Flux Kontext uses size format and has image input\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: 28,\n guidance: 2,\n size: runpodSize,\n output_format: 'png',\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions, // This will include the 'image' parameter if provided\n };\n } else {\n // Regular Flux models use width/height\n const [width, height] = runpodSize.split('*').map(Number);\n\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\n guidance: this.modelId.includes('schnell') ? 7 : 2,\n width,\n height,\n image_format: 'png',\n ...runpodOptions,\n };\n }\n }\n\n // Default format for Qwen and other models\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n }\n}\n\n// Runpod image API response schema (handles both sync and async responses)\nconst runpodImageResponseSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n delayTime: z.number().optional(),\n executionTime: z.number().optional(),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(), // URL to the generated image (Qwen format)\n image_url: z.string().optional(), // URL to the generated image (Flux format)\n })\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\n});\n\n// Schema for polling status endpoint\nconst runpodImageStatusSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(),\n image_url: z.string().optional(),\n })\n .optional(),\n error: z.string().optional(), // Error message if FAILED\n});\n","import { z } from 'zod';\r\nimport { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';\r\n\r\n// Runpod image API error schema (supports both error formats)\r\nexport const runpodImageErrorSchema = z.object({\r\n error: z.string().optional(),\r\n message: z.string().optional(),\r\n});\r\n\r\nexport type RunpodImageErrorData = z.infer<typeof runpodImageErrorSchema>;\r\n\r\nexport const runpodImageFailedResponseHandler = createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: RunpodImageErrorData) => {\r\n // Prefer message if available (more descriptive)\r\n if (data.message) {\r\n return data.message;\r\n }\r\n \r\n // If error field exists, try to extract nested JSON message\r\n if (data.error) {\r\n // Runpod sometimes returns nested JSON in the error field like:\r\n // \"Error submitting task: 400, {\\\"code\\\":400,\\\"message\\\":\\\"...\\\"}\"\r\n // Try to extract the inner message for cleaner error messages\r\n // Find the last occurrence of { which likely starts the JSON object\r\n const lastBraceIndex = data.error.lastIndexOf('{');\r\n if (lastBraceIndex !== -1) {\r\n try {\r\n const jsonStr = data.error.substring(lastBraceIndex);\r\n const nestedError = JSON.parse(jsonStr);\r\n if (nestedError.message && typeof nestedError.message === 'string') {\r\n return nestedError.message;\r\n }\r\n } catch {\r\n // If parsing fails, fall back to the original error string\r\n }\r\n }\r\n return data.error;\r\n }\r\n \r\n return 'Unknown Runpod error';\r\n },\r\n});\r\n\r\n"],"mappings":"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","names":["import_provider_utils","import_provider_utils","import_zod"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/runpod-provider.ts","../src/runpod-image-model.ts","../src/runpod-error.ts"],"sourcesContent":["export { createRunpod, runpod } from './runpod-provider';\nexport type { RunpodProvider, RunpodProviderSettings } from './runpod-provider';\nexport type { RunpodChatModelId } from './runpod-chat-options';\nexport type { RunpodCompletionModelId } from './runpod-completion-options';\nexport type { RunpodImageModelId } from './runpod-image-options';\nexport type { OpenAICompatibleErrorData as RunpodErrorData } from '@ai-sdk/openai-compatible';\nexport type { RunpodImageErrorData } from './runpod-error';\n","import { LanguageModelV2, ImageModelV2 } from '@ai-sdk/provider';\nimport {\n OpenAICompatibleChatLanguageModel,\n OpenAICompatibleCompletionLanguageModel,\n} from '@ai-sdk/openai-compatible';\nimport {\n FetchFunction,\n loadApiKey,\n withoutTrailingSlash,\n} from '@ai-sdk/provider-utils';\nimport { RunpodImageModel } from './runpod-image-model';\n\nexport interface RunpodProviderSettings {\n /**\nRunpod API key.\n*/\n apiKey?: string;\n /**\nCustom base URL for Runpod API. Use this to point to custom endpoints or different Runpod deployments.\nExample: 'https://api.runpod.ai/v2/your-endpoint-id/openai/v1'\n*/\n baseURL?: string;\n /**\nCustom headers to include in the requests.\n*/\n headers?: Record<string, string>;\n /**\nCustom fetch implementation. You can use it as a middleware to intercept requests,\nor to provide a custom fetch implementation for e.g. testing.\n*/\n fetch?: FetchFunction;\n}\n\nexport interface RunpodProvider {\n /**\nCreates a model for text generation.\n*/\n (modelId: string): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: string): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: string): LanguageModelV2;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: string): LanguageModelV2;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: string): ImageModelV2;\n}\n\n// Mapping of Runpod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n 'ibm-granite/granite-4.0-h-small': 'https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1',\n 'gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'openai/gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'deepcogito/cogito-671b-v2.1-fp8': 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n 'deepcogito/cogito-671b-v2.1-FP8': 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n};\n\n// Mapping of Runpod image model IDs to their endpoint URLs\nconst IMAGE_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen-image': 'https://api.runpod.ai/v2/qwen-image-t2i',\n 'qwen/qwen-image-edit': 'https://api.runpod.ai/v2/qwen-image-edit',\n 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\n // Seadream v4 (t2i and edit)\n 'bytedance/seedream-4.0': 'https://api.runpod.ai/v2/seedream-v4-t2i',\n 'bytedance/seedream-4.0-edit': 'https://api.runpod.ai/v2/seedream-v4-edit',\n 'black-forest-labs/flux-1-kontext-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev',\n 'black-forest-labs/flux-1-schnell':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell',\n 'black-forest-labs/flux-1-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-dev',\n // Nano Banana (edit only)\n 'nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit',\n};\n\n// Mapping of Runpod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\n 'deepcogito/cogito-671b-v2.1-fp8': 'deepcogito/cogito-671b-v2.1-FP8',\n 'deepcogito/cogito-671b-v2.1-FP8': 'deepcogito/cogito-671b-v2.1-FP8',\n};\n\n/**\n * Derives the endpoint URL for a model by replacing slashes with hyphens.\n * Example: 'ibm-granite/granite-4.0-h-small' -> 'https://api.runpod.ai/v2/ibm-granite-granite-4.0-h-small/openai/v1'\n */\nfunction deriveEndpointURL(modelId: string): string {\n const normalized = modelId.replace(/\\//g, '-');\n return `https://api.runpod.ai/v2/${normalized}/openai/v1`;\n}\n\nexport function createRunpod(\n options: RunpodProviderSettings = {}\n): RunpodProvider {\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'RUNPOD_API_KEY',\n description: 'Runpod',\n })}`,\n ...options.headers,\n });\n\n const runpodFetch: FetchFunction = async (url, requestInit) => {\n if (requestInit?.body) {\n try {\n const body = JSON.parse(requestInit.body as string);\n if (body.stream === true && !body.stream_options) {\n body.stream_options = { include_usage: true };\n requestInit.body = JSON.stringify(body);\n }\n } catch {}\n }\n const fetchFn = options.fetch || fetch;\n return fetchFn(url, requestInit);\n };\n\n interface CommonModelConfig {\n provider: string;\n url: ({ path }: { path: string }) => string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n }\n\n const getModelConfig = (\n modelId: string,\n modelType: string\n ): CommonModelConfig => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: runpodFetch,\n };\n };\n\n const createChatModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'chat'),\n includeUsage: true,\n });\n };\n\n const createCompletionModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'completion'),\n includeUsage: true,\n });\n };\n\n const createImageModel = (modelId: string) => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL =\n IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return new RunpodImageModel(modelId, {\n provider: 'runpod.image',\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n });\n };\n\n const provider = (modelId: string) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n provider.imageModel = createImageModel;\n\n return provider;\n}\n\nexport const runpod = createRunpod();\n","import { ImageModelV2, ImageModelV2CallWarning } from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createJsonResponseHandler,\n createBinaryResponseHandler,\n FetchFunction,\n postJsonToApi,\n getFromApi,\n} from '@ai-sdk/provider-utils';\nimport { InvalidArgumentError } from '@ai-sdk/provider';\nimport { z } from 'zod';\nimport { runpodImageFailedResponseHandler } from './runpod-error';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ninterface RunpodImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\n// Runpod supported aspect ratios (only validated working sizes)\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\n '1:1': '1328*1328', // ✅ Native support\n '4:3': '1472*1140', // ✅ Native support\n '3:4': '1140*1472', // ✅ Native support\n};\n\n// Runpod supported sizes (validated working sizes)\nconst SUPPORTED_SIZES = new Set([\n // Native aspect ratio sizes\n '1328*1328', // 1:1\n '1472*1140', // 4:3\n '1140*1472', // 3:4\n // Additional validated sizes\n '512*512',\n '768*768',\n '1024*1024',\n '1536*1536',\n '2048*2048',\n '4096*4096',\n '512*768',\n '768*512',\n '1024*768',\n '768*1024',\n]);\n\nexport class RunpodImageModel implements ImageModelV2 {\n readonly specificationVersion = 'v2';\n readonly maxImagesPerCall = 1;\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private config: RunpodImageModelConfig\n ) {}\n\n async doGenerate({\n prompt,\n n = 1,\n size,\n aspectRatio,\n seed,\n providerOptions,\n headers,\n abortSignal,\n }: Parameters<ImageModelV2['doGenerate']>[0]): Promise<\n Awaited<ReturnType<ImageModelV2['doGenerate']>>\n > {\n const warnings: Array<ImageModelV2CallWarning> = [];\n\n // Determine the size to use\n let runpodSize: string;\n\n if (size) {\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\n const runpodSizeCandidate = size.replace('x', '*');\n\n // Validate size is supported\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\n throw new InvalidArgumentError({\n argument: 'size',\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\n SUPPORTED_SIZES\n )\n .map((s) => s.replace('*', 'x'))\n .join(', ')}`,\n });\n }\n\n runpodSize = runpodSizeCandidate;\n } else if (aspectRatio) {\n // Validate aspect ratio is supported\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\n throw new InvalidArgumentError({\n argument: 'aspectRatio',\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\n });\n }\n\n // Use supported aspect ratio mapping\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\n } else {\n // Default to square format\n runpodSize = '1328*1328';\n }\n\n // Handle multiple images warning\n if (n > 1) {\n warnings.push({\n type: 'unsupported-setting',\n setting: 'n',\n details:\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\n });\n }\n\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n // Runpod uses a different request format - /runsync endpoint with input wrapper\n const inputPayload = this.buildInputPayload(\n prompt,\n runpodSize,\n seed,\n providerOptions.runpod\n );\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url: `${this.config.baseURL}/runsync`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n input: inputPayload,\n },\n failedResponseHandler: runpodImageFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageResponseSchema as any\n ),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n // Handle both sync and async responses from Runpod\n const typedResponse = response as any;\n if (\n typedResponse.status === 'COMPLETED' &&\n (typedResponse.output?.result || typedResponse.output?.image_url)\n ) {\n // Sync response - image is ready\n // Different models use different response formats: result vs image_url\n const imageUrl =\n typedResponse.output.result || typedResponse.output.image_url;\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n cost: typedResponse.output?.cost,\n },\n ],\n },\n },\n };\n } else if (\n typedResponse.status === 'IN_QUEUE' ||\n typedResponse.status === 'IN_PROGRESS'\n ) {\n // Async response - need to poll for completion\n const pollOptions = {\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\n pollIntervalMillis: providerOptions.runpod\n ?.pollIntervalMillis as number,\n };\n const imageUrl = await this.pollForCompletion(\n typedResponse.id,\n abortSignal,\n pollOptions\n );\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n jobId: typedResponse.id,\n },\n ],\n },\n },\n };\n } else {\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\n }\n }\n\n private async downloadImage(\n imageUrl: string,\n abortSignal?: AbortSignal\n ): Promise<Uint8Array> {\n const { value: imageData } = await getFromApi({\n url: imageUrl,\n successfulResponseHandler: createBinaryResponseHandler(),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n return imageData;\n }\n\n private async pollForCompletion(\n jobId: string,\n abortSignal?: AbortSignal,\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number }\n ): Promise<string> {\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (abortSignal?.aborted) {\n throw new Error('Image generation was aborted');\n }\n\n const { value: statusResponse } = await getFromApi({\n url: `${this.config.baseURL}/status/${jobId}`,\n headers: this.config.headers(),\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageStatusSchema as any\n ),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n\n const typedStatusResponse = statusResponse as any;\n if (\n typedStatusResponse.status === 'COMPLETED' &&\n (typedStatusResponse.output?.result ||\n typedStatusResponse.output?.image_url)\n ) {\n return (\n typedStatusResponse.output.result ||\n typedStatusResponse.output.image_url\n );\n }\n\n if (typedStatusResponse.status === 'FAILED') {\n throw new Error(\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\n );\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new Error(\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\n );\n }\n\n private buildInputPayload(\n prompt: string,\n runpodSize: string,\n seed?: number,\n runpodOptions?: Record<string, unknown>\n ): Record<string, unknown> {\n // Check if this is a Flux model that uses different parameters\n const isFluxModel =\n this.modelId.includes('flux') ||\n this.modelId.includes('black-forest-labs');\n\n if (isFluxModel) {\n // Check if this is Flux Kontext (uses different parameters)\n const isKontext = this.modelId.includes('kontext');\n\n if (isKontext) {\n // Flux Kontext uses size format and has image input\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: 28,\n guidance: 2,\n size: runpodSize,\n output_format: 'png',\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions, // This will include the 'image' parameter if provided\n };\n } else {\n // Regular Flux models use width/height\n const [width, height] = runpodSize.split('*').map(Number);\n\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\n guidance: this.modelId.includes('schnell') ? 7 : 2,\n width,\n height,\n image_format: 'png',\n ...runpodOptions,\n };\n }\n }\n\n // Default format for Qwen and other models\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n }\n}\n\n// Runpod image API response schema (handles both sync and async responses)\nconst runpodImageResponseSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n delayTime: z.number().optional(),\n executionTime: z.number().optional(),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(), // URL to the generated image (Qwen format)\n image_url: z.string().optional(), // URL to the generated image (Flux format)\n })\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\n});\n\n// Schema for polling status endpoint\nconst runpodImageStatusSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(),\n image_url: z.string().optional(),\n })\n .optional(),\n error: z.string().optional(), // Error message if FAILED\n});\n","import { z } from 'zod';\r\nimport { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';\r\n\r\n// Runpod image API error schema (supports both error formats)\r\nexport const runpodImageErrorSchema = z.object({\r\n error: z.string().optional(),\r\n message: z.string().optional(),\r\n});\r\n\r\nexport type RunpodImageErrorData = z.infer<typeof runpodImageErrorSchema>;\r\n\r\nexport const runpodImageFailedResponseHandler = createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: RunpodImageErrorData) => {\r\n // Prefer message if available (more descriptive)\r\n if (data.message) {\r\n return data.message;\r\n }\r\n \r\n // If error field exists, try to extract nested JSON message\r\n if (data.error) {\r\n // Runpod sometimes returns nested JSON in the error field like:\r\n // \"Error submitting task: 400, {\\\"code\\\":400,\\\"message\\\":\\\"...\\\"}\"\r\n // Try to extract the inner message for cleaner error messages\r\n // Find the last occurrence of { which likely starts the JSON object\r\n const lastBraceIndex = data.error.lastIndexOf('{');\r\n if (lastBraceIndex !== -1) {\r\n try {\r\n const jsonStr = data.error.substring(lastBraceIndex);\r\n const nestedError = JSON.parse(jsonStr);\r\n if (nestedError.message && typeof nestedError.message === 'string') {\r\n return nestedError.message;\r\n }\r\n } catch {\r\n // If parsing fails, fall back to the original error string\r\n }\r\n }\r\n return data.error;\r\n }\r\n \r\n return 'Unknown Runpod error';\r\n },\r\n});\r\n\r\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,+BAGO;AACP,IAAAA,yBAIO;;;ACRP,IAAAC,yBAOO;AACP,sBAAqC;AACrC,IAAAC,cAAkB;;;ACVlB,iBAAkB;AAClB,4BAA+C;AAGxC,IAAM,yBAAyB,aAAE,OAAO;AAAA,EAC7C,OAAO,aAAE,OAAO,EAAE,SAAS;AAAA,EAC3B,SAAS,aAAE,OAAO,EAAE,SAAS;AAC/B,CAAC;AAIM,IAAM,uCAAmC,sDAA+B;AAAA,EAC7E,aAAa;AAAA,EACb,gBAAgB,CAAC,SAA+B;AAE9C,QAAI,KAAK,SAAS;AAChB,aAAO,KAAK;AAAA,IACd;AAGA,QAAI,KAAK,OAAO;AAKd,YAAM,iBAAiB,KAAK,MAAM,YAAY,GAAG;AACjD,UAAI,mBAAmB,IAAI;AACzB,YAAI;AACF,gBAAM,UAAU,KAAK,MAAM,UAAU,cAAc;AACnD,gBAAM,cAAc,KAAK,MAAM,OAAO;AACtC,cAAI,YAAY,WAAW,OAAO,YAAY,YAAY,UAAU;AAClE,mBAAO,YAAY;AAAA,UACrB;AAAA,QACF,QAAQ;AAAA,QAER;AAAA,MACF;AACA,aAAO,KAAK;AAAA,IACd;AAEA,WAAO;AAAA,EACT;AACF,CAAC;;;ADjBD,IAAM,0BAAkD;AAAA,EACtD,OAAO;AAAA;AAAA,EACP,OAAO;AAAA;AAAA,EACP,OAAO;AAAA;AACT;AAGA,IAAM,kBAAkB,oBAAI,IAAI;AAAA;AAAA,EAE9B;AAAA;AAAA,EACA;AAAA;AAAA,EACA;AAAA;AAAA;AAAA,EAEA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAEM,IAAM,mBAAN,MAA+C;AAAA,EAQpD,YACW,SACD,QACR;AAFS;AACD;AATV,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAAA,EASzB;AAAA,EAPH,IAAI,WAAmB;AACrB,WAAO,KAAK,OAAO;AAAA,EACrB;AAAA,EAOA,MAAM,WAAW;AAAA,IACf;AAAA,IACA,IAAI;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,GAEE;AACA,UAAM,WAA2C,CAAC;AAGlD,QAAI;AAEJ,QAAI,MAAM;AAER,YAAM,sBAAsB,KAAK,QAAQ,KAAK,GAAG;AAGjD,UAAI,CAAC,gBAAgB,IAAI,mBAAmB,GAAG;AAC7C,cAAM,IAAI,qCAAqB;AAAA,UAC7B,UAAU;AAAA,UACV,SAAS,QAAQ,IAAI,iDAAiD,MAAM;AAAA,YAC1E;AAAA,UACF,EACG,IAAI,CAAC,MAAM,EAAE,QAAQ,KAAK,GAAG,CAAC,EAC9B,KAAK,IAAI,CAAC;AAAA,QACf,CAAC;AAAA,MACH;AAEA,mBAAa;AAAA,IACf,WAAW,aAAa;AAEtB,UAAI,CAAC,wBAAwB,WAAW,GAAG;AACzC,cAAM,IAAI,qCAAqB;AAAA,UAC7B,UAAU;AAAA,UACV,SAAS,gBAAgB,WAAW,yDAAyD,OAAO,KAAK,uBAAuB,EAAE,KAAK,IAAI,CAAC;AAAA,QAC9I,CAAC;AAAA,MACH;AAGA,mBAAa,wBAAwB,WAAW;AAAA,IAClD,OAAO;AAEL,mBAAa;AAAA,IACf;AAGA,QAAI,IAAI,GAAG;AACT,eAAS,KAAK;AAAA,QACZ,MAAM;AAAA,QACN,SAAS;AAAA,QACT,SACE;AAAA,MACJ,CAAC;AAAA,IACH;AAEA,UAAM,cAAc,KAAK,OAAO,WAAW,cAAc,KAAK,oBAAI,KAAK;AAGvE,UAAM,eAAe,KAAK;AAAA,MACxB;AAAA,MACA;AAAA,MACA;AAAA,MACA,gBAAgB;AAAA,IAClB;AAEA,UAAM,EAAE,OAAO,UAAU,gBAAgB,IAAI,UAAM,sCAAc;AAAA,MAC/D,KAAK,GAAG,KAAK,OAAO,OAAO;AAAA,MAC3B,aAAS,uCAAe,KAAK,OAAO,QAAQ,GAAG,OAAO;AAAA,MACtD,MAAM;AAAA,QACJ,OAAO;AAAA,MACT;AAAA,MACA,uBAAuB;AAAA,MACvB,+BAA2B;AAAA,QACzB;AAAA,MACF;AAAA,MACA;AAAA,MACA,OAAO,KAAK,OAAO;AAAA,IACrB,CAAC;AAGD,UAAM,gBAAgB;AACtB,QACE,cAAc,WAAW,gBACxB,cAAc,QAAQ,UAAU,cAAc,QAAQ,YACvD;AAGA,YAAM,WACJ,cAAc,OAAO,UAAU,cAAc,OAAO;AACtD,YAAM,YAAY,MAAM,KAAK,cAAc,UAAU,WAAW;AAEhE,aAAO;AAAA,QACL,QAAQ,CAAC,SAAS;AAAA,QAClB;AAAA,QACA,UAAU;AAAA,UACR,WAAW;AAAA,UACX,SAAS,KAAK;AAAA,UACd,SAAS;AAAA,QACX;AAAA,QACA,kBAAkB;AAAA,UAChB,QAAQ;AAAA,YACN,QAAQ;AAAA,cACN;AAAA,gBACE,KAAK;AAAA,gBACL,MAAM,cAAc,QAAQ;AAAA,cAC9B;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,WACE,cAAc,WAAW,cACzB,cAAc,WAAW,eACzB;AAEA,YAAM,cAAc;AAAA,QAClB,aAAa,gBAAgB,QAAQ;AAAA,QACrC,oBAAoB,gBAAgB,QAChC;AAAA,MACN;AACA,YAAM,WAAW,MAAM,KAAK;AAAA,QAC1B,cAAc;AAAA,QACd;AAAA,QACA;AAAA,MACF;AACA,YAAM,YAAY,MAAM,KAAK,cAAc,UAAU,WAAW;AAEhE,aAAO;AAAA,QACL,QAAQ,CAAC,SAAS;AAAA,QAClB;AAAA,QACA,UAAU;AAAA,UACR,WAAW;AAAA,UACX,SAAS,KAAK;AAAA,UACd,SAAS;AAAA,QACX;AAAA,QACA,kBAAkB;AAAA,UAChB,QAAQ;AAAA,YACN,QAAQ;AAAA,cACN;AAAA,gBACE,KAAK;AAAA,gBACL,OAAO,cAAc;AAAA,cACvB;AAAA,YACF;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,OAAO;AACL,YAAM,IAAI,MAAM,+BAA+B,cAAc,MAAM,EAAE;AAAA,IACvE;AAAA,EACF;AAAA,EAEA,MAAc,cACZ,UACA,aACqB;AACrB,UAAM,EAAE,OAAO,UAAU,IAAI,UAAM,mCAAW;AAAA,MAC5C,KAAK;AAAA,MACL,+BAA2B,oDAA4B;AAAA,MACvD,uBAAuB;AAAA,MACvB;AAAA,MACA,OAAO,KAAK,OAAO;AAAA,IACrB,CAAC;AACD,WAAO;AAAA,EACT;AAAA,EAEA,MAAc,kBACZ,OACA,aACA,aACiB;AACjB,UAAM,cAAc,aAAa,eAAe;AAChD,UAAM,eAAe,aAAa,sBAAsB;AAExD,aAAS,UAAU,GAAG,UAAU,aAAa,WAAW;AACtD,UAAI,aAAa,SAAS;AACxB,cAAM,IAAI,MAAM,8BAA8B;AAAA,MAChD;AAEA,YAAM,EAAE,OAAO,eAAe,IAAI,UAAM,mCAAW;AAAA,QACjD,KAAK,GAAG,KAAK,OAAO,OAAO,WAAW,KAAK;AAAA,QAC3C,SAAS,KAAK,OAAO,QAAQ;AAAA,QAC7B,+BAA2B;AAAA,UACzB;AAAA,QACF;AAAA,QACA,uBAAuB;AAAA,QACvB;AAAA,QACA,OAAO,KAAK,OAAO;AAAA,MACrB,CAAC;AAED,YAAM,sBAAsB;AAC5B,UACE,oBAAoB,WAAW,gBAC9B,oBAAoB,QAAQ,UAC3B,oBAAoB,QAAQ,YAC9B;AACA,eACE,oBAAoB,OAAO,UAC3B,oBAAoB,OAAO;AAAA,MAE/B;AAEA,UAAI,oBAAoB,WAAW,UAAU;AAC3C,cAAM,IAAI;AAAA,UACR,4BAA4B,oBAAoB,SAAS,eAAe;AAAA,QAC1E;AAAA,MACF;AAGA,YAAM,IAAI,QAAQ,CAAC,YAAY,WAAW,SAAS,YAAY,CAAC;AAAA,IAClE;AAEA,UAAM,IAAI;AAAA,MACR,oCAAoC,WAAW,cAAe,cAAc,eAAgB,GAAI;AAAA,IAClG;AAAA,EACF;AAAA,EAEQ,kBACN,QACA,YACA,MACA,eACyB;AAEzB,UAAM,cACJ,KAAK,QAAQ,SAAS,MAAM,KAC5B,KAAK,QAAQ,SAAS,mBAAmB;AAE3C,QAAI,aAAa;AAEf,YAAM,YAAY,KAAK,QAAQ,SAAS,SAAS;AAEjD,UAAI,WAAW;AAEb,eAAO;AAAA,UACL;AAAA,UACA,iBAAiB,eAAe,mBAAmB;AAAA,UACnD,MAAM,QAAQ;AAAA,UACd,qBAAqB;AAAA,UACrB,UAAU;AAAA,UACV,MAAM;AAAA,UACN,eAAe;AAAA,UACf,uBAAuB,eAAe,yBAAyB;AAAA,UAC/D,GAAG;AAAA;AAAA,QACL;AAAA,MACF,OAAO;AAEL,cAAM,CAAC,OAAO,MAAM,IAAI,WAAW,MAAM,GAAG,EAAE,IAAI,MAAM;AAExD,eAAO;AAAA,UACL;AAAA,UACA,iBAAiB,eAAe,mBAAmB;AAAA,UACnD,MAAM,QAAQ;AAAA,UACd,qBAAqB,KAAK,QAAQ,SAAS,SAAS,IAAI,IAAI;AAAA,UAC5D,UAAU,KAAK,QAAQ,SAAS,SAAS,IAAI,IAAI;AAAA,UACjD;AAAA,UACA;AAAA,UACA,cAAc;AAAA,UACd,GAAG;AAAA,QACL;AAAA,MACF;AAAA,IACF;AAGA,WAAO;AAAA,MACL;AAAA,MACA,iBAAiB,eAAe,mBAAmB;AAAA,MACnD,MAAM;AAAA,MACN,MAAM,QAAQ;AAAA,MACd,uBAAuB,eAAe,yBAAyB;AAAA,MAC/D,GAAG;AAAA,IACL;AAAA,EACF;AACF;AAGA,IAAM,4BAA4B,cAAE,OAAO;AAAA,EACzC,IAAI,cAAE,OAAO;AAAA,EACb,QAAQ,cAAE,KAAK,CAAC,aAAa,YAAY,eAAe,QAAQ,CAAC;AAAA,EACjE,WAAW,cAAE,OAAO,EAAE,SAAS;AAAA,EAC/B,eAAe,cAAE,OAAO,EAAE,SAAS;AAAA,EACnC,QAAQ,cACL,OAAO;AAAA,IACN,MAAM,cAAE,OAAO,EAAE,SAAS;AAAA,IAC1B,QAAQ,cAAE,OAAO,EAAE,SAAS;AAAA;AAAA,IAC5B,WAAW,cAAE,OAAO,EAAE,SAAS;AAAA;AAAA,EACjC,CAAC,EACA,SAAS;AAAA;AACd,CAAC;AAGD,IAAM,0BAA0B,cAAE,OAAO;AAAA,EACvC,IAAI,cAAE,OAAO;AAAA,EACb,QAAQ,cAAE,KAAK,CAAC,aAAa,YAAY,eAAe,QAAQ,CAAC;AAAA,EACjE,QAAQ,cACL,OAAO;AAAA,IACN,MAAM,cAAE,OAAO,EAAE,SAAS;AAAA,IAC1B,QAAQ,cAAE,OAAO,EAAE,SAAS;AAAA,IAC5B,WAAW,cAAE,OAAO,EAAE,SAAS;AAAA,EACjC,CAAC,EACA,SAAS;AAAA,EACZ,OAAO,cAAE,OAAO,EAAE,SAAS;AAAA;AAC7B,CAAC;;;ADpTD,IAAM,2BAAmD;AAAA,EACvD,sBAAsB;AAAA,EACtB,mCAAmC;AAAA,EACnC,gBAAgB;AAAA,EAChB,uBAAuB;AAAA,EACvB,mCAAmC;AAAA,EACnC,mCAAmC;AACrC;AAGA,IAAM,iCAAyD;AAAA,EAC7D,mBAAmB;AAAA,EACnB,wBAAwB;AAAA,EACxB,0BAA0B;AAAA;AAAA,EAE1B,0BAA0B;AAAA,EAC1B,+BAA+B;AAAA,EAC/B,wCACE;AAAA,EACF,oCACE;AAAA,EACF,gCACE;AAAA;AAAA,EAEF,oBAAoB;AACtB;AAGA,IAAM,0BAAkD;AAAA,EACtD,sBAAsB;AAAA,EACtB,mCAAmC;AAAA,EACnC,mCAAmC;AACrC;AAMA,SAAS,kBAAkB,SAAyB;AAClD,QAAM,aAAa,QAAQ,QAAQ,OAAO,GAAG;AAC7C,SAAO,4BAA4B,UAAU;AAC/C;AAEO,SAAS,aACd,UAAkC,CAAC,GACnB;AAChB,QAAM,aAAa,OAAO;AAAA,IACxB,eAAe,cAAU,mCAAW;AAAA,MAClC,QAAQ,QAAQ;AAAA,MAChB,yBAAyB;AAAA,MACzB,aAAa;AAAA,IACf,CAAC,CAAC;AAAA,IACF,GAAG,QAAQ;AAAA,EACb;AAEA,QAAM,cAA6B,OAAO,KAAK,gBAAgB;AAC7D,QAAI,aAAa,MAAM;AACrB,UAAI;AACF,cAAM,OAAO,KAAK,MAAM,YAAY,IAAc;AAClD,YAAI,KAAK,WAAW,QAAQ,CAAC,KAAK,gBAAgB;AAChD,eAAK,iBAAiB,EAAE,eAAe,KAAK;AAC5C,sBAAY,OAAO,KAAK,UAAU,IAAI;AAAA,QACxC;AAAA,MACF,QAAQ;AAAA,MAAC;AAAA,IACX;AACA,UAAM,UAAU,QAAQ,SAAS;AACjC,WAAO,QAAQ,KAAK,WAAW;AAAA,EACjC;AASA,QAAM,iBAAiB,CACrB,SACA,cACsB;AACtB,QAAI;AAEJ,QAAI,QAAQ,SAAS;AACnB,gBAAU,QAAQ;AAAA,IACpB,OAAO;AAEL,gBAAU,yBAAyB,OAAO,KAAK,kBAAkB,OAAO;AAAA,IAC1E;AAEA,WAAO;AAAA,MACL,UAAU,UAAU,SAAS;AAAA,MAC7B,KAAK,CAAC,EAAE,KAAK,MAAM,OAAG,6CAAqB,OAAO,CAAC,GAAG,IAAI;AAAA,MAC1D,SAAS;AAAA,MACT,OAAO;AAAA,IACT;AAAA,EACF;AAEA,QAAM,kBAAkB,CAAC,YAAoB;AAC3C,UAAM,kBAAkB,wBAAwB,OAAO,KAAK;AAC5D,WAAO,IAAI,2DAAkC,iBAAiB;AAAA,MAC5D,GAAG,eAAe,SAAS,MAAM;AAAA,MACjC,cAAc;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,QAAM,wBAAwB,CAAC,YAAoB;AACjD,UAAM,kBAAkB,wBAAwB,OAAO,KAAK;AAC5D,WAAO,IAAI,iEAAwC,iBAAiB;AAAA,MAClE,GAAG,eAAe,SAAS,YAAY;AAAA,MACvC,cAAc;AAAA,IAChB,CAAC;AAAA,EACH;AAEA,QAAM,mBAAmB,CAAC,YAAoB;AAC5C,QAAI;AAEJ,QAAI,QAAQ,SAAS;AACnB,gBAAU,QAAQ;AAAA,IACpB,OAAO;AAEL,gBACE,+BAA+B,OAAO,KAAK,kBAAkB,OAAO;AAAA,IACxE;AAEA,WAAO,IAAI,iBAAiB,SAAS;AAAA,MACnC,UAAU;AAAA,MACV;AAAA,MACA,SAAS;AAAA,MACT,OAAO,QAAQ;AAAA,IACjB,CAAC;AAAA,EACH;AAEA,QAAM,WAAW,CAAC,YAAoB,gBAAgB,OAAO;AAE7D,WAAS,kBAAkB;AAC3B,WAAS,gBAAgB;AACzB,WAAS,YAAY;AACrB,WAAS,aAAa;AAEtB,SAAO;AACT;AAEO,IAAM,SAAS,aAAa;","names":["import_provider_utils","import_provider_utils","import_zod"]}
|
package/dist/index.mjs
CHANGED
|
@@ -320,7 +320,11 @@ var runpodImageStatusSchema = z2.object({
|
|
|
320
320
|
// src/runpod-provider.ts
|
|
321
321
|
var MODEL_ID_TO_ENDPOINT_URL = {
|
|
322
322
|
"qwen/qwen3-32b-awq": "https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1",
|
|
323
|
-
"ibm-granite/granite-4.0-h-small": "https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1"
|
|
323
|
+
"ibm-granite/granite-4.0-h-small": "https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1",
|
|
324
|
+
"gpt-oss-120b": "https://api.runpod.ai/v2/gpt-oss-120b/openai/v1",
|
|
325
|
+
"openai/gpt-oss-120b": "https://api.runpod.ai/v2/gpt-oss-120b/openai/v1",
|
|
326
|
+
"deepcogito/cogito-671b-v2.1-fp8": "https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1",
|
|
327
|
+
"deepcogito/cogito-671b-v2.1-FP8": "https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1"
|
|
324
328
|
};
|
|
325
329
|
var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
|
|
326
330
|
"qwen/qwen-image": "https://api.runpod.ai/v2/qwen-image-t2i",
|
|
@@ -336,7 +340,9 @@ var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
|
|
|
336
340
|
"nano-banana-edit": "https://api.runpod.ai/v2/nano-banana-edit"
|
|
337
341
|
};
|
|
338
342
|
var MODEL_ID_TO_OPENAI_NAME = {
|
|
339
|
-
"qwen/qwen3-32b-awq": "Qwen/Qwen3-32B-AWQ"
|
|
343
|
+
"qwen/qwen3-32b-awq": "Qwen/Qwen3-32B-AWQ",
|
|
344
|
+
"deepcogito/cogito-671b-v2.1-fp8": "deepcogito/cogito-671b-v2.1-FP8",
|
|
345
|
+
"deepcogito/cogito-671b-v2.1-FP8": "deepcogito/cogito-671b-v2.1-FP8"
|
|
340
346
|
};
|
|
341
347
|
function deriveEndpointURL(modelId) {
|
|
342
348
|
const normalized = modelId.replace(/\//g, "-");
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runpod-provider.ts","../src/runpod-image-model.ts","../src/runpod-error.ts"],"sourcesContent":["import { LanguageModelV2, ImageModelV2 } from '@ai-sdk/provider';\nimport {\n OpenAICompatibleChatLanguageModel,\n OpenAICompatibleCompletionLanguageModel,\n} from '@ai-sdk/openai-compatible';\nimport {\n FetchFunction,\n loadApiKey,\n withoutTrailingSlash,\n} from '@ai-sdk/provider-utils';\nimport { RunpodImageModel } from './runpod-image-model';\n\nexport interface RunpodProviderSettings {\n /**\nRunpod API key.\n*/\n apiKey?: string;\n /**\nCustom base URL for Runpod API. Use this to point to custom endpoints or different Runpod deployments.\nExample: 'https://api.runpod.ai/v2/your-endpoint-id/openai/v1'\n*/\n baseURL?: string;\n /**\nCustom headers to include in the requests.\n*/\n headers?: Record<string, string>;\n /**\nCustom fetch implementation. You can use it as a middleware to intercept requests,\nor to provide a custom fetch implementation for e.g. testing.\n*/\n fetch?: FetchFunction;\n}\n\nexport interface RunpodProvider {\n /**\nCreates a model for text generation.\n*/\n (modelId: string): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: string): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: string): LanguageModelV2;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: string): LanguageModelV2;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: string): ImageModelV2;\n}\n\n// Mapping of Runpod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n 'ibm-granite/granite-4.0-h-small': 'https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1',\n};\n\n// Mapping of Runpod image model IDs to their endpoint URLs\nconst IMAGE_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen-image': 'https://api.runpod.ai/v2/qwen-image-t2i',\n 'qwen/qwen-image-edit': 'https://api.runpod.ai/v2/qwen-image-edit',\n 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\n // Seadream v4 (t2i and edit)\n 'bytedance/seedream-4.0': 'https://api.runpod.ai/v2/seedream-v4-t2i',\n 'bytedance/seedream-4.0-edit': 'https://api.runpod.ai/v2/seedream-v4-edit',\n 'black-forest-labs/flux-1-kontext-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev',\n 'black-forest-labs/flux-1-schnell':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell',\n 'black-forest-labs/flux-1-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-dev',\n // Nano Banana (edit only)\n 'nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit',\n};\n\n// Mapping of Runpod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\n};\n\n/**\n * Derives the endpoint URL for a model by replacing slashes with hyphens.\n * Example: 'ibm-granite/granite-4.0-h-small' -> 'https://api.runpod.ai/v2/ibm-granite-granite-4.0-h-small/openai/v1'\n */\nfunction deriveEndpointURL(modelId: string): string {\n const normalized = modelId.replace(/\\//g, '-');\n return `https://api.runpod.ai/v2/${normalized}/openai/v1`;\n}\n\nexport function createRunpod(\n options: RunpodProviderSettings = {}\n): RunpodProvider {\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'RUNPOD_API_KEY',\n description: 'Runpod',\n })}`,\n ...options.headers,\n });\n\n const runpodFetch: FetchFunction = async (url, requestInit) => {\n if (requestInit?.body) {\n try {\n const body = JSON.parse(requestInit.body as string);\n if (body.stream === true && !body.stream_options) {\n body.stream_options = { include_usage: true };\n requestInit.body = JSON.stringify(body);\n }\n } catch {}\n }\n const fetchFn = options.fetch || fetch;\n return fetchFn(url, requestInit);\n };\n\n interface CommonModelConfig {\n provider: string;\n url: ({ path }: { path: string }) => string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n }\n\n const getModelConfig = (\n modelId: string,\n modelType: string\n ): CommonModelConfig => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: runpodFetch,\n };\n };\n\n const createChatModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'chat'),\n includeUsage: true,\n });\n };\n\n const createCompletionModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'completion'),\n includeUsage: true,\n });\n };\n\n const createImageModel = (modelId: string) => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL =\n IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return new RunpodImageModel(modelId, {\n provider: 'runpod.image',\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n });\n };\n\n const provider = (modelId: string) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n provider.imageModel = createImageModel;\n\n return provider;\n}\n\nexport const runpod = createRunpod();\n","import { ImageModelV2, ImageModelV2CallWarning } from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createJsonResponseHandler,\n createBinaryResponseHandler,\n FetchFunction,\n postJsonToApi,\n getFromApi,\n} from '@ai-sdk/provider-utils';\nimport { InvalidArgumentError } from '@ai-sdk/provider';\nimport { z } from 'zod';\nimport { runpodImageFailedResponseHandler } from './runpod-error';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ninterface RunpodImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\n// Runpod supported aspect ratios (only validated working sizes)\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\n '1:1': '1328*1328', // ✅ Native support\n '4:3': '1472*1140', // ✅ Native support\n '3:4': '1140*1472', // ✅ Native support\n};\n\n// Runpod supported sizes (validated working sizes)\nconst SUPPORTED_SIZES = new Set([\n // Native aspect ratio sizes\n '1328*1328', // 1:1\n '1472*1140', // 4:3\n '1140*1472', // 3:4\n // Additional validated sizes\n '512*512',\n '768*768',\n '1024*1024',\n '1536*1536',\n '2048*2048',\n '4096*4096',\n '512*768',\n '768*512',\n '1024*768',\n '768*1024',\n]);\n\nexport class RunpodImageModel implements ImageModelV2 {\n readonly specificationVersion = 'v2';\n readonly maxImagesPerCall = 1;\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private config: RunpodImageModelConfig\n ) {}\n\n async doGenerate({\n prompt,\n n = 1,\n size,\n aspectRatio,\n seed,\n providerOptions,\n headers,\n abortSignal,\n }: Parameters<ImageModelV2['doGenerate']>[0]): Promise<\n Awaited<ReturnType<ImageModelV2['doGenerate']>>\n > {\n const warnings: Array<ImageModelV2CallWarning> = [];\n\n // Determine the size to use\n let runpodSize: string;\n\n if (size) {\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\n const runpodSizeCandidate = size.replace('x', '*');\n\n // Validate size is supported\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\n throw new InvalidArgumentError({\n argument: 'size',\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\n SUPPORTED_SIZES\n )\n .map((s) => s.replace('*', 'x'))\n .join(', ')}`,\n });\n }\n\n runpodSize = runpodSizeCandidate;\n } else if (aspectRatio) {\n // Validate aspect ratio is supported\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\n throw new InvalidArgumentError({\n argument: 'aspectRatio',\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\n });\n }\n\n // Use supported aspect ratio mapping\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\n } else {\n // Default to square format\n runpodSize = '1328*1328';\n }\n\n // Handle multiple images warning\n if (n > 1) {\n warnings.push({\n type: 'unsupported-setting',\n setting: 'n',\n details:\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\n });\n }\n\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n // Runpod uses a different request format - /runsync endpoint with input wrapper\n const inputPayload = this.buildInputPayload(\n prompt,\n runpodSize,\n seed,\n providerOptions.runpod\n );\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url: `${this.config.baseURL}/runsync`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n input: inputPayload,\n },\n failedResponseHandler: runpodImageFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageResponseSchema as any\n ),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n // Handle both sync and async responses from Runpod\n const typedResponse = response as any;\n if (\n typedResponse.status === 'COMPLETED' &&\n (typedResponse.output?.result || typedResponse.output?.image_url)\n ) {\n // Sync response - image is ready\n // Different models use different response formats: result vs image_url\n const imageUrl =\n typedResponse.output.result || typedResponse.output.image_url;\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n cost: typedResponse.output?.cost,\n },\n ],\n },\n },\n };\n } else if (\n typedResponse.status === 'IN_QUEUE' ||\n typedResponse.status === 'IN_PROGRESS'\n ) {\n // Async response - need to poll for completion\n const pollOptions = {\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\n pollIntervalMillis: providerOptions.runpod\n ?.pollIntervalMillis as number,\n };\n const imageUrl = await this.pollForCompletion(\n typedResponse.id,\n abortSignal,\n pollOptions\n );\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n jobId: typedResponse.id,\n },\n ],\n },\n },\n };\n } else {\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\n }\n }\n\n private async downloadImage(\n imageUrl: string,\n abortSignal?: AbortSignal\n ): Promise<Uint8Array> {\n const { value: imageData } = await getFromApi({\n url: imageUrl,\n successfulResponseHandler: createBinaryResponseHandler(),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n return imageData;\n }\n\n private async pollForCompletion(\n jobId: string,\n abortSignal?: AbortSignal,\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number }\n ): Promise<string> {\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (abortSignal?.aborted) {\n throw new Error('Image generation was aborted');\n }\n\n const { value: statusResponse } = await getFromApi({\n url: `${this.config.baseURL}/status/${jobId}`,\n headers: this.config.headers(),\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageStatusSchema as any\n ),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n\n const typedStatusResponse = statusResponse as any;\n if (\n typedStatusResponse.status === 'COMPLETED' &&\n (typedStatusResponse.output?.result ||\n typedStatusResponse.output?.image_url)\n ) {\n return (\n typedStatusResponse.output.result ||\n typedStatusResponse.output.image_url\n );\n }\n\n if (typedStatusResponse.status === 'FAILED') {\n throw new Error(\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\n );\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new Error(\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\n );\n }\n\n private buildInputPayload(\n prompt: string,\n runpodSize: string,\n seed?: number,\n runpodOptions?: Record<string, unknown>\n ): Record<string, unknown> {\n // Check if this is a Flux model that uses different parameters\n const isFluxModel =\n this.modelId.includes('flux') ||\n this.modelId.includes('black-forest-labs');\n\n if (isFluxModel) {\n // Check if this is Flux Kontext (uses different parameters)\n const isKontext = this.modelId.includes('kontext');\n\n if (isKontext) {\n // Flux Kontext uses size format and has image input\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: 28,\n guidance: 2,\n size: runpodSize,\n output_format: 'png',\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions, // This will include the 'image' parameter if provided\n };\n } else {\n // Regular Flux models use width/height\n const [width, height] = runpodSize.split('*').map(Number);\n\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\n guidance: this.modelId.includes('schnell') ? 7 : 2,\n width,\n height,\n image_format: 'png',\n ...runpodOptions,\n };\n }\n }\n\n // Default format for Qwen and other models\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n }\n}\n\n// Runpod image API response schema (handles both sync and async responses)\nconst runpodImageResponseSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n delayTime: z.number().optional(),\n executionTime: z.number().optional(),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(), // URL to the generated image (Qwen format)\n image_url: z.string().optional(), // URL to the generated image (Flux format)\n })\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\n});\n\n// Schema for polling status endpoint\nconst runpodImageStatusSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(),\n image_url: z.string().optional(),\n })\n .optional(),\n error: z.string().optional(), // Error message if FAILED\n});\n","import { z } from 'zod';\r\nimport { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';\r\n\r\n// Runpod image API error schema (supports both error formats)\r\nexport const runpodImageErrorSchema = z.object({\r\n error: z.string().optional(),\r\n message: z.string().optional(),\r\n});\r\n\r\nexport type RunpodImageErrorData = z.infer<typeof runpodImageErrorSchema>;\r\n\r\nexport const runpodImageFailedResponseHandler = createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: RunpodImageErrorData) => {\r\n // Prefer message if available (more descriptive)\r\n if (data.message) {\r\n return data.message;\r\n }\r\n \r\n // If error field exists, try to extract nested JSON message\r\n if (data.error) {\r\n // Runpod sometimes returns nested JSON in the error field like:\r\n // \"Error submitting task: 400, {\\\"code\\\":400,\\\"message\\\":\\\"...\\\"}\"\r\n // Try to extract the inner message for cleaner error messages\r\n // Find the last occurrence of { which likely starts the JSON object\r\n const lastBraceIndex = data.error.lastIndexOf('{');\r\n if (lastBraceIndex !== -1) {\r\n try {\r\n const jsonStr = data.error.substring(lastBraceIndex);\r\n const nestedError = JSON.parse(jsonStr);\r\n if (nestedError.message && typeof nestedError.message === 'string') {\r\n return nestedError.message;\r\n }\r\n } catch {\r\n // If parsing fails, fall back to the original error string\r\n }\r\n }\r\n return data.error;\r\n }\r\n \r\n return 'Unknown Runpod error';\r\n },\r\n});\r\n\r\n"],"mappings":"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","names":["z","z"]}
|
|
1
|
+
{"version":3,"sources":["../src/runpod-provider.ts","../src/runpod-image-model.ts","../src/runpod-error.ts"],"sourcesContent":["import { LanguageModelV2, ImageModelV2 } from '@ai-sdk/provider';\nimport {\n OpenAICompatibleChatLanguageModel,\n OpenAICompatibleCompletionLanguageModel,\n} from '@ai-sdk/openai-compatible';\nimport {\n FetchFunction,\n loadApiKey,\n withoutTrailingSlash,\n} from '@ai-sdk/provider-utils';\nimport { RunpodImageModel } from './runpod-image-model';\n\nexport interface RunpodProviderSettings {\n /**\nRunpod API key.\n*/\n apiKey?: string;\n /**\nCustom base URL for Runpod API. Use this to point to custom endpoints or different Runpod deployments.\nExample: 'https://api.runpod.ai/v2/your-endpoint-id/openai/v1'\n*/\n baseURL?: string;\n /**\nCustom headers to include in the requests.\n*/\n headers?: Record<string, string>;\n /**\nCustom fetch implementation. You can use it as a middleware to intercept requests,\nor to provide a custom fetch implementation for e.g. testing.\n*/\n fetch?: FetchFunction;\n}\n\nexport interface RunpodProvider {\n /**\nCreates a model for text generation.\n*/\n (modelId: string): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: string): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: string): LanguageModelV2;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: string): LanguageModelV2;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: string): ImageModelV2;\n}\n\n// Mapping of Runpod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n 'ibm-granite/granite-4.0-h-small': 'https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1',\n 'gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'openai/gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'deepcogito/cogito-671b-v2.1-fp8': 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n 'deepcogito/cogito-671b-v2.1-FP8': 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n};\n\n// Mapping of Runpod image model IDs to their endpoint URLs\nconst IMAGE_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen-image': 'https://api.runpod.ai/v2/qwen-image-t2i',\n 'qwen/qwen-image-edit': 'https://api.runpod.ai/v2/qwen-image-edit',\n 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\n // Seadream v4 (t2i and edit)\n 'bytedance/seedream-4.0': 'https://api.runpod.ai/v2/seedream-v4-t2i',\n 'bytedance/seedream-4.0-edit': 'https://api.runpod.ai/v2/seedream-v4-edit',\n 'black-forest-labs/flux-1-kontext-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev',\n 'black-forest-labs/flux-1-schnell':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell',\n 'black-forest-labs/flux-1-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-dev',\n // Nano Banana (edit only)\n 'nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit',\n};\n\n// Mapping of Runpod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\n 'deepcogito/cogito-671b-v2.1-fp8': 'deepcogito/cogito-671b-v2.1-FP8',\n 'deepcogito/cogito-671b-v2.1-FP8': 'deepcogito/cogito-671b-v2.1-FP8',\n};\n\n/**\n * Derives the endpoint URL for a model by replacing slashes with hyphens.\n * Example: 'ibm-granite/granite-4.0-h-small' -> 'https://api.runpod.ai/v2/ibm-granite-granite-4.0-h-small/openai/v1'\n */\nfunction deriveEndpointURL(modelId: string): string {\n const normalized = modelId.replace(/\\//g, '-');\n return `https://api.runpod.ai/v2/${normalized}/openai/v1`;\n}\n\nexport function createRunpod(\n options: RunpodProviderSettings = {}\n): RunpodProvider {\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'RUNPOD_API_KEY',\n description: 'Runpod',\n })}`,\n ...options.headers,\n });\n\n const runpodFetch: FetchFunction = async (url, requestInit) => {\n if (requestInit?.body) {\n try {\n const body = JSON.parse(requestInit.body as string);\n if (body.stream === true && !body.stream_options) {\n body.stream_options = { include_usage: true };\n requestInit.body = JSON.stringify(body);\n }\n } catch {}\n }\n const fetchFn = options.fetch || fetch;\n return fetchFn(url, requestInit);\n };\n\n interface CommonModelConfig {\n provider: string;\n url: ({ path }: { path: string }) => string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n }\n\n const getModelConfig = (\n modelId: string,\n modelType: string\n ): CommonModelConfig => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: runpodFetch,\n };\n };\n\n const createChatModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'chat'),\n includeUsage: true,\n });\n };\n\n const createCompletionModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'completion'),\n includeUsage: true,\n });\n };\n\n const createImageModel = (modelId: string) => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL =\n IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return new RunpodImageModel(modelId, {\n provider: 'runpod.image',\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n });\n };\n\n const provider = (modelId: string) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n provider.imageModel = createImageModel;\n\n return provider;\n}\n\nexport const runpod = createRunpod();\n","import { ImageModelV2, ImageModelV2CallWarning } from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createJsonResponseHandler,\n createBinaryResponseHandler,\n FetchFunction,\n postJsonToApi,\n getFromApi,\n} from '@ai-sdk/provider-utils';\nimport { InvalidArgumentError } from '@ai-sdk/provider';\nimport { z } from 'zod';\nimport { runpodImageFailedResponseHandler } from './runpod-error';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ninterface RunpodImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\n// Runpod supported aspect ratios (only validated working sizes)\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\n '1:1': '1328*1328', // ✅ Native support\n '4:3': '1472*1140', // ✅ Native support\n '3:4': '1140*1472', // ✅ Native support\n};\n\n// Runpod supported sizes (validated working sizes)\nconst SUPPORTED_SIZES = new Set([\n // Native aspect ratio sizes\n '1328*1328', // 1:1\n '1472*1140', // 4:3\n '1140*1472', // 3:4\n // Additional validated sizes\n '512*512',\n '768*768',\n '1024*1024',\n '1536*1536',\n '2048*2048',\n '4096*4096',\n '512*768',\n '768*512',\n '1024*768',\n '768*1024',\n]);\n\nexport class RunpodImageModel implements ImageModelV2 {\n readonly specificationVersion = 'v2';\n readonly maxImagesPerCall = 1;\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private config: RunpodImageModelConfig\n ) {}\n\n async doGenerate({\n prompt,\n n = 1,\n size,\n aspectRatio,\n seed,\n providerOptions,\n headers,\n abortSignal,\n }: Parameters<ImageModelV2['doGenerate']>[0]): Promise<\n Awaited<ReturnType<ImageModelV2['doGenerate']>>\n > {\n const warnings: Array<ImageModelV2CallWarning> = [];\n\n // Determine the size to use\n let runpodSize: string;\n\n if (size) {\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\n const runpodSizeCandidate = size.replace('x', '*');\n\n // Validate size is supported\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\n throw new InvalidArgumentError({\n argument: 'size',\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\n SUPPORTED_SIZES\n )\n .map((s) => s.replace('*', 'x'))\n .join(', ')}`,\n });\n }\n\n runpodSize = runpodSizeCandidate;\n } else if (aspectRatio) {\n // Validate aspect ratio is supported\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\n throw new InvalidArgumentError({\n argument: 'aspectRatio',\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\n });\n }\n\n // Use supported aspect ratio mapping\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\n } else {\n // Default to square format\n runpodSize = '1328*1328';\n }\n\n // Handle multiple images warning\n if (n > 1) {\n warnings.push({\n type: 'unsupported-setting',\n setting: 'n',\n details:\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\n });\n }\n\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n // Runpod uses a different request format - /runsync endpoint with input wrapper\n const inputPayload = this.buildInputPayload(\n prompt,\n runpodSize,\n seed,\n providerOptions.runpod\n );\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url: `${this.config.baseURL}/runsync`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n input: inputPayload,\n },\n failedResponseHandler: runpodImageFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageResponseSchema as any\n ),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n // Handle both sync and async responses from Runpod\n const typedResponse = response as any;\n if (\n typedResponse.status === 'COMPLETED' &&\n (typedResponse.output?.result || typedResponse.output?.image_url)\n ) {\n // Sync response - image is ready\n // Different models use different response formats: result vs image_url\n const imageUrl =\n typedResponse.output.result || typedResponse.output.image_url;\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n cost: typedResponse.output?.cost,\n },\n ],\n },\n },\n };\n } else if (\n typedResponse.status === 'IN_QUEUE' ||\n typedResponse.status === 'IN_PROGRESS'\n ) {\n // Async response - need to poll for completion\n const pollOptions = {\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\n pollIntervalMillis: providerOptions.runpod\n ?.pollIntervalMillis as number,\n };\n const imageUrl = await this.pollForCompletion(\n typedResponse.id,\n abortSignal,\n pollOptions\n );\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n jobId: typedResponse.id,\n },\n ],\n },\n },\n };\n } else {\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\n }\n }\n\n private async downloadImage(\n imageUrl: string,\n abortSignal?: AbortSignal\n ): Promise<Uint8Array> {\n const { value: imageData } = await getFromApi({\n url: imageUrl,\n successfulResponseHandler: createBinaryResponseHandler(),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n return imageData;\n }\n\n private async pollForCompletion(\n jobId: string,\n abortSignal?: AbortSignal,\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number }\n ): Promise<string> {\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (abortSignal?.aborted) {\n throw new Error('Image generation was aborted');\n }\n\n const { value: statusResponse } = await getFromApi({\n url: `${this.config.baseURL}/status/${jobId}`,\n headers: this.config.headers(),\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageStatusSchema as any\n ),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n\n const typedStatusResponse = statusResponse as any;\n if (\n typedStatusResponse.status === 'COMPLETED' &&\n (typedStatusResponse.output?.result ||\n typedStatusResponse.output?.image_url)\n ) {\n return (\n typedStatusResponse.output.result ||\n typedStatusResponse.output.image_url\n );\n }\n\n if (typedStatusResponse.status === 'FAILED') {\n throw new Error(\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\n );\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new Error(\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\n );\n }\n\n private buildInputPayload(\n prompt: string,\n runpodSize: string,\n seed?: number,\n runpodOptions?: Record<string, unknown>\n ): Record<string, unknown> {\n // Check if this is a Flux model that uses different parameters\n const isFluxModel =\n this.modelId.includes('flux') ||\n this.modelId.includes('black-forest-labs');\n\n if (isFluxModel) {\n // Check if this is Flux Kontext (uses different parameters)\n const isKontext = this.modelId.includes('kontext');\n\n if (isKontext) {\n // Flux Kontext uses size format and has image input\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: 28,\n guidance: 2,\n size: runpodSize,\n output_format: 'png',\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions, // This will include the 'image' parameter if provided\n };\n } else {\n // Regular Flux models use width/height\n const [width, height] = runpodSize.split('*').map(Number);\n\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\n guidance: this.modelId.includes('schnell') ? 7 : 2,\n width,\n height,\n image_format: 'png',\n ...runpodOptions,\n };\n }\n }\n\n // Default format for Qwen and other models\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n }\n}\n\n// Runpod image API response schema (handles both sync and async responses)\nconst runpodImageResponseSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n delayTime: z.number().optional(),\n executionTime: z.number().optional(),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(), // URL to the generated image (Qwen format)\n image_url: z.string().optional(), // URL to the generated image (Flux format)\n })\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\n});\n\n// Schema for polling status endpoint\nconst runpodImageStatusSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(),\n image_url: z.string().optional(),\n })\n .optional(),\n error: z.string().optional(), // Error message if FAILED\n});\n","import { z } from 'zod';\r\nimport { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';\r\n\r\n// Runpod image API error schema (supports both error formats)\r\nexport const runpodImageErrorSchema = z.object({\r\n error: z.string().optional(),\r\n message: z.string().optional(),\r\n});\r\n\r\nexport type RunpodImageErrorData = z.infer<typeof runpodImageErrorSchema>;\r\n\r\nexport const runpodImageFailedResponseHandler = createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: RunpodImageErrorData) => {\r\n // Prefer message if available (more descriptive)\r\n if (data.message) {\r\n return data.message;\r\n }\r\n \r\n // If error field exists, try to extract nested JSON message\r\n if (data.error) {\r\n // Runpod sometimes returns nested JSON in the error field like:\r\n // \"Error submitting task: 400, {\\\"code\\\":400,\\\"message\\\":\\\"...\\\"}\"\r\n // Try to extract the inner message for cleaner error messages\r\n // Find the last occurrence of { which likely starts the JSON object\r\n const lastBraceIndex = data.error.lastIndexOf('{');\r\n if (lastBraceIndex !== -1) {\r\n try {\r\n const jsonStr = data.error.substring(lastBraceIndex);\r\n const nestedError = JSON.parse(jsonStr);\r\n if (nestedError.message && typeof nestedError.message === 'string') {\r\n return nestedError.message;\r\n }\r\n } catch {\r\n // If parsing fails, fall back to the original error string\r\n }\r\n }\r\n return data.error;\r\n }\r\n \r\n return 'Unknown Runpod error';\r\n },\r\n});\r\n\r\n"],"mappings":"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","names":["z","z"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@runpod/ai-sdk-provider",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -34,7 +34,7 @@
|
|
|
34
34
|
"tsup": "^8.0.0",
|
|
35
35
|
"typescript": "^5.0.0",
|
|
36
36
|
"vitest": "^2.0.0",
|
|
37
|
-
"zod": "^3.25.
|
|
37
|
+
"zod": "^3.25.76"
|
|
38
38
|
},
|
|
39
39
|
"peerDependencies": {
|
|
40
40
|
"zod": "^3.25.76 || ^4"
|