@runpod/ai-sdk-provider 0.1.0 → 0.2.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 +17 -0
- package/README.md +94 -50
- 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.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 1fec4a8: added support for image gen:
|
|
8
|
+
- `qwen/qwen-image`
|
|
9
|
+
- `bytedance/seedream-3.0`
|
|
10
|
+
- `black-forest-labs/flux-1-kontext-dev`
|
|
11
|
+
- `black-forest-labs/flux-1-schnell`
|
|
12
|
+
- `black-forest-labs/flux-1-dev`
|
|
13
|
+
|
|
14
|
+
## 0.1.1
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- 3c90c0e: simplified the docs
|
|
19
|
+
|
|
3
20
|
## 0.1.0
|
|
4
21
|
|
|
5
22
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Runpod AI SDK Provider
|
|
2
2
|
|
|
3
|
-
The **
|
|
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
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
+
# npm
|
|
8
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
|
|
9
16
|
```
|
|
10
17
|
|
|
11
18
|
## Setup
|
|
12
19
|
|
|
13
|
-
The
|
|
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".
|
|
14
21
|
|
|
15
22
|
### Environment Variable
|
|
16
23
|
|
|
@@ -25,27 +32,39 @@ export RUNPOD_API_KEY="your-api-key-here"
|
|
|
25
32
|
Import the provider:
|
|
26
33
|
|
|
27
34
|
```ts
|
|
28
|
-
import { runpod } from
|
|
35
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
29
36
|
```
|
|
30
37
|
|
|
31
38
|
## Supported Models
|
|
32
39
|
|
|
40
|
+
### Language Models
|
|
41
|
+
|
|
33
42
|
| Model ID | Description |
|
|
34
43
|
| -------------------------------------- | ------------------------------------------------------------------- |
|
|
35
44
|
| `deep-cogito/deep-cogito-v2-llama-70b` | 70B parameter general-purpose LLM with advanced reasoning |
|
|
36
45
|
| `qwen/qwen3-32b-awq` | 32B parameter multilingual model with strong reasoning capabilities |
|
|
37
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
|
+
|
|
38
57
|
## Usage Examples
|
|
39
58
|
|
|
40
59
|
### Basic Text Generation
|
|
41
60
|
|
|
42
61
|
```ts
|
|
43
|
-
import { runpod } from
|
|
44
|
-
import { generateText } from
|
|
62
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
63
|
+
import { generateText } from 'ai';
|
|
45
64
|
|
|
46
65
|
const { text } = await generateText({
|
|
47
|
-
model: runpod(
|
|
48
|
-
prompt:
|
|
66
|
+
model: runpod('deep-cogito/deep-cogito-v2-llama-70b'),
|
|
67
|
+
prompt: 'Write a Python function that sorts a list:',
|
|
49
68
|
});
|
|
50
69
|
|
|
51
70
|
console.log(text);
|
|
@@ -53,19 +72,19 @@ console.log(text);
|
|
|
53
72
|
|
|
54
73
|
### Streaming
|
|
55
74
|
|
|
56
|
-
**Note**: Streaming is not yet supported by
|
|
75
|
+
**Note**: Streaming is not yet supported by Runpod's public endpoints. The team is working on implementing this feature.
|
|
57
76
|
|
|
58
77
|
### Chat Conversations
|
|
59
78
|
|
|
60
79
|
```ts
|
|
61
|
-
import { runpod } from
|
|
62
|
-
import { generateText } from
|
|
80
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
81
|
+
import { generateText } from 'ai';
|
|
63
82
|
|
|
64
83
|
const { text } = await generateText({
|
|
65
|
-
model: runpod(
|
|
84
|
+
model: runpod('deep-cogito/deep-cogito-v2-llama-70b'),
|
|
66
85
|
messages: [
|
|
67
|
-
{ role:
|
|
68
|
-
{ role:
|
|
86
|
+
{ role: 'system', content: 'You are a helpful assistant.' },
|
|
87
|
+
{ role: 'user', content: 'What is the capital of France?' },
|
|
69
88
|
],
|
|
70
89
|
});
|
|
71
90
|
```
|
|
@@ -73,18 +92,18 @@ const { text } = await generateText({
|
|
|
73
92
|
### Function Calling
|
|
74
93
|
|
|
75
94
|
```ts
|
|
76
|
-
import { runpod } from
|
|
77
|
-
import { generateText, tool } from
|
|
78
|
-
import { z } from
|
|
95
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
96
|
+
import { generateText, tool } from 'ai';
|
|
97
|
+
import { z } from 'zod';
|
|
79
98
|
|
|
80
99
|
const { text, toolCalls } = await generateText({
|
|
81
|
-
model: runpod(
|
|
82
|
-
prompt:
|
|
100
|
+
model: runpod('deep-cogito/deep-cogito-v2-llama-70b'),
|
|
101
|
+
prompt: 'What is the weather like in San Francisco?',
|
|
83
102
|
tools: {
|
|
84
103
|
getWeather: tool({
|
|
85
|
-
description:
|
|
104
|
+
description: 'Get weather information for a city',
|
|
86
105
|
parameters: z.object({
|
|
87
|
-
city: z.string().describe(
|
|
106
|
+
city: z.string().describe('The city name'),
|
|
88
107
|
}),
|
|
89
108
|
execute: async ({ city }) => {
|
|
90
109
|
// Your weather API call here
|
|
@@ -98,12 +117,12 @@ const { text, toolCalls } = await generateText({
|
|
|
98
117
|
### Structured Output
|
|
99
118
|
|
|
100
119
|
```ts
|
|
101
|
-
import { runpod } from
|
|
102
|
-
import { generateObject } from
|
|
103
|
-
import { z } from
|
|
120
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
121
|
+
import { generateObject } from 'ai';
|
|
122
|
+
import { z } from 'zod';
|
|
104
123
|
|
|
105
124
|
const { object } = await generateObject({
|
|
106
|
-
model: runpod(
|
|
125
|
+
model: runpod('qwen/qwen3-32b-awq'),
|
|
107
126
|
schema: z.object({
|
|
108
127
|
recipe: z.object({
|
|
109
128
|
name: z.string(),
|
|
@@ -111,44 +130,69 @@ const { object } = await generateObject({
|
|
|
111
130
|
steps: z.array(z.string()),
|
|
112
131
|
}),
|
|
113
132
|
}),
|
|
114
|
-
prompt:
|
|
133
|
+
prompt: 'Generate a recipe for chocolate chip cookies.',
|
|
115
134
|
});
|
|
116
135
|
|
|
117
136
|
console.log(object.recipe);
|
|
118
137
|
```
|
|
119
138
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
The provider supports multiple ways to create models:
|
|
139
|
+
### Image Generation
|
|
123
140
|
|
|
124
141
|
```ts
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
// Explicit chat model
|
|
129
|
-
const model2 = runpod.chatModel("deep-cogito/deep-cogito-v2-llama-70b");
|
|
142
|
+
import { runpod } from '@runpod/ai-sdk-provider';
|
|
143
|
+
import { experimental_generateImage as generateImage } from 'ai';
|
|
130
144
|
|
|
131
|
-
|
|
132
|
-
|
|
145
|
+
const { image } = await generateImage({
|
|
146
|
+
model: runpod.imageModel('qwen/qwen-image'),
|
|
147
|
+
prompt: 'A fashion-forward woman in Paris wearing a trench coat',
|
|
148
|
+
aspectRatio: '4:3',
|
|
149
|
+
});
|
|
133
150
|
|
|
134
|
-
//
|
|
135
|
-
const
|
|
151
|
+
// With additional parameters
|
|
152
|
+
const { image } = await generateImage({
|
|
153
|
+
model: runpod.imageModel('qwen/qwen-image'),
|
|
154
|
+
prompt: 'A sunset over mountains',
|
|
155
|
+
size: '1328x1328',
|
|
156
|
+
seed: 42,
|
|
157
|
+
providerOptions: {
|
|
158
|
+
runpod: {
|
|
159
|
+
negative_prompt: 'blurry, low quality',
|
|
160
|
+
enable_safety_checker: true,
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
136
164
|
```
|
|
137
165
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
#### Provider Options
|
|
167
|
+
|
|
168
|
+
The Runpod provider supports additional options via `providerOptions.runpod`:
|
|
169
|
+
|
|
170
|
+
| Option | Type | Default | Description |
|
|
171
|
+
| ----------------------- | --------- | ------- | --------------------------------------------------- |
|
|
172
|
+
| `negative_prompt` | `string` | - | Text describing what you don't want in the image |
|
|
173
|
+
| `enable_safety_checker` | `boolean` | `true` | Enable content safety filtering |
|
|
174
|
+
| `image` | `string` | - | Input image URL (required for Flux Kontext models) |
|
|
175
|
+
| `maxPollAttempts` | `number` | `60` | Maximum polling attempts for async image generation |
|
|
176
|
+
| `pollIntervalMillis` | `number` | `5000` | Polling interval in milliseconds (5 seconds) |
|
|
177
|
+
|
|
178
|
+
**Notes**:
|
|
179
|
+
|
|
180
|
+
- 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.
|
|
181
|
+
- Model availability may vary. Some models might be temporarily unavailable or require specific parameters.
|
|
182
|
+
- **Verified working models**: All models tested and confirmed working
|
|
183
|
+
- `qwen/qwen-image` - Original model (60s, JPEG)
|
|
184
|
+
- `bytedance/seedream-3.0` - Fast model (17s, JPEG)
|
|
185
|
+
- `black-forest-labs/flux-1-schnell` - Very fast (2s, PNG)
|
|
186
|
+
- `black-forest-labs/flux-1-dev` - High quality (6s, PNG)
|
|
187
|
+
- `black-forest-labs/flux-1-kontext-dev` - Context-aware with input images (38s, PNG)
|
|
188
|
+
- The provider automatically handles different parameter formats:
|
|
189
|
+
- **Qwen/Seedream**: `size` parameter, `result` response field
|
|
190
|
+
- **Flux standard**: `width/height` parameters, `image_url` response field
|
|
191
|
+
- **Flux Kontext**: `size` parameter + `image` input, `image_url` response field
|
|
148
192
|
|
|
149
193
|
## Links
|
|
150
194
|
|
|
151
|
-
- [
|
|
152
|
-
- [
|
|
195
|
+
- [Runpod](https://runpod.io) - Cloud platform for AI compute
|
|
196
|
+
- [Runpod Public Endpoints Documentation](https://docs.runpod.io/hub/public-endpoints)
|
|
153
197
|
- [AI SDK Documentation](https://ai-sdk.dev/docs)
|
|
154
198
|
- [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":[]}
|