@runpod/ai-sdk-provider 0.1.1 → 0.2.1
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 +17 -0
- package/README.md +275 -132
- package/dist/index.d.mts +8 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +308 -2
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +313 -0
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,22 @@
|
|
|
1
1
|
# @runpod/ai-sdk-provider
|
|
2
2
|
|
|
3
|
+
## 0.2.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- eb1b7f8: improved docs
|
|
8
|
+
|
|
9
|
+
## 0.2.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 1fec4a8: added support for image gen:
|
|
14
|
+
- `qwen/qwen-image`
|
|
15
|
+
- `bytedance/seedream-3.0`
|
|
16
|
+
- `black-forest-labs/flux-1-kontext-dev`
|
|
17
|
+
- `black-forest-labs/flux-1-schnell`
|
|
18
|
+
- `black-forest-labs/flux-1-dev`
|
|
19
|
+
|
|
3
20
|
## 0.1.1
|
|
4
21
|
|
|
5
22
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -1,132 +1,275 @@
|
|
|
1
|
-
# Runpod AI SDK Provider
|
|
2
|
-
|
|
3
|
-
The **Runpod provider** for the [AI SDK](https://ai-sdk.dev/docs) contains language model support for [Runpod's](https://runpod.io) public endpoints.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
# npm
|
|
9
|
-
npm install @runpod/ai-sdk-provider
|
|
10
|
-
|
|
11
|
-
# pnpm
|
|
12
|
-
pnpm add @runpod/ai-sdk-provider
|
|
13
|
-
|
|
14
|
-
# yarn
|
|
15
|
-
yarn add @runpod/ai-sdk-provider
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
## Setup
|
|
19
|
-
|
|
20
|
-
The Runpod provider requires a Runpod API key. You can obtain one from the [Runpod console](https://console.runpod.io/user/settings) under "API Keys".
|
|
21
|
-
|
|
22
|
-
### Environment Variable
|
|
23
|
-
|
|
24
|
-
Set your API key as an environment variable:
|
|
25
|
-
|
|
26
|
-
```bash
|
|
27
|
-
export RUNPOD_API_KEY="your-api-key-here"
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### Provider Instance
|
|
31
|
-
|
|
32
|
-
Import the provider:
|
|
33
|
-
|
|
34
|
-
```ts
|
|
35
|
-
import { runpod } from '@runpod/ai-sdk-provider';
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
## Supported Models
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
|
43
|
-
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
###
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
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
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
# npm
|
|
9
|
+
npm install @runpod/ai-sdk-provider
|
|
10
|
+
|
|
11
|
+
# pnpm
|
|
12
|
+
pnpm add @runpod/ai-sdk-provider
|
|
13
|
+
|
|
14
|
+
# yarn
|
|
15
|
+
yarn add @runpod/ai-sdk-provider
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Setup
|
|
19
|
+
|
|
20
|
+
The Runpod provider requires a Runpod API key. You can obtain one from the [Runpod console](https://console.runpod.io/user/settings) under "API Keys".
|
|
21
|
+
|
|
22
|
+
### Environment Variable
|
|
23
|
+
|
|
24
|
+
Set your API key as an environment variable:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
export RUNPOD_API_KEY="your-api-key-here"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Provider Instance
|
|
31
|
+
|
|
32
|
+
Import the provider:
|
|
33
|
+
|
|
34
|
+
```ts
|
|
35
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Supported Models
|
|
39
|
+
|
|
40
|
+
### Language Models
|
|
41
|
+
|
|
42
|
+
| Model ID | Description |
|
|
43
|
+
| -------------------------------------- | ------------------------------------------------------------------- |
|
|
44
|
+
| `deep-cogito/deep-cogito-v2-llama-70b` | 70B parameter general-purpose LLM with advanced reasoning |
|
|
45
|
+
| `qwen/qwen3-32b-awq` | 32B parameter multilingual model with strong reasoning capabilities |
|
|
46
|
+
|
|
47
|
+
### Image Models
|
|
48
|
+
|
|
49
|
+
| Model ID | Description | Supported Aspect Ratios |
|
|
50
|
+
| -------------------------------------- | ------------------------------- | ----------------------- |
|
|
51
|
+
| `qwen/qwen-image` | Text-to-image generation | 1:1, 4:3, 3:4 |
|
|
52
|
+
| `bytedance/seedream-3.0` | Advanced text-to-image model | 1:1, 4:3, 3:4 |
|
|
53
|
+
| `black-forest-labs/flux-1-kontext-dev` | Context-aware image generation | 1:1, 4:3, 3:4 |
|
|
54
|
+
| `black-forest-labs/flux-1-schnell` | Fast image generation (4 steps) | 1:1, 4:3, 3:4 |
|
|
55
|
+
| `black-forest-labs/flux-1-dev` | High-quality image generation | 1:1, 4:3, 3:4 |
|
|
56
|
+
|
|
57
|
+
## Text Generation
|
|
58
|
+
|
|
59
|
+
### Basic Usage
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
63
|
+
import { generateText } from 'ai';
|
|
64
|
+
|
|
65
|
+
const { text } = await generateText({
|
|
66
|
+
model: runpod('deep-cogito/deep-cogito-v2-llama-70b'),
|
|
67
|
+
prompt: 'Write a Python function that sorts a list:',
|
|
68
|
+
});
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
**Returns:**
|
|
72
|
+
|
|
73
|
+
- `text` - Generated text string
|
|
74
|
+
- `finishReason` - Why generation stopped ('stop', 'length', etc.)
|
|
75
|
+
- `usage` - Token usage information (prompt, completion, total tokens)
|
|
76
|
+
|
|
77
|
+
### Chat Conversations
|
|
78
|
+
|
|
79
|
+
```ts
|
|
80
|
+
const { text } = await generateText({
|
|
81
|
+
model: runpod('deep-cogito/deep-cogito-v2-llama-70b'),
|
|
82
|
+
messages: [
|
|
83
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
84
|
+
{ role: 'user', content: 'What is the capital of France?' },
|
|
85
|
+
],
|
|
86
|
+
});
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### Function Calling
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { generateText, tool } from 'ai';
|
|
93
|
+
import { z } from 'zod';
|
|
94
|
+
|
|
95
|
+
const { text, toolCalls } = await generateText({
|
|
96
|
+
model: runpod('deep-cogito/deep-cogito-v2-llama-70b'),
|
|
97
|
+
prompt: 'What is the weather like in San Francisco?',
|
|
98
|
+
tools: {
|
|
99
|
+
getWeather: tool({
|
|
100
|
+
description: 'Get weather information for a city',
|
|
101
|
+
parameters: z.object({
|
|
102
|
+
city: z.string().describe('The city name'),
|
|
103
|
+
}),
|
|
104
|
+
execute: async ({ city }) => {
|
|
105
|
+
return `The weather in ${city} is sunny.`;
|
|
106
|
+
},
|
|
107
|
+
}),
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
**Additional Returns:**
|
|
113
|
+
|
|
114
|
+
- `toolCalls` - Array of tool calls made by the model
|
|
115
|
+
- `toolResults` - Results from executed tools
|
|
116
|
+
|
|
117
|
+
### Structured Output
|
|
118
|
+
|
|
119
|
+
```ts
|
|
120
|
+
import { generateObject } from 'ai';
|
|
121
|
+
import { z } from 'zod';
|
|
122
|
+
|
|
123
|
+
const { object } = await generateObject({
|
|
124
|
+
model: runpod('qwen/qwen3-32b-awq'),
|
|
125
|
+
schema: z.object({
|
|
126
|
+
recipe: z.object({
|
|
127
|
+
name: z.string(),
|
|
128
|
+
ingredients: z.array(z.string()),
|
|
129
|
+
steps: z.array(z.string()),
|
|
130
|
+
}),
|
|
131
|
+
}),
|
|
132
|
+
prompt: 'Generate a recipe for chocolate chip cookies.',
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Returns:**
|
|
137
|
+
|
|
138
|
+
- `object` - Parsed object matching your schema
|
|
139
|
+
- `usage` - Token usage information
|
|
140
|
+
|
|
141
|
+
### Streaming
|
|
142
|
+
|
|
143
|
+
**Note**: Streaming is not yet supported by Runpod's public endpoints. The team is working on implementing this feature.
|
|
144
|
+
|
|
145
|
+
## Image Generation
|
|
146
|
+
|
|
147
|
+
### Basic Usage
|
|
148
|
+
|
|
149
|
+
```ts
|
|
150
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
151
|
+
import { experimental_generateImage as generateImage } from 'ai';
|
|
152
|
+
|
|
153
|
+
const { image } = await generateImage({
|
|
154
|
+
model: runpod.imageModel('qwen/qwen-image'),
|
|
155
|
+
prompt: 'A fashion-forward woman in Paris wearing a trench coat',
|
|
156
|
+
aspectRatio: '4:3',
|
|
157
|
+
});
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
**Returns:**
|
|
161
|
+
|
|
162
|
+
- `image.uint8Array` - Binary image data (efficient for processing/saving)
|
|
163
|
+
- `image.base64` - Base64 encoded string (for web display)
|
|
164
|
+
- `image.mediaType` - MIME type ('image/jpeg' or 'image/png')
|
|
165
|
+
- `warnings` - Array of any warnings about unsupported parameters
|
|
166
|
+
|
|
167
|
+
### Advanced Parameters
|
|
168
|
+
|
|
169
|
+
```ts
|
|
170
|
+
const { image } = await generateImage({
|
|
171
|
+
model: runpod.imageModel('bytedance/seedream-3.0'),
|
|
172
|
+
prompt: 'A sunset over mountains',
|
|
173
|
+
size: '1328x1328',
|
|
174
|
+
seed: 42,
|
|
175
|
+
providerOptions: {
|
|
176
|
+
runpod: {
|
|
177
|
+
negative_prompt: 'blurry, low quality',
|
|
178
|
+
enable_safety_checker: true,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// Save to filesystem
|
|
184
|
+
import { writeFileSync } from 'fs';
|
|
185
|
+
writeFileSync('generated-image.jpg', image.uint8Array);
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Context-Aware Generation (Flux Kontext)
|
|
189
|
+
|
|
190
|
+
```ts
|
|
191
|
+
const { image } = await generateImage({
|
|
192
|
+
model: runpod.imageModel('black-forest-labs/flux-1-kontext-dev'),
|
|
193
|
+
prompt: 'Transform this into a cyberpunk style with neon lights',
|
|
194
|
+
aspectRatio: '1:1',
|
|
195
|
+
providerOptions: {
|
|
196
|
+
runpod: {
|
|
197
|
+
image: 'https://example.com/input-image.jpg', // Image URL
|
|
198
|
+
negative_prompt: 'blurry, distorted',
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
// Alternative: Using base64 encoded image
|
|
204
|
+
const { image } = await generateImage({
|
|
205
|
+
model: runpod.imageModel('black-forest-labs/flux-1-kontext-dev'),
|
|
206
|
+
prompt: 'Make this image look like a painting',
|
|
207
|
+
aspectRatio: '4:3',
|
|
208
|
+
providerOptions: {
|
|
209
|
+
runpod: {
|
|
210
|
+
image: 'data:image/png;base64,iVBORw0KGgoAAAANS...', // Base64 data URI
|
|
211
|
+
negative_prompt: 'blurry, distorted',
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
});
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Advanced Configuration
|
|
218
|
+
|
|
219
|
+
```ts
|
|
220
|
+
// Full control over generation parameters
|
|
221
|
+
const { image } = await generateImage({
|
|
222
|
+
model: runpod.imageModel('black-forest-labs/flux-1-dev'),
|
|
223
|
+
prompt: 'A majestic dragon breathing fire in a medieval castle',
|
|
224
|
+
size: '1328x1328',
|
|
225
|
+
seed: 42, // For reproducible results
|
|
226
|
+
providerOptions: {
|
|
227
|
+
runpod: {
|
|
228
|
+
negative_prompt: 'blurry, low quality, distorted, ugly, bad anatomy',
|
|
229
|
+
enable_safety_checker: true,
|
|
230
|
+
num_inference_steps: 50, // Higher quality (default: 28)
|
|
231
|
+
guidance: 3.5, // Stronger prompt adherence (default: 2)
|
|
232
|
+
output_format: 'png', // High quality format
|
|
233
|
+
// Polling settings for long generations
|
|
234
|
+
maxPollAttempts: 30,
|
|
235
|
+
pollIntervalMillis: 4000,
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Fast generation with minimal steps
|
|
241
|
+
const { image } = await generateImage({
|
|
242
|
+
model: runpod.imageModel('black-forest-labs/flux-1-schnell'),
|
|
243
|
+
prompt: 'A simple red apple',
|
|
244
|
+
aspectRatio: '1:1',
|
|
245
|
+
providerOptions: {
|
|
246
|
+
runpod: {
|
|
247
|
+
num_inference_steps: 2, // Even faster (default: 4)
|
|
248
|
+
guidance: 10, // Higher guidance for simple prompts
|
|
249
|
+
output_format: 'jpg', // Smaller file size
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### Provider Options
|
|
256
|
+
|
|
257
|
+
| Option | Type | Default | Description |
|
|
258
|
+
| ----------------------- | --------- | ------- | ----------------------------------------------------------------------- |
|
|
259
|
+
| `negative_prompt` | `string` | `""` | Text describing what you don't want in the image |
|
|
260
|
+
| `enable_safety_checker` | `boolean` | `true` | Enable content safety filtering |
|
|
261
|
+
| `image` | `string` | - | Input image: URL or base64 data URI (required for Flux Kontext models) |
|
|
262
|
+
| `num_inference_steps` | `number` | Auto | Number of denoising steps (Flux: 4 for schnell, 28 for others) |
|
|
263
|
+
| `guidance` | `number` | Auto | Guidance scale for prompt adherence (Flux: 7 for schnell, 2 for others) |
|
|
264
|
+
| `output_format` | `string` | `"png"` | Output image format ("png" or "jpg") |
|
|
265
|
+
| `maxPollAttempts` | `number` | `60` | Maximum polling attempts for async generation |
|
|
266
|
+
| `pollIntervalMillis` | `number` | `5000` | Polling interval in milliseconds (5 seconds) |
|
|
267
|
+
|
|
268
|
+
**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.
|
|
269
|
+
|
|
270
|
+
## Links
|
|
271
|
+
|
|
272
|
+
- [Runpod](https://runpod.io) - Cloud platform for AI compute
|
|
273
|
+
- [Runpod Public Endpoints Documentation](https://docs.runpod.io/hub/public-endpoints)
|
|
274
|
+
- [AI SDK Documentation](https://ai-sdk.dev/docs)
|
|
275
|
+
- [GitHub Repository](https://github.com/runpod/ai-sdk-provider)
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LanguageModelV2 } from '@ai-sdk/provider';
|
|
1
|
+
import { LanguageModelV2, ImageModelV2 } from '@ai-sdk/provider';
|
|
2
2
|
import { FetchFunction } from '@ai-sdk/provider-utils';
|
|
3
3
|
export { OpenAICompatibleErrorData as RunPodErrorData } from '@ai-sdk/openai-compatible';
|
|
4
4
|
|
|
@@ -6,6 +6,8 @@ type RunPodChatModelId = 'deep-cogito/deep-cogito-v2-llama-70b' | 'qwen/qwen3-32
|
|
|
6
6
|
|
|
7
7
|
type RunPodCompletionModelId = 'deep-cogito/deep-cogito-v2-llama-70b' | 'qwen/qwen3-32b-awq' | (string & {});
|
|
8
8
|
|
|
9
|
+
type RunpodImageModelId = 'qwen/qwen-image' | 'bytedance/seedream-3.0' | 'black-forest-labs/flux-1-kontext-dev' | 'black-forest-labs/flux-1-schnell' | 'black-forest-labs/flux-1-dev';
|
|
10
|
+
|
|
9
11
|
interface RunPodProviderSettings {
|
|
10
12
|
/**
|
|
11
13
|
RunPod API key.
|
|
@@ -38,8 +40,12 @@ interface RunPodProvider {
|
|
|
38
40
|
Creates a completion model for text generation.
|
|
39
41
|
*/
|
|
40
42
|
completionModel(modelId: RunPodCompletionModelId): LanguageModelV2;
|
|
43
|
+
/**
|
|
44
|
+
Creates an image model for image generation.
|
|
45
|
+
*/
|
|
46
|
+
imageModel(modelId: RunpodImageModelId): ImageModelV2;
|
|
41
47
|
}
|
|
42
48
|
declare function createRunPod(options?: RunPodProviderSettings): RunPodProvider;
|
|
43
49
|
declare const runpod: RunPodProvider;
|
|
44
50
|
|
|
45
|
-
export { type RunPodChatModelId, type RunPodCompletionModelId, type RunPodProvider, type RunPodProviderSettings, createRunPod, runpod };
|
|
51
|
+
export { type RunPodChatModelId, type RunPodCompletionModelId, type RunPodProvider, type RunPodProviderSettings, type RunpodImageModelId, createRunPod, runpod };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { LanguageModelV2 } from '@ai-sdk/provider';
|
|
1
|
+
import { LanguageModelV2, ImageModelV2 } from '@ai-sdk/provider';
|
|
2
2
|
import { FetchFunction } from '@ai-sdk/provider-utils';
|
|
3
3
|
export { OpenAICompatibleErrorData as RunPodErrorData } from '@ai-sdk/openai-compatible';
|
|
4
4
|
|
|
@@ -6,6 +6,8 @@ type RunPodChatModelId = 'deep-cogito/deep-cogito-v2-llama-70b' | 'qwen/qwen3-32
|
|
|
6
6
|
|
|
7
7
|
type RunPodCompletionModelId = 'deep-cogito/deep-cogito-v2-llama-70b' | 'qwen/qwen3-32b-awq' | (string & {});
|
|
8
8
|
|
|
9
|
+
type RunpodImageModelId = 'qwen/qwen-image' | 'bytedance/seedream-3.0' | 'black-forest-labs/flux-1-kontext-dev' | 'black-forest-labs/flux-1-schnell' | 'black-forest-labs/flux-1-dev';
|
|
10
|
+
|
|
9
11
|
interface RunPodProviderSettings {
|
|
10
12
|
/**
|
|
11
13
|
RunPod API key.
|
|
@@ -38,8 +40,12 @@ interface RunPodProvider {
|
|
|
38
40
|
Creates a completion model for text generation.
|
|
39
41
|
*/
|
|
40
42
|
completionModel(modelId: RunPodCompletionModelId): LanguageModelV2;
|
|
43
|
+
/**
|
|
44
|
+
Creates an image model for image generation.
|
|
45
|
+
*/
|
|
46
|
+
imageModel(modelId: RunpodImageModelId): ImageModelV2;
|
|
41
47
|
}
|
|
42
48
|
declare function createRunPod(options?: RunPodProviderSettings): RunPodProvider;
|
|
43
49
|
declare const runpod: RunPodProvider;
|
|
44
50
|
|
|
45
|
-
export { type RunPodChatModelId, type RunPodCompletionModelId, type RunPodProvider, type RunPodProviderSettings, createRunPod, runpod };
|
|
51
|
+
export { type RunPodChatModelId, type RunPodCompletionModelId, type RunPodProvider, type RunPodProviderSettings, type RunpodImageModelId, createRunPod, runpod };
|
package/dist/index.js
CHANGED
|
@@ -27,18 +27,307 @@ module.exports = __toCommonJS(index_exports);
|
|
|
27
27
|
|
|
28
28
|
// src/runpod-provider.ts
|
|
29
29
|
var import_openai_compatible = require("@ai-sdk/openai-compatible");
|
|
30
|
+
var import_provider_utils2 = require("@ai-sdk/provider-utils");
|
|
31
|
+
|
|
32
|
+
// src/runpod-image-model.ts
|
|
30
33
|
var import_provider_utils = require("@ai-sdk/provider-utils");
|
|
34
|
+
var import_provider = require("@ai-sdk/provider");
|
|
35
|
+
var import_zod = require("zod");
|
|
36
|
+
var SUPPORTED_ASPECT_RATIOS = {
|
|
37
|
+
"1:1": "1328*1328",
|
|
38
|
+
// ✅ Native support
|
|
39
|
+
"4:3": "1472*1140",
|
|
40
|
+
// ✅ Native support
|
|
41
|
+
"3:4": "1140*1472"
|
|
42
|
+
// ✅ Native support
|
|
43
|
+
};
|
|
44
|
+
var SUPPORTED_SIZES = /* @__PURE__ */ new Set([
|
|
45
|
+
// Native aspect ratio sizes
|
|
46
|
+
"1328*1328",
|
|
47
|
+
// 1:1
|
|
48
|
+
"1472*1140",
|
|
49
|
+
// 4:3
|
|
50
|
+
"1140*1472",
|
|
51
|
+
// 3:4
|
|
52
|
+
// Additional validated sizes
|
|
53
|
+
"512*512",
|
|
54
|
+
"768*768",
|
|
55
|
+
"1024*1024",
|
|
56
|
+
"512*768",
|
|
57
|
+
"768*512",
|
|
58
|
+
"1024*768",
|
|
59
|
+
"768*1024"
|
|
60
|
+
]);
|
|
61
|
+
var RunpodImageModel = class {
|
|
62
|
+
constructor(modelId, config) {
|
|
63
|
+
this.modelId = modelId;
|
|
64
|
+
this.config = config;
|
|
65
|
+
this.specificationVersion = "v2";
|
|
66
|
+
this.maxImagesPerCall = 1;
|
|
67
|
+
}
|
|
68
|
+
get provider() {
|
|
69
|
+
return this.config.provider;
|
|
70
|
+
}
|
|
71
|
+
async doGenerate({
|
|
72
|
+
prompt,
|
|
73
|
+
n = 1,
|
|
74
|
+
size,
|
|
75
|
+
aspectRatio,
|
|
76
|
+
seed,
|
|
77
|
+
providerOptions,
|
|
78
|
+
headers,
|
|
79
|
+
abortSignal
|
|
80
|
+
}) {
|
|
81
|
+
const warnings = [];
|
|
82
|
+
let runpodSize;
|
|
83
|
+
if (size) {
|
|
84
|
+
const runpodSizeCandidate = size.replace("x", "*");
|
|
85
|
+
if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {
|
|
86
|
+
throw new import_provider.InvalidArgumentError({
|
|
87
|
+
argument: "size",
|
|
88
|
+
message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(
|
|
89
|
+
SUPPORTED_SIZES
|
|
90
|
+
).map((s) => s.replace("*", "x")).join(", ")}`
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
runpodSize = runpodSizeCandidate;
|
|
94
|
+
} else if (aspectRatio) {
|
|
95
|
+
if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {
|
|
96
|
+
throw new import_provider.InvalidArgumentError({
|
|
97
|
+
argument: "aspectRatio",
|
|
98
|
+
message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(", ")}`
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];
|
|
102
|
+
} else {
|
|
103
|
+
runpodSize = "1328*1328";
|
|
104
|
+
}
|
|
105
|
+
if (n > 1) {
|
|
106
|
+
warnings.push({
|
|
107
|
+
type: "unsupported-setting",
|
|
108
|
+
setting: "n",
|
|
109
|
+
details: "Runpod image models only support generating 1 image at a time. Using n=1."
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
|
|
113
|
+
const inputPayload = this.buildInputPayload(
|
|
114
|
+
prompt,
|
|
115
|
+
runpodSize,
|
|
116
|
+
seed,
|
|
117
|
+
providerOptions.runpod
|
|
118
|
+
);
|
|
119
|
+
const { value: response, responseHeaders } = await (0, import_provider_utils.postJsonToApi)({
|
|
120
|
+
url: `${this.config.baseURL}/runsync`,
|
|
121
|
+
headers: (0, import_provider_utils.combineHeaders)(this.config.headers(), headers),
|
|
122
|
+
body: {
|
|
123
|
+
input: inputPayload
|
|
124
|
+
},
|
|
125
|
+
failedResponseHandler: (0, import_provider_utils.createJsonErrorResponseHandler)({
|
|
126
|
+
errorSchema: runpodImageErrorSchema,
|
|
127
|
+
errorToMessage: (data) => data.error ?? "Unknown error"
|
|
128
|
+
}),
|
|
129
|
+
successfulResponseHandler: (0, import_provider_utils.createJsonResponseHandler)(
|
|
130
|
+
runpodImageResponseSchema
|
|
131
|
+
),
|
|
132
|
+
abortSignal,
|
|
133
|
+
fetch: this.config.fetch
|
|
134
|
+
});
|
|
135
|
+
const typedResponse = response;
|
|
136
|
+
if (typedResponse.status === "COMPLETED" && (typedResponse.output?.result || typedResponse.output?.image_url)) {
|
|
137
|
+
const imageUrl = typedResponse.output.result || typedResponse.output.image_url;
|
|
138
|
+
const imageData = await this.downloadImage(imageUrl, abortSignal);
|
|
139
|
+
return {
|
|
140
|
+
images: [imageData],
|
|
141
|
+
warnings,
|
|
142
|
+
response: {
|
|
143
|
+
timestamp: currentDate,
|
|
144
|
+
modelId: this.modelId,
|
|
145
|
+
headers: responseHeaders
|
|
146
|
+
},
|
|
147
|
+
providerMetadata: {
|
|
148
|
+
runpod: {
|
|
149
|
+
images: [
|
|
150
|
+
{
|
|
151
|
+
url: imageUrl,
|
|
152
|
+
cost: typedResponse.output?.cost
|
|
153
|
+
}
|
|
154
|
+
]
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
} else if (typedResponse.status === "IN_QUEUE" || typedResponse.status === "IN_PROGRESS") {
|
|
159
|
+
const pollOptions = {
|
|
160
|
+
maxAttempts: providerOptions.runpod?.maxPollAttempts,
|
|
161
|
+
pollIntervalMillis: providerOptions.runpod?.pollIntervalMillis
|
|
162
|
+
};
|
|
163
|
+
const imageUrl = await this.pollForCompletion(
|
|
164
|
+
typedResponse.id,
|
|
165
|
+
abortSignal,
|
|
166
|
+
pollOptions
|
|
167
|
+
);
|
|
168
|
+
const imageData = await this.downloadImage(imageUrl, abortSignal);
|
|
169
|
+
return {
|
|
170
|
+
images: [imageData],
|
|
171
|
+
warnings,
|
|
172
|
+
response: {
|
|
173
|
+
timestamp: currentDate,
|
|
174
|
+
modelId: this.modelId,
|
|
175
|
+
headers: responseHeaders
|
|
176
|
+
},
|
|
177
|
+
providerMetadata: {
|
|
178
|
+
runpod: {
|
|
179
|
+
images: [
|
|
180
|
+
{
|
|
181
|
+
url: imageUrl,
|
|
182
|
+
jobId: typedResponse.id
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
};
|
|
188
|
+
} else {
|
|
189
|
+
throw new Error(`Unexpected response status: ${typedResponse.status}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
async downloadImage(imageUrl, abortSignal) {
|
|
193
|
+
const { value: imageData } = await (0, import_provider_utils.getFromApi)({
|
|
194
|
+
url: imageUrl,
|
|
195
|
+
successfulResponseHandler: (0, import_provider_utils.createBinaryResponseHandler)(),
|
|
196
|
+
failedResponseHandler: (0, import_provider_utils.createJsonErrorResponseHandler)({
|
|
197
|
+
errorSchema: runpodImageErrorSchema,
|
|
198
|
+
errorToMessage: (data) => data.error ?? "Failed to download image"
|
|
199
|
+
}),
|
|
200
|
+
abortSignal,
|
|
201
|
+
fetch: this.config.fetch
|
|
202
|
+
});
|
|
203
|
+
return imageData;
|
|
204
|
+
}
|
|
205
|
+
async pollForCompletion(jobId, abortSignal, pollOptions) {
|
|
206
|
+
const maxAttempts = pollOptions?.maxAttempts ?? 60;
|
|
207
|
+
const pollInterval = pollOptions?.pollIntervalMillis ?? 5e3;
|
|
208
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
209
|
+
if (abortSignal?.aborted) {
|
|
210
|
+
throw new Error("Image generation was aborted");
|
|
211
|
+
}
|
|
212
|
+
const { value: statusResponse } = await (0, import_provider_utils.getFromApi)({
|
|
213
|
+
url: `${this.config.baseURL}/status/${jobId}`,
|
|
214
|
+
headers: this.config.headers(),
|
|
215
|
+
successfulResponseHandler: (0, import_provider_utils.createJsonResponseHandler)(
|
|
216
|
+
runpodImageStatusSchema
|
|
217
|
+
),
|
|
218
|
+
failedResponseHandler: (0, import_provider_utils.createJsonErrorResponseHandler)({
|
|
219
|
+
errorSchema: runpodImageErrorSchema,
|
|
220
|
+
errorToMessage: (data) => data.error ?? "Failed to check job status"
|
|
221
|
+
}),
|
|
222
|
+
abortSignal,
|
|
223
|
+
fetch: this.config.fetch
|
|
224
|
+
});
|
|
225
|
+
const typedStatusResponse = statusResponse;
|
|
226
|
+
if (typedStatusResponse.status === "COMPLETED" && (typedStatusResponse.output?.result || typedStatusResponse.output?.image_url)) {
|
|
227
|
+
return typedStatusResponse.output.result || typedStatusResponse.output.image_url;
|
|
228
|
+
}
|
|
229
|
+
if (typedStatusResponse.status === "FAILED") {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`Image generation failed: ${typedStatusResponse.error || "Unknown error"}`
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
235
|
+
}
|
|
236
|
+
throw new Error(
|
|
237
|
+
`Image generation timed out after ${maxAttempts} attempts (${maxAttempts * pollInterval / 1e3}s)`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
buildInputPayload(prompt, runpodSize, seed, runpodOptions) {
|
|
241
|
+
const isFluxModel = this.modelId.includes("flux") || this.modelId.includes("black-forest-labs");
|
|
242
|
+
if (isFluxModel) {
|
|
243
|
+
const isKontext = this.modelId.includes("kontext");
|
|
244
|
+
if (isKontext) {
|
|
245
|
+
return {
|
|
246
|
+
prompt,
|
|
247
|
+
negative_prompt: runpodOptions?.negative_prompt ?? "",
|
|
248
|
+
seed: seed ?? -1,
|
|
249
|
+
num_inference_steps: 28,
|
|
250
|
+
guidance: 2,
|
|
251
|
+
size: runpodSize,
|
|
252
|
+
output_format: "png",
|
|
253
|
+
enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,
|
|
254
|
+
...runpodOptions
|
|
255
|
+
// This will include the 'image' parameter if provided
|
|
256
|
+
};
|
|
257
|
+
} else {
|
|
258
|
+
const [width, height] = runpodSize.split("*").map(Number);
|
|
259
|
+
return {
|
|
260
|
+
prompt,
|
|
261
|
+
negative_prompt: runpodOptions?.negative_prompt ?? "",
|
|
262
|
+
seed: seed ?? -1,
|
|
263
|
+
num_inference_steps: this.modelId.includes("schnell") ? 4 : 28,
|
|
264
|
+
guidance: this.modelId.includes("schnell") ? 7 : 2,
|
|
265
|
+
width,
|
|
266
|
+
height,
|
|
267
|
+
image_format: "png",
|
|
268
|
+
...runpodOptions
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
prompt,
|
|
274
|
+
negative_prompt: runpodOptions?.negative_prompt ?? "",
|
|
275
|
+
size: runpodSize,
|
|
276
|
+
seed: seed ?? -1,
|
|
277
|
+
enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,
|
|
278
|
+
...runpodOptions
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
};
|
|
282
|
+
var runpodImageResponseSchema = import_zod.z.object({
|
|
283
|
+
id: import_zod.z.string(),
|
|
284
|
+
status: import_zod.z.enum(["COMPLETED", "IN_QUEUE", "IN_PROGRESS", "FAILED"]),
|
|
285
|
+
delayTime: import_zod.z.number().optional(),
|
|
286
|
+
executionTime: import_zod.z.number().optional(),
|
|
287
|
+
output: import_zod.z.object({
|
|
288
|
+
cost: import_zod.z.number().optional(),
|
|
289
|
+
result: import_zod.z.string().optional(),
|
|
290
|
+
// URL to the generated image (Qwen format)
|
|
291
|
+
image_url: import_zod.z.string().optional()
|
|
292
|
+
// URL to the generated image (Flux format)
|
|
293
|
+
}).optional()
|
|
294
|
+
// Optional for IN_QUEUE/IN_PROGRESS responses
|
|
295
|
+
});
|
|
296
|
+
var runpodImageStatusSchema = import_zod.z.object({
|
|
297
|
+
id: import_zod.z.string(),
|
|
298
|
+
status: import_zod.z.enum(["COMPLETED", "IN_QUEUE", "IN_PROGRESS", "FAILED"]),
|
|
299
|
+
output: import_zod.z.object({
|
|
300
|
+
cost: import_zod.z.number().optional(),
|
|
301
|
+
result: import_zod.z.string().optional(),
|
|
302
|
+
image_url: import_zod.z.string().optional()
|
|
303
|
+
}).optional(),
|
|
304
|
+
error: import_zod.z.string().optional()
|
|
305
|
+
// Error message if FAILED
|
|
306
|
+
});
|
|
307
|
+
var runpodImageErrorSchema = import_zod.z.object({
|
|
308
|
+
error: import_zod.z.string(),
|
|
309
|
+
message: import_zod.z.string().optional()
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
// src/runpod-provider.ts
|
|
31
313
|
var MODEL_ID_TO_ENDPOINT_URL = {
|
|
32
314
|
"deep-cogito/deep-cogito-v2-llama-70b": "https://api.runpod.ai/v2/deep-cogito-v2-llama-70b/openai/v1",
|
|
33
315
|
"qwen/qwen3-32b-awq": "https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1"
|
|
34
316
|
};
|
|
317
|
+
var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
|
|
318
|
+
"qwen/qwen-image": "https://api.runpod.ai/v2/qwen-image-t2i",
|
|
319
|
+
"bytedance/seedream-3.0": "https://api.runpod.ai/v2/seedream-3-0-t2i",
|
|
320
|
+
"black-forest-labs/flux-1-kontext-dev": "https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev",
|
|
321
|
+
"black-forest-labs/flux-1-schnell": "https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell",
|
|
322
|
+
"black-forest-labs/flux-1-dev": "https://api.runpod.ai/v2/black-forest-labs-flux-1-dev"
|
|
323
|
+
};
|
|
35
324
|
var MODEL_ID_TO_OPENAI_NAME = {
|
|
36
325
|
"deep-cogito/deep-cogito-v2-llama-70b": "deepcogito/cogito-v2-preview-llama-70B",
|
|
37
326
|
"qwen/qwen3-32b-awq": "Qwen/Qwen3-32B-AWQ"
|
|
38
327
|
};
|
|
39
328
|
function createRunPod(options = {}) {
|
|
40
329
|
const getHeaders = () => ({
|
|
41
|
-
Authorization: `Bearer ${(0,
|
|
330
|
+
Authorization: `Bearer ${(0, import_provider_utils2.loadApiKey)({
|
|
42
331
|
apiKey: options.apiKey,
|
|
43
332
|
environmentVariableName: "RUNPOD_API_KEY",
|
|
44
333
|
description: "RunPod"
|
|
@@ -56,7 +345,7 @@ function createRunPod(options = {}) {
|
|
|
56
345
|
}
|
|
57
346
|
return {
|
|
58
347
|
provider: `runpod.${modelType}`,
|
|
59
|
-
url: ({ path }) => `${(0,
|
|
348
|
+
url: ({ path }) => `${(0, import_provider_utils2.withoutTrailingSlash)(baseURL)}${path}`,
|
|
60
349
|
headers: getHeaders,
|
|
61
350
|
fetch: options.fetch
|
|
62
351
|
};
|
|
@@ -75,10 +364,27 @@ function createRunPod(options = {}) {
|
|
|
75
364
|
getModelConfig(modelId, "completion")
|
|
76
365
|
);
|
|
77
366
|
};
|
|
367
|
+
const createImageModel = (modelId) => {
|
|
368
|
+
const baseURL = IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId];
|
|
369
|
+
if (!baseURL) {
|
|
370
|
+
throw new Error(
|
|
371
|
+
`Unsupported Runpod image model: ${modelId}. Supported models: ${Object.keys(
|
|
372
|
+
IMAGE_MODEL_ID_TO_ENDPOINT_URL
|
|
373
|
+
).join(", ")}`
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
return new RunpodImageModel(modelId, {
|
|
377
|
+
provider: "runpod.image",
|
|
378
|
+
baseURL,
|
|
379
|
+
headers: getHeaders,
|
|
380
|
+
fetch: options.fetch
|
|
381
|
+
});
|
|
382
|
+
};
|
|
78
383
|
const provider = (modelId) => createChatModel(modelId);
|
|
79
384
|
provider.completionModel = createCompletionModel;
|
|
80
385
|
provider.languageModel = createChatModel;
|
|
81
386
|
provider.chatModel = createChatModel;
|
|
387
|
+
provider.imageModel = createImageModel;
|
|
82
388
|
return provider;
|
|
83
389
|
}
|
|
84
390
|
var runpod = createRunPod();
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/runpod-provider.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 { OpenAICompatibleErrorData as RunPodErrorData } from '@ai-sdk/openai-compatible';\n","import { LanguageModelV2 } 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 { RunPodChatModelId } from './runpod-chat-options';\nimport { RunPodCompletionModelId } from './runpod-completion-options';\n\nexport interface RunPodProviderSettings {\n /**\nRunPod API key.\n*/\n apiKey?: 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: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: RunPodCompletionModelId): LanguageModelV2;\n}\n\n// Mapping of RunPod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'deep-cogito/deep-cogito-v2-llama-70b':\n 'https://api.runpod.ai/v2/deep-cogito-v2-llama-70b/openai/v1',\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n};\n\n// Mapping of RunPod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'deep-cogito/deep-cogito-v2-llama-70b':\n 'deepcogito/cogito-v2-preview-llama-70B',\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\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 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 const baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId];\n if (!baseURL) {\n throw new Error(\n `Unsupported RunPod model: ${modelId}. Supported models: ${Object.keys(\n MODEL_ID_TO_ENDPOINT_URL\n ).join(', ')}`\n );\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: options.fetch,\n };\n };\n\n const createChatModel = (modelId: RunPodChatModelId) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(\n openaiModelName,\n getModelConfig(modelId, 'chat')\n );\n };\n\n const createCompletionModel = (modelId: RunPodCompletionModelId) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(\n openaiModelName,\n getModelConfig(modelId, 'completion')\n );\n };\n\n const provider = (modelId: RunPodChatModelId) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n\n return provider;\n}\n\nexport const runpod = createRunPod();\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,+BAGO;AACP,4BAIO;AA2CP,IAAM,2BAAmD;AAAA,EACvD,wCACE;AAAA,EACF,sBAAsB;AACxB;AAGA,IAAM,0BAAkD;AAAA,EACtD,wCACE;AAAA,EACF,sBAAsB;AACxB;AAEO,SAAS,aACd,UAAkC,CAAC,GACnB;AAChB,QAAM,aAAa,OAAO;AAAA,IACxB,eAAe,cAAU,kCAAW;AAAA,MAClC,QAAQ,QAAQ;AAAA,MAChB,yBAAyB;AAAA,MACzB,aAAa;AAAA,IACf,CAAC,CAAC;AAAA,IACF,GAAG,QAAQ;AAAA,EACb;AASA,QAAM,iBAAiB,CACrB,SACA,cACsB;AACtB,UAAM,UAAU,yBAAyB,OAAO;AAChD,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,6BAA6B,OAAO,uBAAuB,OAAO;AAAA,UAChE;AAAA,QACF,EAAE,KAAK,IAAI,CAAC;AAAA,MACd;AAAA,IACF;AAEA,WAAO;AAAA,MACL,UAAU,UAAU,SAAS;AAAA,MAC7B,KAAK,CAAC,EAAE,KAAK,MAAM,OAAG,4CAAqB,OAAO,CAAC,GAAG,IAAI;AAAA,MAC1D,SAAS;AAAA,MACT,OAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,kBAAkB,CAAC,YAA+B;AACtD,UAAM,kBAAkB,wBAAwB,OAAO,KAAK;AAC5D,WAAO,IAAI;AAAA,MACT;AAAA,MACA,eAAe,SAAS,MAAM;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,wBAAwB,CAAC,YAAqC;AAClE,UAAM,kBAAkB,wBAAwB,OAAO,KAAK;AAC5D,WAAO,IAAI;AAAA,MACT;AAAA,MACA,eAAe,SAAS,YAAY;AAAA,IACtC;AAAA,EACF;AAEA,QAAM,WAAW,CAAC,YAA+B,gBAAgB,OAAO;AAExE,WAAS,kBAAkB;AAC3B,WAAS,gBAAgB;AACzB,WAAS,YAAY;AAErB,SAAO;AACT;AAEO,IAAM,SAAS,aAAa;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/runpod-provider.ts","../src/runpod-image-model.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';\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 { RunPodChatModelId } from './runpod-chat-options';\nimport { RunPodCompletionModelId } from './runpod-completion-options';\nimport { RunpodImageModelId } from './runpod-image-options';\nimport { RunpodImageModel } from './runpod-image-model';\n\nexport interface RunPodProviderSettings {\n /**\nRunPod API key.\n*/\n apiKey?: 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: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: RunPodCompletionModelId): LanguageModelV2;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: RunpodImageModelId): ImageModelV2;\n}\n\n// Mapping of RunPod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'deep-cogito/deep-cogito-v2-llama-70b':\n 'https://api.runpod.ai/v2/deep-cogito-v2-llama-70b/openai/v1',\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/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 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\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};\n\n// Mapping of RunPod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'deep-cogito/deep-cogito-v2-llama-70b':\n 'deepcogito/cogito-v2-preview-llama-70B',\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\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 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 const baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId];\n if (!baseURL) {\n throw new Error(\n `Unsupported RunPod model: ${modelId}. Supported models: ${Object.keys(\n MODEL_ID_TO_ENDPOINT_URL\n ).join(', ')}`\n );\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: options.fetch,\n };\n };\n\n const createChatModel = (modelId: RunPodChatModelId) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(\n openaiModelName,\n getModelConfig(modelId, 'chat')\n );\n };\n\n const createCompletionModel = (modelId: RunPodCompletionModelId) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(\n openaiModelName,\n getModelConfig(modelId, 'completion')\n );\n };\n\n const createImageModel = (modelId: RunpodImageModelId) => {\n const baseURL = IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId];\n if (!baseURL) {\n throw new Error(\n `Unsupported Runpod image model: ${modelId}. Supported models: ${Object.keys(\n IMAGE_MODEL_ID_TO_ENDPOINT_URL\n ).join(', ')}`\n );\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: RunPodChatModelId) => 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';\r\nimport {\r\n combineHeaders,\r\n createJsonResponseHandler,\r\n createJsonErrorResponseHandler,\r\n createBinaryResponseHandler,\r\n FetchFunction,\r\n postJsonToApi,\r\n getFromApi,\r\n} from '@ai-sdk/provider-utils';\r\nimport { InvalidArgumentError } from '@ai-sdk/provider';\r\nimport { z } from 'zod';\r\nimport { RunpodImageModelId } from './runpod-image-options';\r\n\r\ninterface RunpodImageModelConfig {\r\n provider: string;\r\n baseURL: string;\r\n headers: () => Record<string, string>;\r\n fetch?: FetchFunction;\r\n _internal?: {\r\n currentDate?: () => Date;\r\n };\r\n}\r\n\r\n// Runpod supported aspect ratios (only validated working sizes)\r\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\r\n '1:1': '1328*1328', // ✅ Native support\r\n '4:3': '1472*1140', // ✅ Native support\r\n '3:4': '1140*1472', // ✅ Native support\r\n};\r\n\r\n// Runpod supported sizes (validated working sizes)\r\nconst SUPPORTED_SIZES = new Set([\r\n // Native aspect ratio sizes\r\n '1328*1328', // 1:1\r\n '1472*1140', // 4:3\r\n '1140*1472', // 3:4\r\n // Additional validated sizes\r\n '512*512',\r\n '768*768',\r\n '1024*1024',\r\n '512*768',\r\n '768*512',\r\n '1024*768',\r\n '768*1024',\r\n]);\r\n\r\nexport class RunpodImageModel implements ImageModelV2 {\r\n readonly specificationVersion = 'v2';\r\n readonly maxImagesPerCall = 1;\r\n\r\n get provider(): string {\r\n return this.config.provider;\r\n }\r\n\r\n constructor(\r\n readonly modelId: RunpodImageModelId,\r\n private config: RunpodImageModelConfig\r\n ) {}\r\n\r\n async doGenerate({\r\n prompt,\r\n n = 1,\r\n size,\r\n aspectRatio,\r\n seed,\r\n providerOptions,\r\n headers,\r\n abortSignal,\r\n }: Parameters<ImageModelV2['doGenerate']>[0]): Promise<\r\n Awaited<ReturnType<ImageModelV2['doGenerate']>>\r\n > {\r\n const warnings: Array<ImageModelV2CallWarning> = [];\r\n\r\n // Determine the size to use\r\n let runpodSize: string;\r\n\r\n if (size) {\r\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\r\n const runpodSizeCandidate = size.replace('x', '*');\r\n\r\n // Validate size is supported\r\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\r\n throw new InvalidArgumentError({\r\n argument: 'size',\r\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\r\n SUPPORTED_SIZES\r\n )\r\n .map((s) => s.replace('*', 'x'))\r\n .join(', ')}`,\r\n });\r\n }\r\n\r\n runpodSize = runpodSizeCandidate;\r\n } else if (aspectRatio) {\r\n // Validate aspect ratio is supported\r\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\r\n throw new InvalidArgumentError({\r\n argument: 'aspectRatio',\r\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\r\n });\r\n }\r\n\r\n // Use supported aspect ratio mapping\r\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\r\n } else {\r\n // Default to square format\r\n runpodSize = '1328*1328';\r\n }\r\n\r\n // Handle multiple images warning\r\n if (n > 1) {\r\n warnings.push({\r\n type: 'unsupported-setting',\r\n setting: 'n',\r\n details:\r\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\r\n });\r\n }\r\n\r\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\r\n\r\n // Runpod uses a different request format - /runsync endpoint with input wrapper\r\n const inputPayload = this.buildInputPayload(\r\n prompt,\r\n runpodSize,\r\n seed,\r\n providerOptions.runpod\r\n );\r\n\r\n const { value: response, responseHeaders } = await postJsonToApi({\r\n url: `${this.config.baseURL}/runsync`,\r\n headers: combineHeaders(this.config.headers(), headers),\r\n body: {\r\n input: inputPayload,\r\n },\r\n failedResponseHandler: createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: any) => data.error ?? 'Unknown error',\r\n }),\r\n successfulResponseHandler: createJsonResponseHandler(\r\n runpodImageResponseSchema as any\r\n ),\r\n abortSignal,\r\n fetch: this.config.fetch,\r\n });\r\n\r\n // Handle both sync and async responses from Runpod\r\n const typedResponse = response as any;\r\n if (\r\n typedResponse.status === 'COMPLETED' &&\r\n (typedResponse.output?.result || typedResponse.output?.image_url)\r\n ) {\r\n // Sync response - image is ready\r\n // Different models use different response formats: result vs image_url\r\n const imageUrl =\r\n typedResponse.output.result || typedResponse.output.image_url;\r\n const imageData = await this.downloadImage(imageUrl, abortSignal);\r\n\r\n return {\r\n images: [imageData],\r\n warnings,\r\n response: {\r\n timestamp: currentDate,\r\n modelId: this.modelId,\r\n headers: responseHeaders,\r\n },\r\n providerMetadata: {\r\n runpod: {\r\n images: [\r\n {\r\n url: imageUrl,\r\n cost: typedResponse.output?.cost,\r\n },\r\n ],\r\n },\r\n },\r\n };\r\n } else if (\r\n typedResponse.status === 'IN_QUEUE' ||\r\n typedResponse.status === 'IN_PROGRESS'\r\n ) {\r\n // Async response - need to poll for completion\r\n const pollOptions = {\r\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\r\n pollIntervalMillis: providerOptions.runpod\r\n ?.pollIntervalMillis as number,\r\n };\r\n const imageUrl = await this.pollForCompletion(\r\n typedResponse.id,\r\n abortSignal,\r\n pollOptions\r\n );\r\n const imageData = await this.downloadImage(imageUrl, abortSignal);\r\n\r\n return {\r\n images: [imageData],\r\n warnings,\r\n response: {\r\n timestamp: currentDate,\r\n modelId: this.modelId,\r\n headers: responseHeaders,\r\n },\r\n providerMetadata: {\r\n runpod: {\r\n images: [\r\n {\r\n url: imageUrl,\r\n jobId: typedResponse.id,\r\n },\r\n ],\r\n },\r\n },\r\n };\r\n } else {\r\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\r\n }\r\n }\r\n\r\n private async downloadImage(\r\n imageUrl: string,\r\n abortSignal?: AbortSignal\r\n ): Promise<Uint8Array> {\r\n const { value: imageData } = await getFromApi({\r\n url: imageUrl,\r\n successfulResponseHandler: createBinaryResponseHandler(),\r\n failedResponseHandler: createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: any) => data.error ?? 'Failed to download image',\r\n }),\r\n abortSignal,\r\n fetch: this.config.fetch,\r\n });\r\n return imageData;\r\n }\r\n\r\n private async pollForCompletion(\r\n jobId: string,\r\n abortSignal?: AbortSignal,\r\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number }\r\n ): Promise<string> {\r\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\r\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\r\n\r\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\r\n if (abortSignal?.aborted) {\r\n throw new Error('Image generation was aborted');\r\n }\r\n\r\n const { value: statusResponse } = await getFromApi({\r\n url: `${this.config.baseURL}/status/${jobId}`,\r\n headers: this.config.headers(),\r\n successfulResponseHandler: createJsonResponseHandler(\r\n runpodImageStatusSchema as any\r\n ),\r\n failedResponseHandler: createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: any) =>\r\n data.error ?? 'Failed to check job status',\r\n }),\r\n abortSignal,\r\n fetch: this.config.fetch,\r\n });\r\n\r\n const typedStatusResponse = statusResponse as any;\r\n if (\r\n typedStatusResponse.status === 'COMPLETED' &&\r\n (typedStatusResponse.output?.result ||\r\n typedStatusResponse.output?.image_url)\r\n ) {\r\n return (\r\n typedStatusResponse.output.result ||\r\n typedStatusResponse.output.image_url\r\n );\r\n }\r\n\r\n if (typedStatusResponse.status === 'FAILED') {\r\n throw new Error(\r\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\r\n );\r\n }\r\n\r\n // Wait before next poll\r\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\r\n }\r\n\r\n throw new Error(\r\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\r\n );\r\n }\r\n\r\n private buildInputPayload(\r\n prompt: string,\r\n runpodSize: string,\r\n seed?: number,\r\n runpodOptions?: Record<string, unknown>\r\n ): Record<string, unknown> {\r\n // Check if this is a Flux model that uses different parameters\r\n const isFluxModel =\r\n this.modelId.includes('flux') ||\r\n this.modelId.includes('black-forest-labs');\r\n\r\n if (isFluxModel) {\r\n // Check if this is Flux Kontext (uses different parameters)\r\n const isKontext = this.modelId.includes('kontext');\r\n\r\n if (isKontext) {\r\n // Flux Kontext uses size format and has image input\r\n return {\r\n prompt,\r\n negative_prompt: runpodOptions?.negative_prompt ?? '',\r\n seed: seed ?? -1,\r\n num_inference_steps: 28,\r\n guidance: 2,\r\n size: runpodSize,\r\n output_format: 'png',\r\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\r\n ...runpodOptions, // This will include the 'image' parameter if provided\r\n };\r\n } else {\r\n // Regular Flux models use width/height\r\n const [width, height] = runpodSize.split('*').map(Number);\r\n\r\n return {\r\n prompt,\r\n negative_prompt: runpodOptions?.negative_prompt ?? '',\r\n seed: seed ?? -1,\r\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\r\n guidance: this.modelId.includes('schnell') ? 7 : 2,\r\n width,\r\n height,\r\n image_format: 'png',\r\n ...runpodOptions,\r\n };\r\n }\r\n }\r\n\r\n // Default format for Qwen and other models\r\n return {\r\n prompt,\r\n negative_prompt: runpodOptions?.negative_prompt ?? '',\r\n size: runpodSize,\r\n seed: seed ?? -1,\r\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\r\n ...runpodOptions,\r\n };\r\n }\r\n}\r\n\r\n// Runpod image API response schema (handles both sync and async responses)\r\nconst runpodImageResponseSchema = z.object({\r\n id: z.string(),\r\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\r\n delayTime: z.number().optional(),\r\n executionTime: z.number().optional(),\r\n output: z\r\n .object({\r\n cost: z.number().optional(),\r\n result: z.string().optional(), // URL to the generated image (Qwen format)\r\n image_url: z.string().optional(), // URL to the generated image (Flux format)\r\n })\r\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\r\n});\r\n\r\n// Schema for polling status endpoint\r\nconst runpodImageStatusSchema = z.object({\r\n id: z.string(),\r\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\r\n output: z\r\n .object({\r\n cost: z.number().optional(),\r\n result: z.string().optional(),\r\n image_url: z.string().optional(),\r\n })\r\n .optional(),\r\n error: z.string().optional(), // Error message if FAILED\r\n});\r\n\r\n// Runpod image API error schema\r\nconst runpodImageErrorSchema = z.object({\r\n error: z.string(),\r\n message: z.string().optional(),\r\n});\r\n"],"mappings":"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","names":["import_provider_utils"]}
|
package/dist/index.mjs
CHANGED
|
@@ -7,10 +7,306 @@ import {
|
|
|
7
7
|
loadApiKey,
|
|
8
8
|
withoutTrailingSlash
|
|
9
9
|
} from "@ai-sdk/provider-utils";
|
|
10
|
+
|
|
11
|
+
// src/runpod-image-model.ts
|
|
12
|
+
import {
|
|
13
|
+
combineHeaders,
|
|
14
|
+
createJsonResponseHandler,
|
|
15
|
+
createJsonErrorResponseHandler,
|
|
16
|
+
createBinaryResponseHandler,
|
|
17
|
+
postJsonToApi,
|
|
18
|
+
getFromApi
|
|
19
|
+
} from "@ai-sdk/provider-utils";
|
|
20
|
+
import { InvalidArgumentError } from "@ai-sdk/provider";
|
|
21
|
+
import { z } from "zod";
|
|
22
|
+
var SUPPORTED_ASPECT_RATIOS = {
|
|
23
|
+
"1:1": "1328*1328",
|
|
24
|
+
// ✅ Native support
|
|
25
|
+
"4:3": "1472*1140",
|
|
26
|
+
// ✅ Native support
|
|
27
|
+
"3:4": "1140*1472"
|
|
28
|
+
// ✅ Native support
|
|
29
|
+
};
|
|
30
|
+
var SUPPORTED_SIZES = /* @__PURE__ */ new Set([
|
|
31
|
+
// Native aspect ratio sizes
|
|
32
|
+
"1328*1328",
|
|
33
|
+
// 1:1
|
|
34
|
+
"1472*1140",
|
|
35
|
+
// 4:3
|
|
36
|
+
"1140*1472",
|
|
37
|
+
// 3:4
|
|
38
|
+
// Additional validated sizes
|
|
39
|
+
"512*512",
|
|
40
|
+
"768*768",
|
|
41
|
+
"1024*1024",
|
|
42
|
+
"512*768",
|
|
43
|
+
"768*512",
|
|
44
|
+
"1024*768",
|
|
45
|
+
"768*1024"
|
|
46
|
+
]);
|
|
47
|
+
var RunpodImageModel = class {
|
|
48
|
+
constructor(modelId, config) {
|
|
49
|
+
this.modelId = modelId;
|
|
50
|
+
this.config = config;
|
|
51
|
+
this.specificationVersion = "v2";
|
|
52
|
+
this.maxImagesPerCall = 1;
|
|
53
|
+
}
|
|
54
|
+
get provider() {
|
|
55
|
+
return this.config.provider;
|
|
56
|
+
}
|
|
57
|
+
async doGenerate({
|
|
58
|
+
prompt,
|
|
59
|
+
n = 1,
|
|
60
|
+
size,
|
|
61
|
+
aspectRatio,
|
|
62
|
+
seed,
|
|
63
|
+
providerOptions,
|
|
64
|
+
headers,
|
|
65
|
+
abortSignal
|
|
66
|
+
}) {
|
|
67
|
+
const warnings = [];
|
|
68
|
+
let runpodSize;
|
|
69
|
+
if (size) {
|
|
70
|
+
const runpodSizeCandidate = size.replace("x", "*");
|
|
71
|
+
if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {
|
|
72
|
+
throw new InvalidArgumentError({
|
|
73
|
+
argument: "size",
|
|
74
|
+
message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(
|
|
75
|
+
SUPPORTED_SIZES
|
|
76
|
+
).map((s) => s.replace("*", "x")).join(", ")}`
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
runpodSize = runpodSizeCandidate;
|
|
80
|
+
} else if (aspectRatio) {
|
|
81
|
+
if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {
|
|
82
|
+
throw new InvalidArgumentError({
|
|
83
|
+
argument: "aspectRatio",
|
|
84
|
+
message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(", ")}`
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];
|
|
88
|
+
} else {
|
|
89
|
+
runpodSize = "1328*1328";
|
|
90
|
+
}
|
|
91
|
+
if (n > 1) {
|
|
92
|
+
warnings.push({
|
|
93
|
+
type: "unsupported-setting",
|
|
94
|
+
setting: "n",
|
|
95
|
+
details: "Runpod image models only support generating 1 image at a time. Using n=1."
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
|
|
99
|
+
const inputPayload = this.buildInputPayload(
|
|
100
|
+
prompt,
|
|
101
|
+
runpodSize,
|
|
102
|
+
seed,
|
|
103
|
+
providerOptions.runpod
|
|
104
|
+
);
|
|
105
|
+
const { value: response, responseHeaders } = await postJsonToApi({
|
|
106
|
+
url: `${this.config.baseURL}/runsync`,
|
|
107
|
+
headers: combineHeaders(this.config.headers(), headers),
|
|
108
|
+
body: {
|
|
109
|
+
input: inputPayload
|
|
110
|
+
},
|
|
111
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
112
|
+
errorSchema: runpodImageErrorSchema,
|
|
113
|
+
errorToMessage: (data) => data.error ?? "Unknown error"
|
|
114
|
+
}),
|
|
115
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
116
|
+
runpodImageResponseSchema
|
|
117
|
+
),
|
|
118
|
+
abortSignal,
|
|
119
|
+
fetch: this.config.fetch
|
|
120
|
+
});
|
|
121
|
+
const typedResponse = response;
|
|
122
|
+
if (typedResponse.status === "COMPLETED" && (typedResponse.output?.result || typedResponse.output?.image_url)) {
|
|
123
|
+
const imageUrl = typedResponse.output.result || typedResponse.output.image_url;
|
|
124
|
+
const imageData = await this.downloadImage(imageUrl, abortSignal);
|
|
125
|
+
return {
|
|
126
|
+
images: [imageData],
|
|
127
|
+
warnings,
|
|
128
|
+
response: {
|
|
129
|
+
timestamp: currentDate,
|
|
130
|
+
modelId: this.modelId,
|
|
131
|
+
headers: responseHeaders
|
|
132
|
+
},
|
|
133
|
+
providerMetadata: {
|
|
134
|
+
runpod: {
|
|
135
|
+
images: [
|
|
136
|
+
{
|
|
137
|
+
url: imageUrl,
|
|
138
|
+
cost: typedResponse.output?.cost
|
|
139
|
+
}
|
|
140
|
+
]
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
} else if (typedResponse.status === "IN_QUEUE" || typedResponse.status === "IN_PROGRESS") {
|
|
145
|
+
const pollOptions = {
|
|
146
|
+
maxAttempts: providerOptions.runpod?.maxPollAttempts,
|
|
147
|
+
pollIntervalMillis: providerOptions.runpod?.pollIntervalMillis
|
|
148
|
+
};
|
|
149
|
+
const imageUrl = await this.pollForCompletion(
|
|
150
|
+
typedResponse.id,
|
|
151
|
+
abortSignal,
|
|
152
|
+
pollOptions
|
|
153
|
+
);
|
|
154
|
+
const imageData = await this.downloadImage(imageUrl, abortSignal);
|
|
155
|
+
return {
|
|
156
|
+
images: [imageData],
|
|
157
|
+
warnings,
|
|
158
|
+
response: {
|
|
159
|
+
timestamp: currentDate,
|
|
160
|
+
modelId: this.modelId,
|
|
161
|
+
headers: responseHeaders
|
|
162
|
+
},
|
|
163
|
+
providerMetadata: {
|
|
164
|
+
runpod: {
|
|
165
|
+
images: [
|
|
166
|
+
{
|
|
167
|
+
url: imageUrl,
|
|
168
|
+
jobId: typedResponse.id
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
};
|
|
174
|
+
} else {
|
|
175
|
+
throw new Error(`Unexpected response status: ${typedResponse.status}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async downloadImage(imageUrl, abortSignal) {
|
|
179
|
+
const { value: imageData } = await getFromApi({
|
|
180
|
+
url: imageUrl,
|
|
181
|
+
successfulResponseHandler: createBinaryResponseHandler(),
|
|
182
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
183
|
+
errorSchema: runpodImageErrorSchema,
|
|
184
|
+
errorToMessage: (data) => data.error ?? "Failed to download image"
|
|
185
|
+
}),
|
|
186
|
+
abortSignal,
|
|
187
|
+
fetch: this.config.fetch
|
|
188
|
+
});
|
|
189
|
+
return imageData;
|
|
190
|
+
}
|
|
191
|
+
async pollForCompletion(jobId, abortSignal, pollOptions) {
|
|
192
|
+
const maxAttempts = pollOptions?.maxAttempts ?? 60;
|
|
193
|
+
const pollInterval = pollOptions?.pollIntervalMillis ?? 5e3;
|
|
194
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
195
|
+
if (abortSignal?.aborted) {
|
|
196
|
+
throw new Error("Image generation was aborted");
|
|
197
|
+
}
|
|
198
|
+
const { value: statusResponse } = await getFromApi({
|
|
199
|
+
url: `${this.config.baseURL}/status/${jobId}`,
|
|
200
|
+
headers: this.config.headers(),
|
|
201
|
+
successfulResponseHandler: createJsonResponseHandler(
|
|
202
|
+
runpodImageStatusSchema
|
|
203
|
+
),
|
|
204
|
+
failedResponseHandler: createJsonErrorResponseHandler({
|
|
205
|
+
errorSchema: runpodImageErrorSchema,
|
|
206
|
+
errorToMessage: (data) => data.error ?? "Failed to check job status"
|
|
207
|
+
}),
|
|
208
|
+
abortSignal,
|
|
209
|
+
fetch: this.config.fetch
|
|
210
|
+
});
|
|
211
|
+
const typedStatusResponse = statusResponse;
|
|
212
|
+
if (typedStatusResponse.status === "COMPLETED" && (typedStatusResponse.output?.result || typedStatusResponse.output?.image_url)) {
|
|
213
|
+
return typedStatusResponse.output.result || typedStatusResponse.output.image_url;
|
|
214
|
+
}
|
|
215
|
+
if (typedStatusResponse.status === "FAILED") {
|
|
216
|
+
throw new Error(
|
|
217
|
+
`Image generation failed: ${typedStatusResponse.error || "Unknown error"}`
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
221
|
+
}
|
|
222
|
+
throw new Error(
|
|
223
|
+
`Image generation timed out after ${maxAttempts} attempts (${maxAttempts * pollInterval / 1e3}s)`
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
buildInputPayload(prompt, runpodSize, seed, runpodOptions) {
|
|
227
|
+
const isFluxModel = this.modelId.includes("flux") || this.modelId.includes("black-forest-labs");
|
|
228
|
+
if (isFluxModel) {
|
|
229
|
+
const isKontext = this.modelId.includes("kontext");
|
|
230
|
+
if (isKontext) {
|
|
231
|
+
return {
|
|
232
|
+
prompt,
|
|
233
|
+
negative_prompt: runpodOptions?.negative_prompt ?? "",
|
|
234
|
+
seed: seed ?? -1,
|
|
235
|
+
num_inference_steps: 28,
|
|
236
|
+
guidance: 2,
|
|
237
|
+
size: runpodSize,
|
|
238
|
+
output_format: "png",
|
|
239
|
+
enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,
|
|
240
|
+
...runpodOptions
|
|
241
|
+
// This will include the 'image' parameter if provided
|
|
242
|
+
};
|
|
243
|
+
} else {
|
|
244
|
+
const [width, height] = runpodSize.split("*").map(Number);
|
|
245
|
+
return {
|
|
246
|
+
prompt,
|
|
247
|
+
negative_prompt: runpodOptions?.negative_prompt ?? "",
|
|
248
|
+
seed: seed ?? -1,
|
|
249
|
+
num_inference_steps: this.modelId.includes("schnell") ? 4 : 28,
|
|
250
|
+
guidance: this.modelId.includes("schnell") ? 7 : 2,
|
|
251
|
+
width,
|
|
252
|
+
height,
|
|
253
|
+
image_format: "png",
|
|
254
|
+
...runpodOptions
|
|
255
|
+
};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
prompt,
|
|
260
|
+
negative_prompt: runpodOptions?.negative_prompt ?? "",
|
|
261
|
+
size: runpodSize,
|
|
262
|
+
seed: seed ?? -1,
|
|
263
|
+
enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,
|
|
264
|
+
...runpodOptions
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
};
|
|
268
|
+
var runpodImageResponseSchema = z.object({
|
|
269
|
+
id: z.string(),
|
|
270
|
+
status: z.enum(["COMPLETED", "IN_QUEUE", "IN_PROGRESS", "FAILED"]),
|
|
271
|
+
delayTime: z.number().optional(),
|
|
272
|
+
executionTime: z.number().optional(),
|
|
273
|
+
output: z.object({
|
|
274
|
+
cost: z.number().optional(),
|
|
275
|
+
result: z.string().optional(),
|
|
276
|
+
// URL to the generated image (Qwen format)
|
|
277
|
+
image_url: z.string().optional()
|
|
278
|
+
// URL to the generated image (Flux format)
|
|
279
|
+
}).optional()
|
|
280
|
+
// Optional for IN_QUEUE/IN_PROGRESS responses
|
|
281
|
+
});
|
|
282
|
+
var runpodImageStatusSchema = z.object({
|
|
283
|
+
id: z.string(),
|
|
284
|
+
status: z.enum(["COMPLETED", "IN_QUEUE", "IN_PROGRESS", "FAILED"]),
|
|
285
|
+
output: z.object({
|
|
286
|
+
cost: z.number().optional(),
|
|
287
|
+
result: z.string().optional(),
|
|
288
|
+
image_url: z.string().optional()
|
|
289
|
+
}).optional(),
|
|
290
|
+
error: z.string().optional()
|
|
291
|
+
// Error message if FAILED
|
|
292
|
+
});
|
|
293
|
+
var runpodImageErrorSchema = z.object({
|
|
294
|
+
error: z.string(),
|
|
295
|
+
message: z.string().optional()
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
// src/runpod-provider.ts
|
|
10
299
|
var MODEL_ID_TO_ENDPOINT_URL = {
|
|
11
300
|
"deep-cogito/deep-cogito-v2-llama-70b": "https://api.runpod.ai/v2/deep-cogito-v2-llama-70b/openai/v1",
|
|
12
301
|
"qwen/qwen3-32b-awq": "https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1"
|
|
13
302
|
};
|
|
303
|
+
var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
|
|
304
|
+
"qwen/qwen-image": "https://api.runpod.ai/v2/qwen-image-t2i",
|
|
305
|
+
"bytedance/seedream-3.0": "https://api.runpod.ai/v2/seedream-3-0-t2i",
|
|
306
|
+
"black-forest-labs/flux-1-kontext-dev": "https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev",
|
|
307
|
+
"black-forest-labs/flux-1-schnell": "https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell",
|
|
308
|
+
"black-forest-labs/flux-1-dev": "https://api.runpod.ai/v2/black-forest-labs-flux-1-dev"
|
|
309
|
+
};
|
|
14
310
|
var MODEL_ID_TO_OPENAI_NAME = {
|
|
15
311
|
"deep-cogito/deep-cogito-v2-llama-70b": "deepcogito/cogito-v2-preview-llama-70B",
|
|
16
312
|
"qwen/qwen3-32b-awq": "Qwen/Qwen3-32B-AWQ"
|
|
@@ -54,10 +350,27 @@ function createRunPod(options = {}) {
|
|
|
54
350
|
getModelConfig(modelId, "completion")
|
|
55
351
|
);
|
|
56
352
|
};
|
|
353
|
+
const createImageModel = (modelId) => {
|
|
354
|
+
const baseURL = IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId];
|
|
355
|
+
if (!baseURL) {
|
|
356
|
+
throw new Error(
|
|
357
|
+
`Unsupported Runpod image model: ${modelId}. Supported models: ${Object.keys(
|
|
358
|
+
IMAGE_MODEL_ID_TO_ENDPOINT_URL
|
|
359
|
+
).join(", ")}`
|
|
360
|
+
);
|
|
361
|
+
}
|
|
362
|
+
return new RunpodImageModel(modelId, {
|
|
363
|
+
provider: "runpod.image",
|
|
364
|
+
baseURL,
|
|
365
|
+
headers: getHeaders,
|
|
366
|
+
fetch: options.fetch
|
|
367
|
+
});
|
|
368
|
+
};
|
|
57
369
|
const provider = (modelId) => createChatModel(modelId);
|
|
58
370
|
provider.completionModel = createCompletionModel;
|
|
59
371
|
provider.languageModel = createChatModel;
|
|
60
372
|
provider.chatModel = createChatModel;
|
|
373
|
+
provider.imageModel = createImageModel;
|
|
61
374
|
return provider;
|
|
62
375
|
}
|
|
63
376
|
var runpod = createRunPod();
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/runpod-provider.ts"],"sourcesContent":["import { LanguageModelV2 } 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 { RunPodChatModelId } from './runpod-chat-options';\nimport { RunPodCompletionModelId } from './runpod-completion-options';\n\nexport interface RunPodProviderSettings {\n /**\nRunPod API key.\n*/\n apiKey?: 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: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: RunPodCompletionModelId): LanguageModelV2;\n}\n\n// Mapping of RunPod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'deep-cogito/deep-cogito-v2-llama-70b':\n 'https://api.runpod.ai/v2/deep-cogito-v2-llama-70b/openai/v1',\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n};\n\n// Mapping of RunPod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'deep-cogito/deep-cogito-v2-llama-70b':\n 'deepcogito/cogito-v2-preview-llama-70B',\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\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 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 const baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId];\n if (!baseURL) {\n throw new Error(\n `Unsupported RunPod model: ${modelId}. Supported models: ${Object.keys(\n MODEL_ID_TO_ENDPOINT_URL\n ).join(', ')}`\n );\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: options.fetch,\n };\n };\n\n const createChatModel = (modelId: RunPodChatModelId) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(\n openaiModelName,\n getModelConfig(modelId, 'chat')\n );\n };\n\n const createCompletionModel = (modelId: RunPodCompletionModelId) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(\n openaiModelName,\n getModelConfig(modelId, 'completion')\n );\n };\n\n const provider = (modelId: RunPodChatModelId) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n\n return provider;\n}\n\nexport const runpod = createRunPod();\n"],"mappings":";AACA;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP;AAAA,EAEE;AAAA,EACA;AAAA,OACK;AA2CP,IAAM,2BAAmD;AAAA,EACvD,wCACE;AAAA,EACF,sBAAsB;AACxB;AAGA,IAAM,0BAAkD;AAAA,EACtD,wCACE;AAAA,EACF,sBAAsB;AACxB;AAEO,SAAS,aACd,UAAkC,CAAC,GACnB;AAChB,QAAM,aAAa,OAAO;AAAA,IACxB,eAAe,UAAU,WAAW;AAAA,MAClC,QAAQ,QAAQ;AAAA,MAChB,yBAAyB;AAAA,MACzB,aAAa;AAAA,IACf,CAAC,CAAC;AAAA,IACF,GAAG,QAAQ;AAAA,EACb;AASA,QAAM,iBAAiB,CACrB,SACA,cACsB;AACtB,UAAM,UAAU,yBAAyB,OAAO;AAChD,QAAI,CAAC,SAAS;AACZ,YAAM,IAAI;AAAA,QACR,6BAA6B,OAAO,uBAAuB,OAAO;AAAA,UAChE;AAAA,QACF,EAAE,KAAK,IAAI,CAAC;AAAA,MACd;AAAA,IACF;AAEA,WAAO;AAAA,MACL,UAAU,UAAU,SAAS;AAAA,MAC7B,KAAK,CAAC,EAAE,KAAK,MAAM,GAAG,qBAAqB,OAAO,CAAC,GAAG,IAAI;AAAA,MAC1D,SAAS;AAAA,MACT,OAAO,QAAQ;AAAA,IACjB;AAAA,EACF;AAEA,QAAM,kBAAkB,CAAC,YAA+B;AACtD,UAAM,kBAAkB,wBAAwB,OAAO,KAAK;AAC5D,WAAO,IAAI;AAAA,MACT;AAAA,MACA,eAAe,SAAS,MAAM;AAAA,IAChC;AAAA,EACF;AAEA,QAAM,wBAAwB,CAAC,YAAqC;AAClE,UAAM,kBAAkB,wBAAwB,OAAO,KAAK;AAC5D,WAAO,IAAI;AAAA,MACT;AAAA,MACA,eAAe,SAAS,YAAY;AAAA,IACtC;AAAA,EACF;AAEA,QAAM,WAAW,CAAC,YAA+B,gBAAgB,OAAO;AAExE,WAAS,kBAAkB;AAC3B,WAAS,gBAAgB;AACzB,WAAS,YAAY;AAErB,SAAO;AACT;AAEO,IAAM,SAAS,aAAa;","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/runpod-provider.ts","../src/runpod-image-model.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 { RunPodChatModelId } from './runpod-chat-options';\nimport { RunPodCompletionModelId } from './runpod-completion-options';\nimport { RunpodImageModelId } from './runpod-image-options';\nimport { RunpodImageModel } from './runpod-image-model';\n\nexport interface RunPodProviderSettings {\n /**\nRunPod API key.\n*/\n apiKey?: 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: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: RunPodChatModelId): LanguageModelV2;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: RunPodCompletionModelId): LanguageModelV2;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: RunpodImageModelId): ImageModelV2;\n}\n\n// Mapping of RunPod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'deep-cogito/deep-cogito-v2-llama-70b':\n 'https://api.runpod.ai/v2/deep-cogito-v2-llama-70b/openai/v1',\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/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 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\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};\n\n// Mapping of RunPod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'deep-cogito/deep-cogito-v2-llama-70b':\n 'deepcogito/cogito-v2-preview-llama-70B',\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\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 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 const baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId];\n if (!baseURL) {\n throw new Error(\n `Unsupported RunPod model: ${modelId}. Supported models: ${Object.keys(\n MODEL_ID_TO_ENDPOINT_URL\n ).join(', ')}`\n );\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: options.fetch,\n };\n };\n\n const createChatModel = (modelId: RunPodChatModelId) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(\n openaiModelName,\n getModelConfig(modelId, 'chat')\n );\n };\n\n const createCompletionModel = (modelId: RunPodCompletionModelId) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(\n openaiModelName,\n getModelConfig(modelId, 'completion')\n );\n };\n\n const createImageModel = (modelId: RunpodImageModelId) => {\n const baseURL = IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId];\n if (!baseURL) {\n throw new Error(\n `Unsupported Runpod image model: ${modelId}. Supported models: ${Object.keys(\n IMAGE_MODEL_ID_TO_ENDPOINT_URL\n ).join(', ')}`\n );\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: RunPodChatModelId) => 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';\r\nimport {\r\n combineHeaders,\r\n createJsonResponseHandler,\r\n createJsonErrorResponseHandler,\r\n createBinaryResponseHandler,\r\n FetchFunction,\r\n postJsonToApi,\r\n getFromApi,\r\n} from '@ai-sdk/provider-utils';\r\nimport { InvalidArgumentError } from '@ai-sdk/provider';\r\nimport { z } from 'zod';\r\nimport { RunpodImageModelId } from './runpod-image-options';\r\n\r\ninterface RunpodImageModelConfig {\r\n provider: string;\r\n baseURL: string;\r\n headers: () => Record<string, string>;\r\n fetch?: FetchFunction;\r\n _internal?: {\r\n currentDate?: () => Date;\r\n };\r\n}\r\n\r\n// Runpod supported aspect ratios (only validated working sizes)\r\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\r\n '1:1': '1328*1328', // ✅ Native support\r\n '4:3': '1472*1140', // ✅ Native support\r\n '3:4': '1140*1472', // ✅ Native support\r\n};\r\n\r\n// Runpod supported sizes (validated working sizes)\r\nconst SUPPORTED_SIZES = new Set([\r\n // Native aspect ratio sizes\r\n '1328*1328', // 1:1\r\n '1472*1140', // 4:3\r\n '1140*1472', // 3:4\r\n // Additional validated sizes\r\n '512*512',\r\n '768*768',\r\n '1024*1024',\r\n '512*768',\r\n '768*512',\r\n '1024*768',\r\n '768*1024',\r\n]);\r\n\r\nexport class RunpodImageModel implements ImageModelV2 {\r\n readonly specificationVersion = 'v2';\r\n readonly maxImagesPerCall = 1;\r\n\r\n get provider(): string {\r\n return this.config.provider;\r\n }\r\n\r\n constructor(\r\n readonly modelId: RunpodImageModelId,\r\n private config: RunpodImageModelConfig\r\n ) {}\r\n\r\n async doGenerate({\r\n prompt,\r\n n = 1,\r\n size,\r\n aspectRatio,\r\n seed,\r\n providerOptions,\r\n headers,\r\n abortSignal,\r\n }: Parameters<ImageModelV2['doGenerate']>[0]): Promise<\r\n Awaited<ReturnType<ImageModelV2['doGenerate']>>\r\n > {\r\n const warnings: Array<ImageModelV2CallWarning> = [];\r\n\r\n // Determine the size to use\r\n let runpodSize: string;\r\n\r\n if (size) {\r\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\r\n const runpodSizeCandidate = size.replace('x', '*');\r\n\r\n // Validate size is supported\r\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\r\n throw new InvalidArgumentError({\r\n argument: 'size',\r\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\r\n SUPPORTED_SIZES\r\n )\r\n .map((s) => s.replace('*', 'x'))\r\n .join(', ')}`,\r\n });\r\n }\r\n\r\n runpodSize = runpodSizeCandidate;\r\n } else if (aspectRatio) {\r\n // Validate aspect ratio is supported\r\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\r\n throw new InvalidArgumentError({\r\n argument: 'aspectRatio',\r\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\r\n });\r\n }\r\n\r\n // Use supported aspect ratio mapping\r\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\r\n } else {\r\n // Default to square format\r\n runpodSize = '1328*1328';\r\n }\r\n\r\n // Handle multiple images warning\r\n if (n > 1) {\r\n warnings.push({\r\n type: 'unsupported-setting',\r\n setting: 'n',\r\n details:\r\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\r\n });\r\n }\r\n\r\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\r\n\r\n // Runpod uses a different request format - /runsync endpoint with input wrapper\r\n const inputPayload = this.buildInputPayload(\r\n prompt,\r\n runpodSize,\r\n seed,\r\n providerOptions.runpod\r\n );\r\n\r\n const { value: response, responseHeaders } = await postJsonToApi({\r\n url: `${this.config.baseURL}/runsync`,\r\n headers: combineHeaders(this.config.headers(), headers),\r\n body: {\r\n input: inputPayload,\r\n },\r\n failedResponseHandler: createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: any) => data.error ?? 'Unknown error',\r\n }),\r\n successfulResponseHandler: createJsonResponseHandler(\r\n runpodImageResponseSchema as any\r\n ),\r\n abortSignal,\r\n fetch: this.config.fetch,\r\n });\r\n\r\n // Handle both sync and async responses from Runpod\r\n const typedResponse = response as any;\r\n if (\r\n typedResponse.status === 'COMPLETED' &&\r\n (typedResponse.output?.result || typedResponse.output?.image_url)\r\n ) {\r\n // Sync response - image is ready\r\n // Different models use different response formats: result vs image_url\r\n const imageUrl =\r\n typedResponse.output.result || typedResponse.output.image_url;\r\n const imageData = await this.downloadImage(imageUrl, abortSignal);\r\n\r\n return {\r\n images: [imageData],\r\n warnings,\r\n response: {\r\n timestamp: currentDate,\r\n modelId: this.modelId,\r\n headers: responseHeaders,\r\n },\r\n providerMetadata: {\r\n runpod: {\r\n images: [\r\n {\r\n url: imageUrl,\r\n cost: typedResponse.output?.cost,\r\n },\r\n ],\r\n },\r\n },\r\n };\r\n } else if (\r\n typedResponse.status === 'IN_QUEUE' ||\r\n typedResponse.status === 'IN_PROGRESS'\r\n ) {\r\n // Async response - need to poll for completion\r\n const pollOptions = {\r\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\r\n pollIntervalMillis: providerOptions.runpod\r\n ?.pollIntervalMillis as number,\r\n };\r\n const imageUrl = await this.pollForCompletion(\r\n typedResponse.id,\r\n abortSignal,\r\n pollOptions\r\n );\r\n const imageData = await this.downloadImage(imageUrl, abortSignal);\r\n\r\n return {\r\n images: [imageData],\r\n warnings,\r\n response: {\r\n timestamp: currentDate,\r\n modelId: this.modelId,\r\n headers: responseHeaders,\r\n },\r\n providerMetadata: {\r\n runpod: {\r\n images: [\r\n {\r\n url: imageUrl,\r\n jobId: typedResponse.id,\r\n },\r\n ],\r\n },\r\n },\r\n };\r\n } else {\r\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\r\n }\r\n }\r\n\r\n private async downloadImage(\r\n imageUrl: string,\r\n abortSignal?: AbortSignal\r\n ): Promise<Uint8Array> {\r\n const { value: imageData } = await getFromApi({\r\n url: imageUrl,\r\n successfulResponseHandler: createBinaryResponseHandler(),\r\n failedResponseHandler: createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: any) => data.error ?? 'Failed to download image',\r\n }),\r\n abortSignal,\r\n fetch: this.config.fetch,\r\n });\r\n return imageData;\r\n }\r\n\r\n private async pollForCompletion(\r\n jobId: string,\r\n abortSignal?: AbortSignal,\r\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number }\r\n ): Promise<string> {\r\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\r\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\r\n\r\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\r\n if (abortSignal?.aborted) {\r\n throw new Error('Image generation was aborted');\r\n }\r\n\r\n const { value: statusResponse } = await getFromApi({\r\n url: `${this.config.baseURL}/status/${jobId}`,\r\n headers: this.config.headers(),\r\n successfulResponseHandler: createJsonResponseHandler(\r\n runpodImageStatusSchema as any\r\n ),\r\n failedResponseHandler: createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: any) =>\r\n data.error ?? 'Failed to check job status',\r\n }),\r\n abortSignal,\r\n fetch: this.config.fetch,\r\n });\r\n\r\n const typedStatusResponse = statusResponse as any;\r\n if (\r\n typedStatusResponse.status === 'COMPLETED' &&\r\n (typedStatusResponse.output?.result ||\r\n typedStatusResponse.output?.image_url)\r\n ) {\r\n return (\r\n typedStatusResponse.output.result ||\r\n typedStatusResponse.output.image_url\r\n );\r\n }\r\n\r\n if (typedStatusResponse.status === 'FAILED') {\r\n throw new Error(\r\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\r\n );\r\n }\r\n\r\n // Wait before next poll\r\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\r\n }\r\n\r\n throw new Error(\r\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\r\n );\r\n }\r\n\r\n private buildInputPayload(\r\n prompt: string,\r\n runpodSize: string,\r\n seed?: number,\r\n runpodOptions?: Record<string, unknown>\r\n ): Record<string, unknown> {\r\n // Check if this is a Flux model that uses different parameters\r\n const isFluxModel =\r\n this.modelId.includes('flux') ||\r\n this.modelId.includes('black-forest-labs');\r\n\r\n if (isFluxModel) {\r\n // Check if this is Flux Kontext (uses different parameters)\r\n const isKontext = this.modelId.includes('kontext');\r\n\r\n if (isKontext) {\r\n // Flux Kontext uses size format and has image input\r\n return {\r\n prompt,\r\n negative_prompt: runpodOptions?.negative_prompt ?? '',\r\n seed: seed ?? -1,\r\n num_inference_steps: 28,\r\n guidance: 2,\r\n size: runpodSize,\r\n output_format: 'png',\r\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\r\n ...runpodOptions, // This will include the 'image' parameter if provided\r\n };\r\n } else {\r\n // Regular Flux models use width/height\r\n const [width, height] = runpodSize.split('*').map(Number);\r\n\r\n return {\r\n prompt,\r\n negative_prompt: runpodOptions?.negative_prompt ?? '',\r\n seed: seed ?? -1,\r\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\r\n guidance: this.modelId.includes('schnell') ? 7 : 2,\r\n width,\r\n height,\r\n image_format: 'png',\r\n ...runpodOptions,\r\n };\r\n }\r\n }\r\n\r\n // Default format for Qwen and other models\r\n return {\r\n prompt,\r\n negative_prompt: runpodOptions?.negative_prompt ?? '',\r\n size: runpodSize,\r\n seed: seed ?? -1,\r\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\r\n ...runpodOptions,\r\n };\r\n }\r\n}\r\n\r\n// Runpod image API response schema (handles both sync and async responses)\r\nconst runpodImageResponseSchema = z.object({\r\n id: z.string(),\r\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\r\n delayTime: z.number().optional(),\r\n executionTime: z.number().optional(),\r\n output: z\r\n .object({\r\n cost: z.number().optional(),\r\n result: z.string().optional(), // URL to the generated image (Qwen format)\r\n image_url: z.string().optional(), // URL to the generated image (Flux format)\r\n })\r\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\r\n});\r\n\r\n// Schema for polling status endpoint\r\nconst runpodImageStatusSchema = z.object({\r\n id: z.string(),\r\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\r\n output: z\r\n .object({\r\n cost: z.number().optional(),\r\n result: z.string().optional(),\r\n image_url: z.string().optional(),\r\n })\r\n .optional(),\r\n error: z.string().optional(), // Error message if FAILED\r\n});\r\n\r\n// Runpod image API error schema\r\nconst runpodImageErrorSchema = z.object({\r\n error: z.string(),\r\n message: z.string().optional(),\r\n});\r\n"],"mappings":"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","names":[]}
|