@runpod/ai-sdk-provider 1.0.0 → 1.1.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 CHANGED
@@ -1,5 +1,21 @@
1
1
  # @runpod/ai-sdk-provider
2
2
 
3
+ ## 1.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7ec59bc: add image models and improvements
8
+ - alibaba/wan-2.6: text-to-image model (max 1024x1024)
9
+ - qwen/qwen-image-edit-2511: edit model (max 1536x1536), supports 1-3 input images and loras
10
+ - google/nano-banana-edit: renamed from nano-banana-edit (backwards compatible), fixed payload format
11
+ - added resolution and aspect ratios columns to supported models table
12
+
13
+ ## 1.0.1
14
+
15
+ ### Patch Changes
16
+
17
+ - f13758a: docs: update generateImage and generateSpeech imports to non-experimental
18
+
3
19
  ## 1.0.0
4
20
 
5
21
  ### Major Changes
package/README.md CHANGED
@@ -1,7 +1,5 @@
1
1
  # Runpod AI SDK Provider
2
2
 
3
- ![Runpod AI SDK Provider banner](https://image.runpod.ai/runpod/ai-sdk-provider/banner.jpg)
4
-
5
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.
6
4
 
7
5
  ## Setup
@@ -207,13 +205,13 @@ With image models you can:
207
205
  - **Text-to-image**: generate a new image from a text prompt.
208
206
  - **Edit image**: transform an existing image by providing reference image(s).
209
207
 
210
- All examples use the AI SDK's `experimental_generateImage` and `runpod.image(modelId)`.
208
+ All examples use the AI SDK's `generateImage` and `runpod.image(modelId)`.
211
209
 
212
210
  ### Text-to-Image
213
211
 
214
212
  ```ts
215
213
  import { runpod } from '@runpod/ai-sdk-provider';
216
- import { experimental_generateImage as generateImage } from 'ai';
214
+ import { generateImage } from 'ai';
217
215
  import { writeFileSync } from 'fs';
218
216
 
219
217
  const { image } = await generateImage({
@@ -240,7 +238,7 @@ For editing, pass reference images via `prompt.images` (recommended). The AI SDK
240
238
 
241
239
  ```ts
242
240
  import { runpod } from '@runpod/ai-sdk-provider';
243
- import { experimental_generateImage as generateImage } from 'ai';
241
+ import { generateImage } from 'ai';
244
242
 
245
243
  const { image } = await generateImage({
246
244
  model: runpod.image('pruna/p-image-edit'),
@@ -258,7 +256,7 @@ Note: Prior to v1.0.0, images were passed via `providerOptions.runpod.image` / `
258
256
 
259
257
  ```ts
260
258
  import { runpod } from '@runpod/ai-sdk-provider';
261
- import { experimental_generateImage as generateImage } from 'ai';
259
+ import { generateImage } from 'ai';
262
260
 
263
261
  const { image } = await generateImage({
264
262
  model: runpod.image('google/nano-banana-pro-edit'),
@@ -280,20 +278,22 @@ Check out our [examples](https://github.com/runpod/examples/tree/main/ai-sdk/get
280
278
 
281
279
  ### Supported Models
282
280
 
283
- | Model ID | Type |
284
- | -------------------------------------- | ---- |
285
- | `pruna/p-image-t2i` | t2i |
286
- | `pruna/p-image-edit` | edit |
287
- | `google/nano-banana-pro-edit` | edit |
288
- | `bytedance/seedream-3.0` | t2i |
289
- | `bytedance/seedream-4.0` | t2i |
290
- | `bytedance/seedream-4.0-edit` | edit |
291
- | `qwen/qwen-image` | t2i |
292
- | `qwen/qwen-image-edit` | edit |
293
- | `nano-banana-edit` | edit |
294
- | `black-forest-labs/flux-1-schnell` | t2i |
295
- | `black-forest-labs/flux-1-dev` | t2i |
296
- | `black-forest-labs/flux-1-kontext-dev` | edit |
281
+ | Model ID | Type | Resolution | Aspect Ratios |
282
+ | -------------------------------------- | ---- | --------------- | ----------------------------------------- |
283
+ | `alibaba/wan-2.6` | t2i | 1024x1024 | 1:1, 4:3, 3:4 |
284
+ | `pruna/p-image-t2i` | t2i | up to 1440x1440 | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 |
285
+ | `pruna/p-image-edit` | edit | up to 1440x1440 | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3 |
286
+ | `google/nano-banana-edit` | edit | up to 4096x4096 | 1:1, 4:3, 3:4 |
287
+ | `google/nano-banana-pro-edit` | edit | 1k, 2k, 4k | 1:1, 16:9, 9:16, 4:3, 3:4, 3:2, 2:3, 21:9 |
288
+ | `bytedance/seedream-3.0` | t2i | up to 4096x4096 | 1:1, 4:3, 3:4 |
289
+ | `bytedance/seedream-4.0` | t2i | up to 4096x4096 | 1:1, 4:3, 3:4 |
290
+ | `bytedance/seedream-4.0-edit` | edit | up to 4096x4096 | uses size |
291
+ | `qwen/qwen-image` | t2i | up to 4096x4096 | 1:1, 4:3, 3:4 |
292
+ | `qwen/qwen-image-edit` | edit | up to 4096x4096 | 1:1, 4:3, 3:4 |
293
+ | `qwen/qwen-image-edit-2511` | edit | up to 1536x1536 | 1:1, 4:3, 3:4 |
294
+ | `black-forest-labs/flux-1-schnell` | t2i | up to 2048x2048 | 1:1, 4:3, 3:4 |
295
+ | `black-forest-labs/flux-1-dev` | t2i | up to 2048x2048 | 1:1, 4:3, 3:4 |
296
+ | `black-forest-labs/flux-1-kontext-dev` | edit | up to 2048x2048 | 1:1, 4:3, 3:4 |
297
297
 
298
298
  For the full list of models, see the [Runpod Public Endpoint Reference](https://docs.runpod.io/hub/public-endpoint-reference).
299
299
 
@@ -301,21 +301,15 @@ For the full list of models, see the [Runpod Public Endpoint Reference](https://
301
301
 
302
302
  Additional options through `providerOptions.runpod` (supported options depend on the model):
303
303
 
304
- | Option | Type | Default | Description |
305
- | ------------------------ | ---------- | ------- | ----------------------------------------------------------- |
306
- | `negative_prompt` | `string` | `""` | What to avoid in the image (model-dependent) |
307
- | `enable_safety_checker` | `boolean` | `true` | Content safety filtering (model-dependent) |
308
- | `disable_safety_checker` | `boolean` | `false` | Disable safety checker (Pruna) |
309
- | `aspect_ratio` | `string` | - | Model-specific aspect ratio (Pruna: supports `custom`) |
310
- | `image` | `string` | - | Legacy: Single input image URL/base64 (use `prompt.images`) |
311
- | `images` | `string[]` | - | Legacy: Multiple input images (use `prompt.images`) |
312
- | `resolution` | `string` | `"1k"` | Output resolution: 1k, 2k, 4k (Nano Banana Pro) |
313
- | `width` / `height` | `number` | - | Custom dimensions (Pruna t2i, 256-1440; multiples of 16) |
314
- | `num_inference_steps` | `number` | Auto | Denoising steps (model-dependent) |
315
- | `guidance` | `number` | Auto | Prompt adherence strength (model-dependent) |
316
- | `output_format` | `string` | `"png"` | Output format: png, jpg, jpeg, webp (model-dependent) |
317
- | `maxPollAttempts` | `number` | `60` | Max polling attempts |
318
- | `pollIntervalMillis` | `number` | `5000` | Polling interval (ms) |
304
+ | Option | Type | Default | Description |
305
+ | ----------------------- | --------- | ------- | -------------------------------------------- |
306
+ | `negative_prompt` | `string` | `""` | What to avoid in the image (model-dependent) |
307
+ | `enable_safety_checker` | `boolean` | `true` | Content safety filtering (model-dependent) |
308
+ | `num_inference_steps` | `number` | Auto | Denoising steps (model-dependent) |
309
+ | `guidance` | `number` | Auto | Prompt adherence strength (model-dependent) |
310
+ | `output_format` | `string` | `"png"` | Output format: png, jpg, jpeg, webp |
311
+ | `maxPollAttempts` | `number` | `60` | Max polling attempts |
312
+ | `pollIntervalMillis` | `number` | `5000` | Polling interval (ms) |
319
313
 
320
314
  **Example (providerOptions):**
321
315
 
@@ -363,24 +357,59 @@ const { image } = await generateImage({
363
357
 
364
358
  #### Google (Nano Banana Pro)
365
359
 
366
- Supported model: `google/nano-banana-pro-edit`
360
+ | Option | Values |
361
+ | :---------------------------------- | :--------------- |
362
+ | `providerOptions.runpod.resolution` | `1k`, `2k`, `4k` |
363
+
364
+ ```ts
365
+ const { image } = await generateImage({
366
+ model: runpod.image('google/nano-banana-pro'),
367
+ prompt: 'A futuristic cityscape at sunset',
368
+ aspectRatio: '16:9',
369
+ providerOptions: {
370
+ runpod: {
371
+ resolution: '4k',
372
+ },
373
+ },
374
+ });
375
+ ```
376
+
377
+ #### Qwen (Image Edit 2511)
367
378
 
368
- | Parameter | Supported Values | Notes |
369
- | :------------------------------ | :---------------------------------------------------------------- | :----------------------------------- |
370
- | `aspectRatio` | `1:1`, `16:9`, `9:16`, `4:3`, `3:4`, `3:2`, `2:3`, `21:9`, `9:21` | Standard AI SDK parameter |
371
- | `resolution` | `1k`, `2k`, `4k` | Output resolution quality |
372
- | `output_format` | `jpeg`, `png`, `webp` | Output image format |
373
- | `prompt.images` | `string[]` | Recommended. Input image(s) to edit. |
374
- | `files` | `ImageModelV3File[]` | Alternative (lower-level). |
375
- | `providerOptions.runpod.images` | `string[]` | Legacy. Input image(s) to edit. |
379
+ | Option | Values |
380
+ | :----------------------------- | :--------------------- |
381
+ | `providerOptions.runpod.loras` | `[{path, scale}, ...]` |
382
+
383
+ Supports 1-3 input images.
384
+
385
+ ```ts
386
+ const { image } = await generateImage({
387
+ model: runpod.image('qwen/qwen-image-edit-2511'),
388
+ prompt: {
389
+ text: 'Transform into anime style',
390
+ images: ['https://image.runpod.ai/asset/qwen/qwen-image-edit-2511.png'],
391
+ },
392
+ size: '1024x1024',
393
+ providerOptions: {
394
+ runpod: {
395
+ loras: [
396
+ {
397
+ path: 'https://huggingface.co/flymy-ai/qwen-image-anime-irl-lora/resolve/main/flymy_anime_irl.safetensors',
398
+ scale: 1,
399
+ },
400
+ ],
401
+ },
402
+ },
403
+ });
404
+ ```
376
405
 
377
406
  ## Speech Models
378
407
 
379
- Generate speech using the AI SDK's `experimental_generateSpeech` and `runpod.speech(...)`:
408
+ Generate speech using the AI SDK's `generateSpeech` and `runpod.speech(...)`:
380
409
 
381
410
  ```ts
382
411
  import { runpod } from '@runpod/ai-sdk-provider';
383
- import { experimental_generateSpeech as generateSpeech } from 'ai';
412
+ import { generateSpeech } from 'ai';
384
413
 
385
414
  const result = await generateSpeech({
386
415
  model: runpod.speech('resembleai/chatterbox-turbo'),
@@ -474,7 +503,7 @@ const result = await generateSpeech({
474
503
  text: 'Hello!',
475
504
  providerOptions: {
476
505
  runpod: {
477
- voice_url: 'https://example.com/voice.wav',
506
+ voice_url: 'https://your-audio-host.com/your-voice-sample.wav', // 5-10s audio sample
478
507
  },
479
508
  },
480
509
  });
package/dist/index.d.mts CHANGED
@@ -64,7 +64,7 @@ type RunpodChatModelId = 'qwen/qwen3-32b-awq' | (string & {});
64
64
 
65
65
  type RunpodCompletionModelId = 'qwen/qwen3-32b-awq' | (string & {});
66
66
 
67
- type RunpodImageModelId = 'qwen/qwen-image' | 'qwen/qwen-image-edit' | 'bytedance/seedream-3.0' | 'bytedance/seedream-4.0' | 'bytedance/seedream-4.0-edit' | 'black-forest-labs/flux-1-kontext-dev' | 'black-forest-labs/flux-1-schnell' | 'black-forest-labs/flux-1-dev' | 'nano-banana-edit';
67
+ type RunpodImageModelId = 'qwen/qwen-image' | 'qwen/qwen-image-edit' | 'qwen/qwen-image-edit-2511' | 'bytedance/seedream-3.0' | 'bytedance/seedream-4.0' | 'bytedance/seedream-4.0-edit' | 'black-forest-labs/flux-1-kontext-dev' | 'black-forest-labs/flux-1-schnell' | 'black-forest-labs/flux-1-dev' | 'alibaba/wan-2.6' | 'google/nano-banana-edit' | 'nano-banana-edit';
68
68
 
69
69
  declare const runpodImageErrorSchema: z.ZodObject<{
70
70
  error: z.ZodOptional<z.ZodString>;
package/dist/index.d.ts CHANGED
@@ -64,7 +64,7 @@ type RunpodChatModelId = 'qwen/qwen3-32b-awq' | (string & {});
64
64
 
65
65
  type RunpodCompletionModelId = 'qwen/qwen3-32b-awq' | (string & {});
66
66
 
67
- type RunpodImageModelId = 'qwen/qwen-image' | 'qwen/qwen-image-edit' | 'bytedance/seedream-3.0' | 'bytedance/seedream-4.0' | 'bytedance/seedream-4.0-edit' | 'black-forest-labs/flux-1-kontext-dev' | 'black-forest-labs/flux-1-schnell' | 'black-forest-labs/flux-1-dev' | 'nano-banana-edit';
67
+ type RunpodImageModelId = 'qwen/qwen-image' | 'qwen/qwen-image-edit' | 'qwen/qwen-image-edit-2511' | 'bytedance/seedream-3.0' | 'bytedance/seedream-4.0' | 'bytedance/seedream-4.0-edit' | 'black-forest-labs/flux-1-kontext-dev' | 'black-forest-labs/flux-1-schnell' | 'black-forest-labs/flux-1-dev' | 'alibaba/wan-2.6' | 'google/nano-banana-edit' | 'nano-banana-edit';
68
68
 
69
69
  declare const runpodImageErrorSchema: z.ZodObject<{
70
70
  error: z.ZodOptional<z.ZodString>;
package/dist/index.js CHANGED
@@ -168,8 +168,16 @@ var RunpodImageModel = class {
168
168
  aspectRatio,
169
169
  standardizedImages
170
170
  );
171
+ let effectiveBaseURL = this.config.baseURL;
172
+ const runpodOptions = providerOptions.runpod;
173
+ if (this.modelId.includes("qwen-image-edit-2511") && !this.modelId.includes("lora") && runpodOptions?.loras && Array.isArray(runpodOptions.loras) && runpodOptions.loras.length > 0) {
174
+ effectiveBaseURL = this.config.baseURL.replace(
175
+ "qwen-image-edit-2511",
176
+ "qwen-image-edit-2511-lora"
177
+ );
178
+ }
171
179
  const { value: response, responseHeaders } = await (0, import_provider_utils2.postJsonToApi)({
172
- url: `${this.config.baseURL}/runsync`,
180
+ url: `${effectiveBaseURL}/runsync`,
173
181
  headers: (0, import_provider_utils2.combineHeaders)(this.config.headers(), headers),
174
182
  body: {
175
183
  input: inputPayload
@@ -212,7 +220,8 @@ var RunpodImageModel = class {
212
220
  const imageUrl = await this.pollForCompletion(
213
221
  typedResponse.id,
214
222
  abortSignal,
215
- pollOptions
223
+ pollOptions,
224
+ effectiveBaseURL
216
225
  );
217
226
  const imageData = await this.downloadImage(imageUrl, abortSignal);
218
227
  return {
@@ -248,15 +257,16 @@ var RunpodImageModel = class {
248
257
  });
249
258
  return imageData;
250
259
  }
251
- async pollForCompletion(jobId, abortSignal, pollOptions) {
260
+ async pollForCompletion(jobId, abortSignal, pollOptions, effectiveBaseURL) {
252
261
  const maxAttempts = pollOptions?.maxAttempts ?? 60;
253
262
  const pollInterval = pollOptions?.pollIntervalMillis ?? 5e3;
263
+ const baseURL = effectiveBaseURL ?? this.config.baseURL;
254
264
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
255
265
  if (abortSignal?.aborted) {
256
266
  throw new Error("Image generation was aborted");
257
267
  }
258
268
  const { value: statusResponse } = await (0, import_provider_utils2.getFromApi)({
259
- url: `${this.config.baseURL}/status/${jobId}`,
269
+ url: `${baseURL}/status/${jobId}`,
260
270
  headers: this.config.headers(),
261
271
  successfulResponseHandler: (0, import_provider_utils2.createJsonResponseHandler)(
262
272
  runpodImageStatusSchema
@@ -406,6 +416,47 @@ var RunpodImageModel = class {
406
416
  }
407
417
  return nanoBananaPayload;
408
418
  }
419
+ const isNanaBananaEditModel = this.modelId.includes("nano-banana-edit") && !this.modelId.includes("nano-banana-pro");
420
+ if (isNanaBananaEditModel) {
421
+ const nanoBananaEditPayload = {
422
+ prompt,
423
+ enable_safety_checker: runpodOptions?.enable_safety_checker ?? true
424
+ };
425
+ if (standardizedImages && standardizedImages.length > 0) {
426
+ nanoBananaEditPayload.images = standardizedImages;
427
+ } else if (runpodOptions?.images) {
428
+ nanoBananaEditPayload.images = runpodOptions.images;
429
+ }
430
+ return nanoBananaEditPayload;
431
+ }
432
+ const isQwenImageEdit2511 = this.modelId.includes("qwen-image-edit-2511");
433
+ if (isQwenImageEdit2511) {
434
+ const qwenEdit2511Payload = {
435
+ prompt,
436
+ size: runpodSize,
437
+ seed: seed ?? -1,
438
+ output_format: runpodOptions?.output_format ?? "jpeg",
439
+ enable_base64_output: runpodOptions?.enable_base64_output ?? false,
440
+ enable_sync_mode: runpodOptions?.enable_sync_mode ?? false,
441
+ ...runpodOptions
442
+ };
443
+ if (standardizedImages && standardizedImages.length > 0) {
444
+ qwenEdit2511Payload.images = standardizedImages;
445
+ } else if (runpodOptions?.images) {
446
+ qwenEdit2511Payload.images = runpodOptions.images;
447
+ }
448
+ return qwenEdit2511Payload;
449
+ }
450
+ const isWanModel = this.modelId.includes("wan-2");
451
+ if (isWanModel) {
452
+ return {
453
+ prompt,
454
+ size: runpodSize,
455
+ seed: seed ?? -1,
456
+ enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,
457
+ ...runpodOptions
458
+ };
459
+ }
409
460
  const defaultPayload = {
410
461
  prompt,
411
462
  negative_prompt: runpodOptions?.negative_prompt ?? "",
@@ -604,6 +655,8 @@ var MODEL_ID_TO_ENDPOINT_URL = {
604
655
  var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
605
656
  "qwen/qwen-image": "https://api.runpod.ai/v2/qwen-image-t2i",
606
657
  "qwen/qwen-image-edit": "https://api.runpod.ai/v2/qwen-image-edit",
658
+ "qwen/qwen-image-edit-2511": "https://api.runpod.ai/v2/qwen-image-edit-2511",
659
+ "qwen/qwen-image-edit-2511-lora": "https://api.runpod.ai/v2/qwen-image-edit-2511-lora",
607
660
  "bytedance/seedream-3.0": "https://api.runpod.ai/v2/seedream-3-0-t2i",
608
661
  // Seadream v4 (t2i and edit)
609
662
  "bytedance/seedream-4.0": "https://api.runpod.ai/v2/seedream-v4-t2i",
@@ -611,8 +664,12 @@ var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
611
664
  "black-forest-labs/flux-1-kontext-dev": "https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev",
612
665
  "black-forest-labs/flux-1-schnell": "https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell",
613
666
  "black-forest-labs/flux-1-dev": "https://api.runpod.ai/v2/black-forest-labs-flux-1-dev",
667
+ // Alibaba Wan 2.6 (t2i)
668
+ "alibaba/wan-2.6": "https://api.runpod.ai/v2/wan-2-6-t2i",
614
669
  // Nano Banana (edit only)
670
+ "google/nano-banana-edit": "https://api.runpod.ai/v2/nano-banana-edit",
615
671
  "nano-banana-edit": "https://api.runpod.ai/v2/nano-banana-edit",
672
+ // backwards compatibility
616
673
  // Nano Banana Pro (edit only)
617
674
  "google/nano-banana-pro-edit": "https://api.runpod.ai/v2/nano-banana-pro-edit",
618
675
  // Pruna (t2i and edit)
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/runpod-provider.ts","../src/runpod-image-model.ts","../src/runpod-error.ts","../src/runpod-speech-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';\nexport type { RunpodImageErrorData } from './runpod-error';\n","import { ImageModelV3, LanguageModelV3, SpeechModelV3 } from '@ai-sdk/provider';\nimport {\n OpenAICompatibleChatLanguageModel,\n OpenAICompatibleCompletionLanguageModel,\n} from '@ai-sdk/openai-compatible';\nimport {\n FetchFunction,\n loadApiKey,\n withoutTrailingSlash,\n} from '@ai-sdk/provider-utils';\nimport { RunpodImageModel } from './runpod-image-model';\nimport { RunpodSpeechModel } from './runpod-speech-model';\n\nexport interface RunpodProviderSettings {\n /**\nRunpod API key.\n*/\n apiKey?: string;\n /**\nCustom base URL for Runpod API. Use this to point to custom endpoints or different Runpod deployments.\nExample: 'https://api.runpod.ai/v2/your-endpoint-id/openai/v1'\n*/\n baseURL?: string;\n /**\nCustom headers to include in the requests.\n*/\n headers?: Record<string, string>;\n /**\nCustom fetch implementation. You can use it as a middleware to intercept requests,\nor to provide a custom fetch implementation for e.g. testing.\n*/\n fetch?: FetchFunction;\n}\n\nexport interface RunpodProvider {\n /**\nCreates a model for text generation.\n*/\n (modelId: string): LanguageModelV3;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: string): LanguageModelV3;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: string): LanguageModelV3;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: string): LanguageModelV3;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: string): ImageModelV3;\n\n /**\nCreates an image model for image generation.\n*/\n image(modelId: string): ImageModelV3;\n\n /**\nCreates a speech model for speech generation.\n*/\n speechModel(modelId: string): SpeechModelV3;\n\n /**\nCreates a speech model for speech generation.\n*/\n speech(modelId: string): SpeechModelV3;\n}\n\n// Mapping of Runpod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n 'ibm-granite/granite-4.0-h-small':\n 'https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1',\n 'gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'openai/gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'deepcogito/cogito-671b-v2.1-fp8':\n 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n 'deepcogito/cogito-671b-v2.1-FP8':\n 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n};\n\n// Mapping of Runpod image model IDs to their endpoint URLs\nconst IMAGE_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen-image': 'https://api.runpod.ai/v2/qwen-image-t2i',\n 'qwen/qwen-image-edit': 'https://api.runpod.ai/v2/qwen-image-edit',\n 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\n // Seadream v4 (t2i and edit)\n 'bytedance/seedream-4.0': 'https://api.runpod.ai/v2/seedream-v4-t2i',\n 'bytedance/seedream-4.0-edit': 'https://api.runpod.ai/v2/seedream-v4-edit',\n 'black-forest-labs/flux-1-kontext-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev',\n 'black-forest-labs/flux-1-schnell':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell',\n 'black-forest-labs/flux-1-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-dev',\n // Nano Banana (edit only)\n 'nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit',\n // Nano Banana Pro (edit only)\n 'google/nano-banana-pro-edit':\n 'https://api.runpod.ai/v2/nano-banana-pro-edit',\n // Pruna (t2i and edit)\n 'pruna/p-image-t2i': 'https://api.runpod.ai/v2/p-image-t2i',\n 'pruna/p-image-edit': 'https://api.runpod.ai/v2/p-image-edit',\n};\n\n// Mapping of Runpod speech model IDs to their serverless endpoint URLs\n// Note: This is intentionally a temporary mapping for a stealth release.\nconst SPEECH_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'resembleai/chatterbox-turbo': 'https://api.runpod.ai/v2/chatterbox-turbo/',\n};\n\n// Mapping of Runpod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\n 'deepcogito/cogito-671b-v2.1-fp8': 'deepcogito/cogito-671b-v2.1-FP8',\n 'deepcogito/cogito-671b-v2.1-FP8': 'deepcogito/cogito-671b-v2.1-FP8',\n};\n\n/**\n * Derives the endpoint URL for a model by replacing slashes with hyphens.\n * Example: 'ibm-granite/granite-4.0-h-small' -> 'https://api.runpod.ai/v2/ibm-granite-granite-4.0-h-small/openai/v1'\n */\nfunction deriveEndpointURL(modelId: string): string {\n const normalized = modelId.replace(/\\//g, '-');\n return `https://api.runpod.ai/v2/${normalized}/openai/v1`;\n}\n\nfunction parseRunpodConsoleEndpointId(modelIdOrUrl: string): string | null {\n if (!modelIdOrUrl.startsWith('http')) {\n return null;\n }\n\n try {\n const url = new URL(modelIdOrUrl);\n if (url.hostname !== 'console.runpod.io') {\n return null;\n }\n\n // Example:\n // https://console.runpod.io/serverless/user/endpoint/<ENDPOINT_ID>\n const parts = url.pathname.split('/').filter(Boolean);\n const idx = parts.lastIndexOf('endpoint');\n const endpointId = idx !== -1 ? parts[idx + 1] : undefined;\n\n return endpointId || null;\n } catch {\n return null;\n }\n}\n\nexport function createRunpod(\n options: RunpodProviderSettings = {}\n): RunpodProvider {\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'RUNPOD_API_KEY',\n description: 'Runpod',\n })}`,\n ...options.headers,\n });\n\n const runpodFetch: FetchFunction = async (url, requestInit) => {\n if (requestInit?.body) {\n try {\n const body = JSON.parse(requestInit.body as string);\n if (body.stream === true && !body.stream_options) {\n body.stream_options = { include_usage: true };\n requestInit.body = JSON.stringify(body);\n }\n } catch {}\n }\n const fetchFn = options.fetch || fetch;\n return fetchFn(url, requestInit);\n };\n\n interface CommonModelConfig {\n provider: string;\n url: ({ path }: { path: string }) => string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n }\n\n const getModelConfig = (\n modelId: string,\n modelType: string\n ): CommonModelConfig => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: runpodFetch,\n };\n };\n\n const createChatModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'chat'),\n includeUsage: true,\n });\n };\n\n const createCompletionModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'completion'),\n includeUsage: true,\n });\n };\n\n const createImageModel = (modelId: string) => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL =\n IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return new RunpodImageModel(modelId, {\n provider: 'runpod.image',\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n });\n };\n\n const createSpeechModel = (modelId: string) => {\n const endpointIdFromConsole = parseRunpodConsoleEndpointId(modelId);\n const normalizedModelId = endpointIdFromConsole ?? modelId;\n\n // Prefer explicit mapping for known speech model IDs.\n const mappedBaseURL = SPEECH_MODEL_ID_TO_ENDPOINT_URL[normalizedModelId];\n\n const baseURL =\n mappedBaseURL ??\n (normalizedModelId.startsWith('http')\n ? normalizedModelId\n : `https://api.runpod.ai/v2/${normalizedModelId}`);\n\n return new RunpodSpeechModel(normalizedModelId, {\n provider: 'runpod.speech',\n baseURL,\n headers: getHeaders,\n fetch: runpodFetch,\n });\n };\n\n const provider = (modelId: string) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n provider.imageModel = createImageModel;\n provider.image = createImageModel;\n provider.speechModel = createSpeechModel;\n provider.speech = createSpeechModel;\n\n return provider;\n}\n\nexport const runpod = createRunpod();\n","import {\n ImageModelV3,\n ImageModelV3CallOptions,\n ImageModelV3File,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createJsonResponseHandler,\n createBinaryResponseHandler,\n FetchFunction,\n postJsonToApi,\n getFromApi,\n} from '@ai-sdk/provider-utils';\nimport { InvalidArgumentError } from '@ai-sdk/provider';\nimport { z } from 'zod';\nimport { runpodImageFailedResponseHandler } from './runpod-error';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ninterface RunpodImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\n// Runpod supported aspect ratios (only validated working sizes)\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\n '1:1': '1328*1328', // ✅ Native support\n '4:3': '1472*1140', // ✅ Native support\n '3:4': '1140*1472', // ✅ Native support\n};\n\n// Runpod supported sizes (validated working sizes)\nconst SUPPORTED_SIZES = new Set([\n // Native aspect ratio sizes\n '1328*1328', // 1:1\n '1472*1140', // 4:3\n '1140*1472', // 3:4\n // Additional validated sizes\n '512*512',\n '768*768',\n '1024*1024',\n '1536*1536',\n '2048*2048',\n '4096*4096',\n '512*768',\n '768*512',\n '1024*768',\n '768*1024',\n]);\n\nexport class RunpodImageModel implements ImageModelV3 {\n readonly specificationVersion = 'v3';\n readonly maxImagesPerCall = 1;\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private config: RunpodImageModelConfig\n ) {}\n\n async doGenerate({\n prompt,\n n,\n size,\n aspectRatio,\n seed,\n files,\n mask,\n providerOptions,\n headers,\n abortSignal,\n }: ImageModelV3CallOptions): Promise<\n Awaited<ReturnType<ImageModelV3['doGenerate']>>\n > {\n const warnings: Array<SharedV3Warning> = [];\n\n // Convert standardized files to Runpod format (base64 data URLs or raw base64)\n const standardizedImages = this.convertFilesToRunpodFormat(files);\n\n if (mask) {\n warnings.push({\n type: 'unsupported',\n feature: 'mask',\n details: 'Mask input for inpainting is not yet supported.',\n });\n }\n\n // Check if this is a Pruna model (skip standard size/aspectRatio validation)\n const isPrunaModel =\n this.modelId.includes('pruna') || this.modelId.includes('p-image');\n\n // Check if this is a Nano Banana Pro model (skip standard size/aspectRatio validation)\n const isNanoBananaProModel = this.modelId.includes('nano-banana-pro');\n\n // Determine the size to use\n let runpodSize: string;\n\n if (isPrunaModel || isNanoBananaProModel) {\n // These models use aspect_ratio string directly, skip size validation\n // Pass through the aspectRatio or use default, validation happens at API level\n runpodSize = aspectRatio || '1:1';\n } else if (size) {\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\n const runpodSizeCandidate = size.replace('x', '*');\n\n // Validate size is supported\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\n throw new InvalidArgumentError({\n argument: 'size',\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\n SUPPORTED_SIZES\n )\n .map((s) => s.replace('*', 'x'))\n .join(', ')}`,\n });\n }\n\n runpodSize = runpodSizeCandidate;\n } else if (aspectRatio) {\n // Validate aspect ratio is supported\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\n throw new InvalidArgumentError({\n argument: 'aspectRatio',\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\n });\n }\n\n // Use supported aspect ratio mapping\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\n } else {\n // Default to square format\n runpodSize = '1328*1328';\n }\n\n // Handle multiple images warning\n if (n > 1) {\n warnings.push({\n type: 'unsupported',\n feature: 'multiple images (n > 1)',\n details:\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\n });\n }\n\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n // Runpod uses a different request format - /runsync endpoint with input wrapper\n const inputPayload = this.buildInputPayload(\n prompt ?? '',\n runpodSize,\n seed,\n providerOptions.runpod as Record<string, unknown> | undefined,\n aspectRatio,\n standardizedImages\n );\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url: `${this.config.baseURL}/runsync`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n input: inputPayload,\n },\n failedResponseHandler: runpodImageFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageResponseSchema as any\n ),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n // Handle both sync and async responses from Runpod\n const typedResponse = response as any;\n if (\n typedResponse.status === 'COMPLETED' &&\n (typedResponse.output?.result || typedResponse.output?.image_url)\n ) {\n // Sync response - image is ready\n // Different models use different response formats: result vs image_url\n const imageUrl =\n typedResponse.output.result || typedResponse.output.image_url;\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n cost: typedResponse.output?.cost,\n },\n ],\n },\n },\n };\n } else if (\n typedResponse.status === 'IN_QUEUE' ||\n typedResponse.status === 'IN_PROGRESS'\n ) {\n // Async response - need to poll for completion\n const pollOptions = {\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\n pollIntervalMillis: providerOptions.runpod\n ?.pollIntervalMillis as number,\n };\n const imageUrl = await this.pollForCompletion(\n typedResponse.id,\n abortSignal,\n pollOptions\n );\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n jobId: typedResponse.id,\n },\n ],\n },\n },\n };\n } else {\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\n }\n }\n\n private async downloadImage(\n imageUrl: string,\n abortSignal?: AbortSignal\n ): Promise<Uint8Array> {\n const { value: imageData } = await getFromApi({\n url: imageUrl,\n successfulResponseHandler: createBinaryResponseHandler(),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n return imageData;\n }\n\n private async pollForCompletion(\n jobId: string,\n abortSignal?: AbortSignal,\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number }\n ): Promise<string> {\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (abortSignal?.aborted) {\n throw new Error('Image generation was aborted');\n }\n\n const { value: statusResponse } = await getFromApi({\n url: `${this.config.baseURL}/status/${jobId}`,\n headers: this.config.headers(),\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageStatusSchema as any\n ),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n\n const typedStatusResponse = statusResponse as any;\n if (\n typedStatusResponse.status === 'COMPLETED' &&\n (typedStatusResponse.output?.result ||\n typedStatusResponse.output?.image_url)\n ) {\n return (\n typedStatusResponse.output.result ||\n typedStatusResponse.output.image_url\n );\n }\n\n if (typedStatusResponse.status === 'FAILED') {\n throw new Error(\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\n );\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new Error(\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\n );\n }\n\n /**\n * Converts standardized ImageModelV3File[] to Runpod-compatible format.\n * Returns URLs or base64 data URLs that Runpod API accepts.\n */\n private convertFilesToRunpodFormat(\n files: ImageModelV3File[] | undefined\n ): string[] | undefined {\n if (!files || files.length === 0) {\n return undefined;\n }\n\n return files.map((file) => {\n // Handle URL type - return URL directly\n if (file.type === 'url') {\n return file.url;\n }\n\n // Handle file type with data\n if (typeof file.data === 'string') {\n // If it's already a data URL, return as-is\n if (file.data.startsWith('data:')) {\n return file.data;\n }\n // Otherwise, wrap as data URL with media type\n return `data:${file.mediaType};base64,${file.data}`;\n }\n\n // Convert Uint8Array to base64 data URL\n const base64 = this.uint8ArrayToBase64(file.data);\n return `data:${file.mediaType};base64,${base64}`;\n });\n }\n\n /**\n * Converts Uint8Array to base64 string.\n */\n private uint8ArrayToBase64(data: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]);\n }\n return btoa(binary);\n }\n\n private buildInputPayload(\n prompt: string,\n runpodSize: string,\n seed?: number,\n runpodOptions?: Record<string, unknown>,\n aspectRatio?: string,\n standardizedImages?: string[]\n ): Record<string, unknown> {\n // Check if this is a Flux model that uses different parameters\n const isFluxModel =\n this.modelId.includes('flux') ||\n this.modelId.includes('black-forest-labs');\n\n if (isFluxModel) {\n // Check if this is Flux Kontext (uses different parameters)\n const isKontext = this.modelId.includes('kontext');\n\n if (isKontext) {\n // Flux Kontext uses size format and has image input\n // Prioritize standardized files over providerOptions\n const kontextPayload: Record<string, unknown> = {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: 28,\n guidance: 2,\n size: runpodSize,\n output_format: 'png',\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n\n // Use standardized files if provided (first image), otherwise use providerOptions.image\n if (standardizedImages && standardizedImages.length > 0) {\n kontextPayload.image = standardizedImages[0];\n }\n\n return kontextPayload;\n } else {\n // Regular Flux models use width/height\n const [width, height] = runpodSize.split('*').map(Number);\n\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\n guidance: this.modelId.includes('schnell') ? 7 : 2,\n width,\n height,\n image_format: 'png',\n ...runpodOptions,\n };\n }\n }\n\n // Check if this is a Pruna model\n const isPrunaModel =\n this.modelId.includes('pruna') || this.modelId.includes('p-image');\n if (isPrunaModel) {\n const isPrunaEdit = this.modelId.includes('edit');\n\n if (isPrunaEdit) {\n // Pruna image edit\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\"\n // Supports 1-5 images via providerOptions.runpod.images\n const editPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n disable_safety_checker:\n (runpodOptions?.disable_safety_checker as boolean) ?? false,\n };\n\n // Add seed if provided\n if (seed !== undefined) {\n editPayload.seed = seed;\n } else if (runpodOptions?.seed !== undefined) {\n editPayload.seed = runpodOptions.seed;\n }\n\n // Use standardized files if provided, otherwise use providerOptions.images\n if (standardizedImages && standardizedImages.length > 0) {\n editPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n editPayload.images = runpodOptions.images;\n }\n\n return editPayload;\n } else {\n // Pruna text-to-image\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"custom\"\n // For custom: width/height 256-1440, must be multiple of 16\n const t2iPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n disable_safety_checker:\n (runpodOptions?.disable_safety_checker as boolean) ?? false,\n };\n\n // Add seed if provided\n if (seed !== undefined) {\n t2iPayload.seed = seed;\n } else if (runpodOptions?.seed !== undefined) {\n t2iPayload.seed = runpodOptions.seed;\n }\n\n // Handle custom aspect ratio with width/height\n if (t2iPayload.aspect_ratio === 'custom') {\n if (runpodOptions?.width) {\n t2iPayload.width = runpodOptions.width;\n }\n if (runpodOptions?.height) {\n t2iPayload.height = runpodOptions.height;\n }\n }\n\n return t2iPayload;\n }\n }\n\n // Check if this is a Nano Banana Pro model (google/nano-banana-pro-edit)\n const isNanaBananaProModel = this.modelId.includes('nano-banana-pro');\n if (isNanaBananaProModel) {\n // Nano Banana Pro image edit\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"21:9\", \"9:21\"\n // Supported resolution: \"1k\", \"2k\", \"4k\"\n // Supported output_format: \"jpeg\", \"png\", \"webp\"\n const nanoBananaPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n resolution: (runpodOptions?.resolution as string) ?? '1k',\n output_format: (runpodOptions?.output_format as string) ?? 'jpeg',\n enable_base64_output:\n (runpodOptions?.enable_base64_output as boolean) ?? false,\n enable_sync_mode:\n (runpodOptions?.enable_sync_mode as boolean) ?? false,\n };\n\n // Use standardized files if provided, otherwise use providerOptions.images\n if (standardizedImages && standardizedImages.length > 0) {\n nanoBananaPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n nanoBananaPayload.images = runpodOptions.images;\n }\n\n return nanoBananaPayload;\n }\n\n // Default format for Qwen and other models\n const defaultPayload: Record<string, unknown> = {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n\n // For edit models, use standardized files if provided\n if (standardizedImages && standardizedImages.length > 0) {\n // Single image models use 'image', multi-image models use 'images'\n if (standardizedImages.length === 1) {\n defaultPayload.image = standardizedImages[0];\n } else {\n defaultPayload.images = standardizedImages;\n }\n }\n\n return defaultPayload;\n }\n}\n\n// Runpod image API response schema (handles both sync and async responses)\nconst runpodImageResponseSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n delayTime: z.number().optional(),\n executionTime: z.number().optional(),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(), // URL to the generated image (Qwen format)\n image_url: z.string().optional(), // URL to the generated image (Flux format)\n })\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\n});\n\n// Schema for polling status endpoint\nconst runpodImageStatusSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(),\n image_url: z.string().optional(),\n })\n .optional(),\n error: z.string().optional(), // Error message if FAILED\n});\n","import { z } from 'zod';\r\nimport { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';\r\n\r\n// Runpod image API error schema (supports both error formats)\r\nexport const runpodImageErrorSchema = z.object({\r\n error: z.string().optional(),\r\n message: z.string().optional(),\r\n});\r\n\r\nexport type RunpodImageErrorData = z.infer<typeof runpodImageErrorSchema>;\r\n\r\nexport const runpodImageFailedResponseHandler = createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: RunpodImageErrorData) => {\r\n // Prefer message if available (more descriptive)\r\n if (data.message) {\r\n return data.message;\r\n }\r\n \r\n // If error field exists, try to extract nested JSON message\r\n if (data.error) {\r\n // Runpod sometimes returns nested JSON in the error field like:\r\n // \"Error submitting task: 400, {\\\"code\\\":400,\\\"message\\\":\\\"...\\\"}\"\r\n // Try to extract the inner message for cleaner error messages\r\n // Find the last occurrence of { which likely starts the JSON object\r\n const lastBraceIndex = data.error.lastIndexOf('{');\r\n if (lastBraceIndex !== -1) {\r\n try {\r\n const jsonStr = data.error.substring(lastBraceIndex);\r\n const nestedError = JSON.parse(jsonStr);\r\n if (nestedError.message && typeof nestedError.message === 'string') {\r\n return nestedError.message;\r\n }\r\n } catch {\r\n // If parsing fails, fall back to the original error string\r\n }\r\n }\r\n return data.error;\r\n }\r\n \r\n return 'Unknown Runpod error';\r\n },\r\n});\r\n\r\n","import {\n JSONValue,\n SpeechModelV3,\n SpeechModelV3CallOptions,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport { FetchFunction, withoutTrailingSlash } from '@ai-sdk/provider-utils';\n\nexport interface RunpodSpeechModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction replaceNewlinesWithSpaces(value: string): string {\n return value.replace(/[\\r\\n]+/g, ' ');\n}\n\nexport class RunpodSpeechModel implements SpeechModelV3 {\n readonly specificationVersion = 'v3';\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private readonly config: RunpodSpeechModelConfig\n ) {}\n\n private getRunpodRunSyncUrl(): string {\n // `withoutTrailingSlash` is typed to potentially return undefined, but in our\n // case `config.baseURL` is always a string.\n const baseURL =\n withoutTrailingSlash(this.config.baseURL) ?? this.config.baseURL;\n\n // Allow users to pass /run or /runsync directly.\n if (baseURL.endsWith('/run') || baseURL.endsWith('/runsync')) {\n return baseURL;\n }\n\n return `${baseURL}/runsync`;\n }\n\n async doGenerate(\n options: SpeechModelV3CallOptions\n ): Promise<Awaited<ReturnType<SpeechModelV3['doGenerate']>>> {\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n const warnings: SharedV3Warning[] = [];\n\n const {\n text,\n voice,\n outputFormat,\n instructions,\n speed,\n language,\n providerOptions,\n abortSignal,\n headers,\n } = options;\n\n // This endpoint currently returns wav. Warn and ignore other formats.\n if (outputFormat != null && outputFormat !== 'wav') {\n warnings.push({\n type: 'unsupported',\n feature: 'outputFormat',\n details: `Unsupported outputFormat: ${outputFormat}. This endpoint returns 'wav'.`,\n });\n }\n\n if (instructions != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'instructions',\n details: `Instructions are not supported by this speech endpoint.`,\n });\n }\n\n if (speed != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'speed',\n details: `Speed is not supported by this speech endpoint.`,\n });\n }\n\n if (language != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'language',\n details: `Language selection is not supported by this speech endpoint.`,\n });\n }\n\n const runpodProviderOptions = isRecord(providerOptions)\n ? (providerOptions as any).runpod\n : undefined;\n\n const voiceUrl =\n isRecord(runpodProviderOptions) &&\n (typeof runpodProviderOptions.voice_url === 'string' ||\n typeof runpodProviderOptions.voiceUrl === 'string')\n ? (runpodProviderOptions.voice_url ??\n runpodProviderOptions.voiceUrl ??\n undefined)\n : undefined;\n\n const input: Record<string, unknown> = {\n prompt: replaceNewlinesWithSpaces(text),\n };\n\n // The endpoint supports either a built-in voice name or a voice_url prompt.\n if (voiceUrl) {\n input.voice_url = voiceUrl;\n } else if (voice) {\n input.voice = voice;\n }\n\n const requestBody = { input };\n const url = this.getRunpodRunSyncUrl();\n\n const fetchFn = this.config.fetch ?? fetch;\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.config.headers(),\n };\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (value != null) {\n requestHeaders[key] = value;\n }\n }\n }\n\n const response = await fetchFn(url, {\n method: 'POST',\n headers: requestHeaders,\n body: JSON.stringify(requestBody),\n signal: abortSignal,\n });\n\n const responseHeaders = Object.fromEntries(response.headers.entries());\n const rawBodyText = await response.text();\n\n let parsed: any = undefined;\n try {\n parsed = rawBodyText ? JSON.parse(rawBodyText) : undefined;\n } catch {\n // ignore - we'll surface the raw body in the error below\n }\n\n if (!response.ok) {\n const message =\n (parsed && typeof parsed.error === 'string' && parsed.error) ||\n rawBodyText ||\n `HTTP ${response.status}`;\n throw new Error(`Runpod speech request failed: ${message}`);\n }\n\n const output = parsed?.output ?? parsed;\n\n const audioUrl = output?.audio_url;\n if (typeof audioUrl !== 'string' || audioUrl.length === 0) {\n throw new Error('Runpod speech response did not include an audio_url.');\n }\n\n const audioResponse = await fetchFn(audioUrl, { signal: abortSignal });\n if (!audioResponse.ok) {\n throw new Error(\n `Failed to download generated audio (${audioResponse.status}).`\n );\n }\n\n const audio = new Uint8Array(await audioResponse.arrayBuffer());\n\n const providerMetadata: Record<string, Record<string, JSONValue>> = {\n runpod: {\n audioUrl,\n ...(typeof output?.cost === 'number' ? { cost: output.cost } : {}),\n },\n };\n\n return {\n audio,\n warnings,\n request: {\n body: JSON.stringify(requestBody),\n },\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders as any,\n body: rawBodyText,\n },\n providerMetadata,\n };\n }\n}\n"],"mappings":"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","names":["import_provider_utils","import_provider_utils","import_zod","import_provider_utils"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/runpod-provider.ts","../src/runpod-image-model.ts","../src/runpod-error.ts","../src/runpod-speech-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';\nexport type { RunpodImageErrorData } from './runpod-error';\n","import { ImageModelV3, LanguageModelV3, SpeechModelV3 } from '@ai-sdk/provider';\nimport {\n OpenAICompatibleChatLanguageModel,\n OpenAICompatibleCompletionLanguageModel,\n} from '@ai-sdk/openai-compatible';\nimport {\n FetchFunction,\n loadApiKey,\n withoutTrailingSlash,\n} from '@ai-sdk/provider-utils';\nimport { RunpodImageModel } from './runpod-image-model';\nimport { RunpodSpeechModel } from './runpod-speech-model';\n\nexport interface RunpodProviderSettings {\n /**\nRunpod API key.\n*/\n apiKey?: string;\n /**\nCustom base URL for Runpod API. Use this to point to custom endpoints or different Runpod deployments.\nExample: 'https://api.runpod.ai/v2/your-endpoint-id/openai/v1'\n*/\n baseURL?: string;\n /**\nCustom headers to include in the requests.\n*/\n headers?: Record<string, string>;\n /**\nCustom fetch implementation. You can use it as a middleware to intercept requests,\nor to provide a custom fetch implementation for e.g. testing.\n*/\n fetch?: FetchFunction;\n}\n\nexport interface RunpodProvider {\n /**\nCreates a model for text generation.\n*/\n (modelId: string): LanguageModelV3;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: string): LanguageModelV3;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: string): LanguageModelV3;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: string): LanguageModelV3;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: string): ImageModelV3;\n\n /**\nCreates an image model for image generation.\n*/\n image(modelId: string): ImageModelV3;\n\n /**\nCreates a speech model for speech generation.\n*/\n speechModel(modelId: string): SpeechModelV3;\n\n /**\nCreates a speech model for speech generation.\n*/\n speech(modelId: string): SpeechModelV3;\n}\n\n// Mapping of Runpod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n 'ibm-granite/granite-4.0-h-small':\n 'https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1',\n 'gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'openai/gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'deepcogito/cogito-671b-v2.1-fp8':\n 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n 'deepcogito/cogito-671b-v2.1-FP8':\n 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n};\n\n// Mapping of Runpod image model IDs to their endpoint URLs\nconst IMAGE_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen-image': 'https://api.runpod.ai/v2/qwen-image-t2i',\n 'qwen/qwen-image-edit': 'https://api.runpod.ai/v2/qwen-image-edit',\n 'qwen/qwen-image-edit-2511': 'https://api.runpod.ai/v2/qwen-image-edit-2511',\n 'qwen/qwen-image-edit-2511-lora':\n 'https://api.runpod.ai/v2/qwen-image-edit-2511-lora',\n 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\n // Seadream v4 (t2i and edit)\n 'bytedance/seedream-4.0': 'https://api.runpod.ai/v2/seedream-v4-t2i',\n 'bytedance/seedream-4.0-edit': 'https://api.runpod.ai/v2/seedream-v4-edit',\n 'black-forest-labs/flux-1-kontext-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev',\n 'black-forest-labs/flux-1-schnell':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell',\n 'black-forest-labs/flux-1-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-dev',\n // Alibaba Wan 2.6 (t2i)\n 'alibaba/wan-2.6': 'https://api.runpod.ai/v2/wan-2-6-t2i',\n // Nano Banana (edit only)\n 'google/nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit',\n 'nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit', // backwards compatibility\n // Nano Banana Pro (edit only)\n 'google/nano-banana-pro-edit':\n 'https://api.runpod.ai/v2/nano-banana-pro-edit',\n // Pruna (t2i and edit)\n 'pruna/p-image-t2i': 'https://api.runpod.ai/v2/p-image-t2i',\n 'pruna/p-image-edit': 'https://api.runpod.ai/v2/p-image-edit',\n};\n\n// Mapping of Runpod speech model IDs to their serverless endpoint URLs\n// Note: This is intentionally a temporary mapping for a stealth release.\nconst SPEECH_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'resembleai/chatterbox-turbo': 'https://api.runpod.ai/v2/chatterbox-turbo/',\n};\n\n// Mapping of Runpod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\n 'deepcogito/cogito-671b-v2.1-fp8': 'deepcogito/cogito-671b-v2.1-FP8',\n 'deepcogito/cogito-671b-v2.1-FP8': 'deepcogito/cogito-671b-v2.1-FP8',\n};\n\n/**\n * Derives the endpoint URL for a model by replacing slashes with hyphens.\n * Example: 'ibm-granite/granite-4.0-h-small' -> 'https://api.runpod.ai/v2/ibm-granite-granite-4.0-h-small/openai/v1'\n */\nfunction deriveEndpointURL(modelId: string): string {\n const normalized = modelId.replace(/\\//g, '-');\n return `https://api.runpod.ai/v2/${normalized}/openai/v1`;\n}\n\nfunction parseRunpodConsoleEndpointId(modelIdOrUrl: string): string | null {\n if (!modelIdOrUrl.startsWith('http')) {\n return null;\n }\n\n try {\n const url = new URL(modelIdOrUrl);\n if (url.hostname !== 'console.runpod.io') {\n return null;\n }\n\n // Example:\n // https://console.runpod.io/serverless/user/endpoint/<ENDPOINT_ID>\n const parts = url.pathname.split('/').filter(Boolean);\n const idx = parts.lastIndexOf('endpoint');\n const endpointId = idx !== -1 ? parts[idx + 1] : undefined;\n\n return endpointId || null;\n } catch {\n return null;\n }\n}\n\nexport function createRunpod(\n options: RunpodProviderSettings = {}\n): RunpodProvider {\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'RUNPOD_API_KEY',\n description: 'Runpod',\n })}`,\n ...options.headers,\n });\n\n const runpodFetch: FetchFunction = async (url, requestInit) => {\n if (requestInit?.body) {\n try {\n const body = JSON.parse(requestInit.body as string);\n if (body.stream === true && !body.stream_options) {\n body.stream_options = { include_usage: true };\n requestInit.body = JSON.stringify(body);\n }\n } catch {}\n }\n const fetchFn = options.fetch || fetch;\n return fetchFn(url, requestInit);\n };\n\n interface CommonModelConfig {\n provider: string;\n url: ({ path }: { path: string }) => string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n }\n\n const getModelConfig = (\n modelId: string,\n modelType: string\n ): CommonModelConfig => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: runpodFetch,\n };\n };\n\n const createChatModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'chat'),\n includeUsage: true,\n });\n };\n\n const createCompletionModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'completion'),\n includeUsage: true,\n });\n };\n\n const createImageModel = (modelId: string) => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL =\n IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return new RunpodImageModel(modelId, {\n provider: 'runpod.image',\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n });\n };\n\n const createSpeechModel = (modelId: string) => {\n const endpointIdFromConsole = parseRunpodConsoleEndpointId(modelId);\n const normalizedModelId = endpointIdFromConsole ?? modelId;\n\n // Prefer explicit mapping for known speech model IDs.\n const mappedBaseURL = SPEECH_MODEL_ID_TO_ENDPOINT_URL[normalizedModelId];\n\n const baseURL =\n mappedBaseURL ??\n (normalizedModelId.startsWith('http')\n ? normalizedModelId\n : `https://api.runpod.ai/v2/${normalizedModelId}`);\n\n return new RunpodSpeechModel(normalizedModelId, {\n provider: 'runpod.speech',\n baseURL,\n headers: getHeaders,\n fetch: runpodFetch,\n });\n };\n\n const provider = (modelId: string) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n provider.imageModel = createImageModel;\n provider.image = createImageModel;\n provider.speechModel = createSpeechModel;\n provider.speech = createSpeechModel;\n\n return provider;\n}\n\nexport const runpod = createRunpod();\n","import {\n ImageModelV3,\n ImageModelV3CallOptions,\n ImageModelV3File,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createJsonResponseHandler,\n createBinaryResponseHandler,\n FetchFunction,\n postJsonToApi,\n getFromApi,\n} from '@ai-sdk/provider-utils';\nimport { InvalidArgumentError } from '@ai-sdk/provider';\nimport { z } from 'zod';\nimport { runpodImageFailedResponseHandler } from './runpod-error';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ninterface RunpodImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\n// Runpod supported aspect ratios (only validated working sizes)\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\n '1:1': '1328*1328', // ✅ Native support\n '4:3': '1472*1140', // ✅ Native support\n '3:4': '1140*1472', // ✅ Native support\n};\n\n// Runpod supported sizes (validated working sizes)\nconst SUPPORTED_SIZES = new Set([\n // Native aspect ratio sizes\n '1328*1328', // 1:1\n '1472*1140', // 4:3\n '1140*1472', // 3:4\n // Additional validated sizes\n '512*512',\n '768*768',\n '1024*1024',\n '1536*1536',\n '2048*2048',\n '4096*4096',\n '512*768',\n '768*512',\n '1024*768',\n '768*1024',\n]);\n\nexport class RunpodImageModel implements ImageModelV3 {\n readonly specificationVersion = 'v3';\n readonly maxImagesPerCall = 1;\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private config: RunpodImageModelConfig\n ) {}\n\n async doGenerate({\n prompt,\n n,\n size,\n aspectRatio,\n seed,\n files,\n mask,\n providerOptions,\n headers,\n abortSignal,\n }: ImageModelV3CallOptions): Promise<\n Awaited<ReturnType<ImageModelV3['doGenerate']>>\n > {\n const warnings: Array<SharedV3Warning> = [];\n\n // Convert standardized files to Runpod format (base64 data URLs or raw base64)\n const standardizedImages = this.convertFilesToRunpodFormat(files);\n\n if (mask) {\n warnings.push({\n type: 'unsupported',\n feature: 'mask',\n details: 'Mask input for inpainting is not yet supported.',\n });\n }\n\n // Check if this is a Pruna model (skip standard size/aspectRatio validation)\n const isPrunaModel =\n this.modelId.includes('pruna') || this.modelId.includes('p-image');\n\n // Check if this is a Nano Banana Pro model (skip standard size/aspectRatio validation)\n const isNanoBananaProModel = this.modelId.includes('nano-banana-pro');\n\n // Determine the size to use\n let runpodSize: string;\n\n if (isPrunaModel || isNanoBananaProModel) {\n // These models use aspect_ratio string directly, skip size validation\n // Pass through the aspectRatio or use default, validation happens at API level\n runpodSize = aspectRatio || '1:1';\n } else if (size) {\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\n const runpodSizeCandidate = size.replace('x', '*');\n\n // Validate size is supported\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\n throw new InvalidArgumentError({\n argument: 'size',\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\n SUPPORTED_SIZES\n )\n .map((s) => s.replace('*', 'x'))\n .join(', ')}`,\n });\n }\n\n runpodSize = runpodSizeCandidate;\n } else if (aspectRatio) {\n // Validate aspect ratio is supported\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\n throw new InvalidArgumentError({\n argument: 'aspectRatio',\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\n });\n }\n\n // Use supported aspect ratio mapping\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\n } else {\n // Default to square format\n runpodSize = '1328*1328';\n }\n\n // Handle multiple images warning\n if (n > 1) {\n warnings.push({\n type: 'unsupported',\n feature: 'multiple images (n > 1)',\n details:\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\n });\n }\n\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n // Runpod uses a different request format - /runsync endpoint with input wrapper\n const inputPayload = this.buildInputPayload(\n prompt ?? '',\n runpodSize,\n seed,\n providerOptions.runpod as Record<string, unknown> | undefined,\n aspectRatio,\n standardizedImages\n );\n\n // Determine the effective baseURL (may switch for LoRA models)\n let effectiveBaseURL = this.config.baseURL;\n const runpodOptions = providerOptions.runpod as\n | Record<string, unknown>\n | undefined;\n if (\n this.modelId.includes('qwen-image-edit-2511') &&\n !this.modelId.includes('lora') &&\n runpodOptions?.loras &&\n Array.isArray(runpodOptions.loras) &&\n runpodOptions.loras.length > 0\n ) {\n // Switch to LoRA endpoint when loras are provided\n effectiveBaseURL = this.config.baseURL.replace(\n 'qwen-image-edit-2511',\n 'qwen-image-edit-2511-lora'\n );\n }\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url: `${effectiveBaseURL}/runsync`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n input: inputPayload,\n },\n failedResponseHandler: runpodImageFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageResponseSchema as any\n ),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n // Handle both sync and async responses from Runpod\n const typedResponse = response as any;\n if (\n typedResponse.status === 'COMPLETED' &&\n (typedResponse.output?.result || typedResponse.output?.image_url)\n ) {\n // Sync response - image is ready\n // Different models use different response formats: result vs image_url\n const imageUrl =\n typedResponse.output.result || typedResponse.output.image_url;\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n cost: typedResponse.output?.cost,\n },\n ],\n },\n },\n };\n } else if (\n typedResponse.status === 'IN_QUEUE' ||\n typedResponse.status === 'IN_PROGRESS'\n ) {\n // Async response - need to poll for completion\n const pollOptions = {\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\n pollIntervalMillis: providerOptions.runpod\n ?.pollIntervalMillis as number,\n };\n const imageUrl = await this.pollForCompletion(\n typedResponse.id,\n abortSignal,\n pollOptions,\n effectiveBaseURL\n );\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n jobId: typedResponse.id,\n },\n ],\n },\n },\n };\n } else {\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\n }\n }\n\n private async downloadImage(\n imageUrl: string,\n abortSignal?: AbortSignal\n ): Promise<Uint8Array> {\n const { value: imageData } = await getFromApi({\n url: imageUrl,\n successfulResponseHandler: createBinaryResponseHandler(),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n return imageData;\n }\n\n private async pollForCompletion(\n jobId: string,\n abortSignal?: AbortSignal,\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number },\n effectiveBaseURL?: string\n ): Promise<string> {\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\n const baseURL = effectiveBaseURL ?? this.config.baseURL;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (abortSignal?.aborted) {\n throw new Error('Image generation was aborted');\n }\n\n const { value: statusResponse } = await getFromApi({\n url: `${baseURL}/status/${jobId}`,\n headers: this.config.headers(),\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageStatusSchema as any\n ),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n\n const typedStatusResponse = statusResponse as any;\n if (\n typedStatusResponse.status === 'COMPLETED' &&\n (typedStatusResponse.output?.result ||\n typedStatusResponse.output?.image_url)\n ) {\n return (\n typedStatusResponse.output.result ||\n typedStatusResponse.output.image_url\n );\n }\n\n if (typedStatusResponse.status === 'FAILED') {\n throw new Error(\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\n );\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new Error(\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\n );\n }\n\n /**\n * Converts standardized ImageModelV3File[] to Runpod-compatible format.\n * Returns URLs or base64 data URLs that Runpod API accepts.\n */\n private convertFilesToRunpodFormat(\n files: ImageModelV3File[] | undefined\n ): string[] | undefined {\n if (!files || files.length === 0) {\n return undefined;\n }\n\n return files.map((file) => {\n // Handle URL type - return URL directly\n if (file.type === 'url') {\n return file.url;\n }\n\n // Handle file type with data\n if (typeof file.data === 'string') {\n // If it's already a data URL, return as-is\n if (file.data.startsWith('data:')) {\n return file.data;\n }\n // Otherwise, wrap as data URL with media type\n return `data:${file.mediaType};base64,${file.data}`;\n }\n\n // Convert Uint8Array to base64 data URL\n const base64 = this.uint8ArrayToBase64(file.data);\n return `data:${file.mediaType};base64,${base64}`;\n });\n }\n\n /**\n * Converts Uint8Array to base64 string.\n */\n private uint8ArrayToBase64(data: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]);\n }\n return btoa(binary);\n }\n\n private buildInputPayload(\n prompt: string,\n runpodSize: string,\n seed?: number,\n runpodOptions?: Record<string, unknown>,\n aspectRatio?: string,\n standardizedImages?: string[]\n ): Record<string, unknown> {\n // Check if this is a Flux model that uses different parameters\n const isFluxModel =\n this.modelId.includes('flux') ||\n this.modelId.includes('black-forest-labs');\n\n if (isFluxModel) {\n // Check if this is Flux Kontext (uses different parameters)\n const isKontext = this.modelId.includes('kontext');\n\n if (isKontext) {\n // Flux Kontext uses size format and has image input\n // Prioritize standardized files over providerOptions\n const kontextPayload: Record<string, unknown> = {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: 28,\n guidance: 2,\n size: runpodSize,\n output_format: 'png',\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n\n // Use standardized files if provided (first image), otherwise use providerOptions.image\n if (standardizedImages && standardizedImages.length > 0) {\n kontextPayload.image = standardizedImages[0];\n }\n\n return kontextPayload;\n } else {\n // Regular Flux models use width/height\n const [width, height] = runpodSize.split('*').map(Number);\n\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\n guidance: this.modelId.includes('schnell') ? 7 : 2,\n width,\n height,\n image_format: 'png',\n ...runpodOptions,\n };\n }\n }\n\n // Check if this is a Pruna model\n const isPrunaModel =\n this.modelId.includes('pruna') || this.modelId.includes('p-image');\n if (isPrunaModel) {\n const isPrunaEdit = this.modelId.includes('edit');\n\n if (isPrunaEdit) {\n // Pruna image edit\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\"\n // Supports 1-5 images via providerOptions.runpod.images\n const editPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n disable_safety_checker:\n (runpodOptions?.disable_safety_checker as boolean) ?? false,\n };\n\n // Add seed if provided\n if (seed !== undefined) {\n editPayload.seed = seed;\n } else if (runpodOptions?.seed !== undefined) {\n editPayload.seed = runpodOptions.seed;\n }\n\n // Use standardized files if provided, otherwise use providerOptions.images\n if (standardizedImages && standardizedImages.length > 0) {\n editPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n editPayload.images = runpodOptions.images;\n }\n\n return editPayload;\n } else {\n // Pruna text-to-image\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"custom\"\n // For custom: width/height 256-1440, must be multiple of 16\n const t2iPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n disable_safety_checker:\n (runpodOptions?.disable_safety_checker as boolean) ?? false,\n };\n\n // Add seed if provided\n if (seed !== undefined) {\n t2iPayload.seed = seed;\n } else if (runpodOptions?.seed !== undefined) {\n t2iPayload.seed = runpodOptions.seed;\n }\n\n // Handle custom aspect ratio with width/height\n if (t2iPayload.aspect_ratio === 'custom') {\n if (runpodOptions?.width) {\n t2iPayload.width = runpodOptions.width;\n }\n if (runpodOptions?.height) {\n t2iPayload.height = runpodOptions.height;\n }\n }\n\n return t2iPayload;\n }\n }\n\n // Check if this is a Nano Banana Pro model (google/nano-banana-pro-edit)\n const isNanaBananaProModel = this.modelId.includes('nano-banana-pro');\n if (isNanaBananaProModel) {\n // Nano Banana Pro image edit\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"21:9\", \"9:21\"\n // Supported resolution: \"1k\", \"2k\", \"4k\"\n // Supported output_format: \"jpeg\", \"png\", \"webp\"\n const nanoBananaPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n resolution: (runpodOptions?.resolution as string) ?? '1k',\n output_format: (runpodOptions?.output_format as string) ?? 'jpeg',\n enable_base64_output:\n (runpodOptions?.enable_base64_output as boolean) ?? false,\n enable_sync_mode:\n (runpodOptions?.enable_sync_mode as boolean) ?? false,\n };\n\n // Use standardized files if provided, otherwise use providerOptions.images\n if (standardizedImages && standardizedImages.length > 0) {\n nanoBananaPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n nanoBananaPayload.images = runpodOptions.images;\n }\n\n return nanoBananaPayload;\n }\n\n // Check if this is a Nano Banana (non-pro) edit model\n const isNanaBananaEditModel =\n this.modelId.includes('nano-banana-edit') &&\n !this.modelId.includes('nano-banana-pro');\n if (isNanaBananaEditModel) {\n // Nano Banana edit uses simple format: prompt, images array, enable_safety_checker\n const nanoBananaEditPayload: Record<string, unknown> = {\n prompt,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n };\n\n // Always use images array format\n if (standardizedImages && standardizedImages.length > 0) {\n nanoBananaEditPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n nanoBananaEditPayload.images = runpodOptions.images;\n }\n\n return nanoBananaEditPayload;\n }\n\n // Check if this is a Qwen Image Edit 2511 model (uses images array format)\n const isQwenImageEdit2511 = this.modelId.includes('qwen-image-edit-2511');\n if (isQwenImageEdit2511) {\n // Qwen Image Edit 2511 uses images array, output_format, and sync options\n const qwenEdit2511Payload: Record<string, unknown> = {\n prompt,\n size: runpodSize,\n seed: seed ?? -1,\n output_format: (runpodOptions?.output_format as string) ?? 'jpeg',\n enable_base64_output:\n (runpodOptions?.enable_base64_output as boolean) ?? false,\n enable_sync_mode:\n (runpodOptions?.enable_sync_mode as boolean) ?? false,\n ...runpodOptions,\n };\n\n // Always use images array format for this model\n if (standardizedImages && standardizedImages.length > 0) {\n qwenEdit2511Payload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n qwenEdit2511Payload.images = runpodOptions.images;\n }\n\n return qwenEdit2511Payload;\n }\n\n // Check if this is an Alibaba Wan model\n const isWanModel = this.modelId.includes('wan-2');\n if (isWanModel) {\n // Alibaba Wan 2.6 uses standard t2i format with negative prompt in prompt string\n return {\n prompt,\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n }\n\n // Default format for Qwen and other models\n const defaultPayload: Record<string, unknown> = {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n\n // For edit models, use standardized files if provided\n if (standardizedImages && standardizedImages.length > 0) {\n // Single image models use 'image', multi-image models use 'images'\n if (standardizedImages.length === 1) {\n defaultPayload.image = standardizedImages[0];\n } else {\n defaultPayload.images = standardizedImages;\n }\n }\n\n return defaultPayload;\n }\n}\n\n// Runpod image API response schema (handles both sync and async responses)\nconst runpodImageResponseSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n delayTime: z.number().optional(),\n executionTime: z.number().optional(),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(), // URL to the generated image (Qwen format)\n image_url: z.string().optional(), // URL to the generated image (Flux format)\n })\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\n});\n\n// Schema for polling status endpoint\nconst runpodImageStatusSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(),\n image_url: z.string().optional(),\n })\n .optional(),\n error: z.string().optional(), // Error message if FAILED\n});\n","import { z } from 'zod';\r\nimport { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';\r\n\r\n// Runpod image API error schema (supports both error formats)\r\nexport const runpodImageErrorSchema = z.object({\r\n error: z.string().optional(),\r\n message: z.string().optional(),\r\n});\r\n\r\nexport type RunpodImageErrorData = z.infer<typeof runpodImageErrorSchema>;\r\n\r\nexport const runpodImageFailedResponseHandler = createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: RunpodImageErrorData) => {\r\n // Prefer message if available (more descriptive)\r\n if (data.message) {\r\n return data.message;\r\n }\r\n \r\n // If error field exists, try to extract nested JSON message\r\n if (data.error) {\r\n // Runpod sometimes returns nested JSON in the error field like:\r\n // \"Error submitting task: 400, {\\\"code\\\":400,\\\"message\\\":\\\"...\\\"}\"\r\n // Try to extract the inner message for cleaner error messages\r\n // Find the last occurrence of { which likely starts the JSON object\r\n const lastBraceIndex = data.error.lastIndexOf('{');\r\n if (lastBraceIndex !== -1) {\r\n try {\r\n const jsonStr = data.error.substring(lastBraceIndex);\r\n const nestedError = JSON.parse(jsonStr);\r\n if (nestedError.message && typeof nestedError.message === 'string') {\r\n return nestedError.message;\r\n }\r\n } catch {\r\n // If parsing fails, fall back to the original error string\r\n }\r\n }\r\n return data.error;\r\n }\r\n \r\n return 'Unknown Runpod error';\r\n },\r\n});\r\n\r\n","import {\n JSONValue,\n SpeechModelV3,\n SpeechModelV3CallOptions,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport { FetchFunction, withoutTrailingSlash } from '@ai-sdk/provider-utils';\n\nexport interface RunpodSpeechModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction replaceNewlinesWithSpaces(value: string): string {\n return value.replace(/[\\r\\n]+/g, ' ');\n}\n\nexport class RunpodSpeechModel implements SpeechModelV3 {\n readonly specificationVersion = 'v3';\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private readonly config: RunpodSpeechModelConfig\n ) {}\n\n private getRunpodRunSyncUrl(): string {\n // `withoutTrailingSlash` is typed to potentially return undefined, but in our\n // case `config.baseURL` is always a string.\n const baseURL =\n withoutTrailingSlash(this.config.baseURL) ?? this.config.baseURL;\n\n // Allow users to pass /run or /runsync directly.\n if (baseURL.endsWith('/run') || baseURL.endsWith('/runsync')) {\n return baseURL;\n }\n\n return `${baseURL}/runsync`;\n }\n\n async doGenerate(\n options: SpeechModelV3CallOptions\n ): Promise<Awaited<ReturnType<SpeechModelV3['doGenerate']>>> {\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n const warnings: SharedV3Warning[] = [];\n\n const {\n text,\n voice,\n outputFormat,\n instructions,\n speed,\n language,\n providerOptions,\n abortSignal,\n headers,\n } = options;\n\n // This endpoint currently returns wav. Warn and ignore other formats.\n if (outputFormat != null && outputFormat !== 'wav') {\n warnings.push({\n type: 'unsupported',\n feature: 'outputFormat',\n details: `Unsupported outputFormat: ${outputFormat}. This endpoint returns 'wav'.`,\n });\n }\n\n if (instructions != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'instructions',\n details: `Instructions are not supported by this speech endpoint.`,\n });\n }\n\n if (speed != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'speed',\n details: `Speed is not supported by this speech endpoint.`,\n });\n }\n\n if (language != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'language',\n details: `Language selection is not supported by this speech endpoint.`,\n });\n }\n\n const runpodProviderOptions = isRecord(providerOptions)\n ? (providerOptions as any).runpod\n : undefined;\n\n const voiceUrl =\n isRecord(runpodProviderOptions) &&\n (typeof runpodProviderOptions.voice_url === 'string' ||\n typeof runpodProviderOptions.voiceUrl === 'string')\n ? (runpodProviderOptions.voice_url ??\n runpodProviderOptions.voiceUrl ??\n undefined)\n : undefined;\n\n const input: Record<string, unknown> = {\n prompt: replaceNewlinesWithSpaces(text),\n };\n\n // The endpoint supports either a built-in voice name or a voice_url prompt.\n if (voiceUrl) {\n input.voice_url = voiceUrl;\n } else if (voice) {\n input.voice = voice;\n }\n\n const requestBody = { input };\n const url = this.getRunpodRunSyncUrl();\n\n const fetchFn = this.config.fetch ?? fetch;\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.config.headers(),\n };\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (value != null) {\n requestHeaders[key] = value;\n }\n }\n }\n\n const response = await fetchFn(url, {\n method: 'POST',\n headers: requestHeaders,\n body: JSON.stringify(requestBody),\n signal: abortSignal,\n });\n\n const responseHeaders = Object.fromEntries(response.headers.entries());\n const rawBodyText = await response.text();\n\n let parsed: any = undefined;\n try {\n parsed = rawBodyText ? JSON.parse(rawBodyText) : undefined;\n } catch {\n // ignore - we'll surface the raw body in the error below\n }\n\n if (!response.ok) {\n const message =\n (parsed && typeof parsed.error === 'string' && parsed.error) ||\n rawBodyText ||\n `HTTP ${response.status}`;\n throw new Error(`Runpod speech request failed: ${message}`);\n }\n\n const output = parsed?.output ?? parsed;\n\n const audioUrl = output?.audio_url;\n if (typeof audioUrl !== 'string' || audioUrl.length === 0) {\n throw new Error('Runpod speech response did not include an audio_url.');\n }\n\n const audioResponse = await fetchFn(audioUrl, { signal: abortSignal });\n if (!audioResponse.ok) {\n throw new Error(\n `Failed to download generated audio (${audioResponse.status}).`\n );\n }\n\n const audio = new Uint8Array(await audioResponse.arrayBuffer());\n\n const providerMetadata: Record<string, Record<string, JSONValue>> = {\n runpod: {\n audioUrl,\n ...(typeof output?.cost === 'number' ? { cost: output.cost } : {}),\n },\n };\n\n return {\n audio,\n warnings,\n request: {\n body: JSON.stringify(requestBody),\n },\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders as any,\n body: rawBodyText,\n },\n providerMetadata,\n };\n }\n}\n"],"mappings":"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","names":["import_provider_utils","import_provider_utils","import_zod","import_provider_utils"]}
package/dist/index.mjs CHANGED
@@ -153,8 +153,16 @@ var RunpodImageModel = class {
153
153
  aspectRatio,
154
154
  standardizedImages
155
155
  );
156
+ let effectiveBaseURL = this.config.baseURL;
157
+ const runpodOptions = providerOptions.runpod;
158
+ if (this.modelId.includes("qwen-image-edit-2511") && !this.modelId.includes("lora") && runpodOptions?.loras && Array.isArray(runpodOptions.loras) && runpodOptions.loras.length > 0) {
159
+ effectiveBaseURL = this.config.baseURL.replace(
160
+ "qwen-image-edit-2511",
161
+ "qwen-image-edit-2511-lora"
162
+ );
163
+ }
156
164
  const { value: response, responseHeaders } = await postJsonToApi({
157
- url: `${this.config.baseURL}/runsync`,
165
+ url: `${effectiveBaseURL}/runsync`,
158
166
  headers: combineHeaders(this.config.headers(), headers),
159
167
  body: {
160
168
  input: inputPayload
@@ -197,7 +205,8 @@ var RunpodImageModel = class {
197
205
  const imageUrl = await this.pollForCompletion(
198
206
  typedResponse.id,
199
207
  abortSignal,
200
- pollOptions
208
+ pollOptions,
209
+ effectiveBaseURL
201
210
  );
202
211
  const imageData = await this.downloadImage(imageUrl, abortSignal);
203
212
  return {
@@ -233,15 +242,16 @@ var RunpodImageModel = class {
233
242
  });
234
243
  return imageData;
235
244
  }
236
- async pollForCompletion(jobId, abortSignal, pollOptions) {
245
+ async pollForCompletion(jobId, abortSignal, pollOptions, effectiveBaseURL) {
237
246
  const maxAttempts = pollOptions?.maxAttempts ?? 60;
238
247
  const pollInterval = pollOptions?.pollIntervalMillis ?? 5e3;
248
+ const baseURL = effectiveBaseURL ?? this.config.baseURL;
239
249
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
240
250
  if (abortSignal?.aborted) {
241
251
  throw new Error("Image generation was aborted");
242
252
  }
243
253
  const { value: statusResponse } = await getFromApi({
244
- url: `${this.config.baseURL}/status/${jobId}`,
254
+ url: `${baseURL}/status/${jobId}`,
245
255
  headers: this.config.headers(),
246
256
  successfulResponseHandler: createJsonResponseHandler(
247
257
  runpodImageStatusSchema
@@ -391,6 +401,47 @@ var RunpodImageModel = class {
391
401
  }
392
402
  return nanoBananaPayload;
393
403
  }
404
+ const isNanaBananaEditModel = this.modelId.includes("nano-banana-edit") && !this.modelId.includes("nano-banana-pro");
405
+ if (isNanaBananaEditModel) {
406
+ const nanoBananaEditPayload = {
407
+ prompt,
408
+ enable_safety_checker: runpodOptions?.enable_safety_checker ?? true
409
+ };
410
+ if (standardizedImages && standardizedImages.length > 0) {
411
+ nanoBananaEditPayload.images = standardizedImages;
412
+ } else if (runpodOptions?.images) {
413
+ nanoBananaEditPayload.images = runpodOptions.images;
414
+ }
415
+ return nanoBananaEditPayload;
416
+ }
417
+ const isQwenImageEdit2511 = this.modelId.includes("qwen-image-edit-2511");
418
+ if (isQwenImageEdit2511) {
419
+ const qwenEdit2511Payload = {
420
+ prompt,
421
+ size: runpodSize,
422
+ seed: seed ?? -1,
423
+ output_format: runpodOptions?.output_format ?? "jpeg",
424
+ enable_base64_output: runpodOptions?.enable_base64_output ?? false,
425
+ enable_sync_mode: runpodOptions?.enable_sync_mode ?? false,
426
+ ...runpodOptions
427
+ };
428
+ if (standardizedImages && standardizedImages.length > 0) {
429
+ qwenEdit2511Payload.images = standardizedImages;
430
+ } else if (runpodOptions?.images) {
431
+ qwenEdit2511Payload.images = runpodOptions.images;
432
+ }
433
+ return qwenEdit2511Payload;
434
+ }
435
+ const isWanModel = this.modelId.includes("wan-2");
436
+ if (isWanModel) {
437
+ return {
438
+ prompt,
439
+ size: runpodSize,
440
+ seed: seed ?? -1,
441
+ enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,
442
+ ...runpodOptions
443
+ };
444
+ }
394
445
  const defaultPayload = {
395
446
  prompt,
396
447
  negative_prompt: runpodOptions?.negative_prompt ?? "",
@@ -589,6 +640,8 @@ var MODEL_ID_TO_ENDPOINT_URL = {
589
640
  var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
590
641
  "qwen/qwen-image": "https://api.runpod.ai/v2/qwen-image-t2i",
591
642
  "qwen/qwen-image-edit": "https://api.runpod.ai/v2/qwen-image-edit",
643
+ "qwen/qwen-image-edit-2511": "https://api.runpod.ai/v2/qwen-image-edit-2511",
644
+ "qwen/qwen-image-edit-2511-lora": "https://api.runpod.ai/v2/qwen-image-edit-2511-lora",
592
645
  "bytedance/seedream-3.0": "https://api.runpod.ai/v2/seedream-3-0-t2i",
593
646
  // Seadream v4 (t2i and edit)
594
647
  "bytedance/seedream-4.0": "https://api.runpod.ai/v2/seedream-v4-t2i",
@@ -596,8 +649,12 @@ var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
596
649
  "black-forest-labs/flux-1-kontext-dev": "https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev",
597
650
  "black-forest-labs/flux-1-schnell": "https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell",
598
651
  "black-forest-labs/flux-1-dev": "https://api.runpod.ai/v2/black-forest-labs-flux-1-dev",
652
+ // Alibaba Wan 2.6 (t2i)
653
+ "alibaba/wan-2.6": "https://api.runpod.ai/v2/wan-2-6-t2i",
599
654
  // Nano Banana (edit only)
655
+ "google/nano-banana-edit": "https://api.runpod.ai/v2/nano-banana-edit",
600
656
  "nano-banana-edit": "https://api.runpod.ai/v2/nano-banana-edit",
657
+ // backwards compatibility
601
658
  // Nano Banana Pro (edit only)
602
659
  "google/nano-banana-pro-edit": "https://api.runpod.ai/v2/nano-banana-pro-edit",
603
660
  // Pruna (t2i and edit)
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/runpod-provider.ts","../src/runpod-image-model.ts","../src/runpod-error.ts","../src/runpod-speech-model.ts"],"sourcesContent":["import { ImageModelV3, LanguageModelV3, SpeechModelV3 } from '@ai-sdk/provider';\nimport {\n OpenAICompatibleChatLanguageModel,\n OpenAICompatibleCompletionLanguageModel,\n} from '@ai-sdk/openai-compatible';\nimport {\n FetchFunction,\n loadApiKey,\n withoutTrailingSlash,\n} from '@ai-sdk/provider-utils';\nimport { RunpodImageModel } from './runpod-image-model';\nimport { RunpodSpeechModel } from './runpod-speech-model';\n\nexport interface RunpodProviderSettings {\n /**\nRunpod API key.\n*/\n apiKey?: string;\n /**\nCustom base URL for Runpod API. Use this to point to custom endpoints or different Runpod deployments.\nExample: 'https://api.runpod.ai/v2/your-endpoint-id/openai/v1'\n*/\n baseURL?: string;\n /**\nCustom headers to include in the requests.\n*/\n headers?: Record<string, string>;\n /**\nCustom fetch implementation. You can use it as a middleware to intercept requests,\nor to provide a custom fetch implementation for e.g. testing.\n*/\n fetch?: FetchFunction;\n}\n\nexport interface RunpodProvider {\n /**\nCreates a model for text generation.\n*/\n (modelId: string): LanguageModelV3;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: string): LanguageModelV3;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: string): LanguageModelV3;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: string): LanguageModelV3;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: string): ImageModelV3;\n\n /**\nCreates an image model for image generation.\n*/\n image(modelId: string): ImageModelV3;\n\n /**\nCreates a speech model for speech generation.\n*/\n speechModel(modelId: string): SpeechModelV3;\n\n /**\nCreates a speech model for speech generation.\n*/\n speech(modelId: string): SpeechModelV3;\n}\n\n// Mapping of Runpod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n 'ibm-granite/granite-4.0-h-small':\n 'https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1',\n 'gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'openai/gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'deepcogito/cogito-671b-v2.1-fp8':\n 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n 'deepcogito/cogito-671b-v2.1-FP8':\n 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n};\n\n// Mapping of Runpod image model IDs to their endpoint URLs\nconst IMAGE_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen-image': 'https://api.runpod.ai/v2/qwen-image-t2i',\n 'qwen/qwen-image-edit': 'https://api.runpod.ai/v2/qwen-image-edit',\n 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\n // Seadream v4 (t2i and edit)\n 'bytedance/seedream-4.0': 'https://api.runpod.ai/v2/seedream-v4-t2i',\n 'bytedance/seedream-4.0-edit': 'https://api.runpod.ai/v2/seedream-v4-edit',\n 'black-forest-labs/flux-1-kontext-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev',\n 'black-forest-labs/flux-1-schnell':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell',\n 'black-forest-labs/flux-1-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-dev',\n // Nano Banana (edit only)\n 'nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit',\n // Nano Banana Pro (edit only)\n 'google/nano-banana-pro-edit':\n 'https://api.runpod.ai/v2/nano-banana-pro-edit',\n // Pruna (t2i and edit)\n 'pruna/p-image-t2i': 'https://api.runpod.ai/v2/p-image-t2i',\n 'pruna/p-image-edit': 'https://api.runpod.ai/v2/p-image-edit',\n};\n\n// Mapping of Runpod speech model IDs to their serverless endpoint URLs\n// Note: This is intentionally a temporary mapping for a stealth release.\nconst SPEECH_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'resembleai/chatterbox-turbo': 'https://api.runpod.ai/v2/chatterbox-turbo/',\n};\n\n// Mapping of Runpod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\n 'deepcogito/cogito-671b-v2.1-fp8': 'deepcogito/cogito-671b-v2.1-FP8',\n 'deepcogito/cogito-671b-v2.1-FP8': 'deepcogito/cogito-671b-v2.1-FP8',\n};\n\n/**\n * Derives the endpoint URL for a model by replacing slashes with hyphens.\n * Example: 'ibm-granite/granite-4.0-h-small' -> 'https://api.runpod.ai/v2/ibm-granite-granite-4.0-h-small/openai/v1'\n */\nfunction deriveEndpointURL(modelId: string): string {\n const normalized = modelId.replace(/\\//g, '-');\n return `https://api.runpod.ai/v2/${normalized}/openai/v1`;\n}\n\nfunction parseRunpodConsoleEndpointId(modelIdOrUrl: string): string | null {\n if (!modelIdOrUrl.startsWith('http')) {\n return null;\n }\n\n try {\n const url = new URL(modelIdOrUrl);\n if (url.hostname !== 'console.runpod.io') {\n return null;\n }\n\n // Example:\n // https://console.runpod.io/serverless/user/endpoint/<ENDPOINT_ID>\n const parts = url.pathname.split('/').filter(Boolean);\n const idx = parts.lastIndexOf('endpoint');\n const endpointId = idx !== -1 ? parts[idx + 1] : undefined;\n\n return endpointId || null;\n } catch {\n return null;\n }\n}\n\nexport function createRunpod(\n options: RunpodProviderSettings = {}\n): RunpodProvider {\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'RUNPOD_API_KEY',\n description: 'Runpod',\n })}`,\n ...options.headers,\n });\n\n const runpodFetch: FetchFunction = async (url, requestInit) => {\n if (requestInit?.body) {\n try {\n const body = JSON.parse(requestInit.body as string);\n if (body.stream === true && !body.stream_options) {\n body.stream_options = { include_usage: true };\n requestInit.body = JSON.stringify(body);\n }\n } catch {}\n }\n const fetchFn = options.fetch || fetch;\n return fetchFn(url, requestInit);\n };\n\n interface CommonModelConfig {\n provider: string;\n url: ({ path }: { path: string }) => string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n }\n\n const getModelConfig = (\n modelId: string,\n modelType: string\n ): CommonModelConfig => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: runpodFetch,\n };\n };\n\n const createChatModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'chat'),\n includeUsage: true,\n });\n };\n\n const createCompletionModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'completion'),\n includeUsage: true,\n });\n };\n\n const createImageModel = (modelId: string) => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL =\n IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return new RunpodImageModel(modelId, {\n provider: 'runpod.image',\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n });\n };\n\n const createSpeechModel = (modelId: string) => {\n const endpointIdFromConsole = parseRunpodConsoleEndpointId(modelId);\n const normalizedModelId = endpointIdFromConsole ?? modelId;\n\n // Prefer explicit mapping for known speech model IDs.\n const mappedBaseURL = SPEECH_MODEL_ID_TO_ENDPOINT_URL[normalizedModelId];\n\n const baseURL =\n mappedBaseURL ??\n (normalizedModelId.startsWith('http')\n ? normalizedModelId\n : `https://api.runpod.ai/v2/${normalizedModelId}`);\n\n return new RunpodSpeechModel(normalizedModelId, {\n provider: 'runpod.speech',\n baseURL,\n headers: getHeaders,\n fetch: runpodFetch,\n });\n };\n\n const provider = (modelId: string) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n provider.imageModel = createImageModel;\n provider.image = createImageModel;\n provider.speechModel = createSpeechModel;\n provider.speech = createSpeechModel;\n\n return provider;\n}\n\nexport const runpod = createRunpod();\n","import {\n ImageModelV3,\n ImageModelV3CallOptions,\n ImageModelV3File,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createJsonResponseHandler,\n createBinaryResponseHandler,\n FetchFunction,\n postJsonToApi,\n getFromApi,\n} from '@ai-sdk/provider-utils';\nimport { InvalidArgumentError } from '@ai-sdk/provider';\nimport { z } from 'zod';\nimport { runpodImageFailedResponseHandler } from './runpod-error';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ninterface RunpodImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\n// Runpod supported aspect ratios (only validated working sizes)\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\n '1:1': '1328*1328', // ✅ Native support\n '4:3': '1472*1140', // ✅ Native support\n '3:4': '1140*1472', // ✅ Native support\n};\n\n// Runpod supported sizes (validated working sizes)\nconst SUPPORTED_SIZES = new Set([\n // Native aspect ratio sizes\n '1328*1328', // 1:1\n '1472*1140', // 4:3\n '1140*1472', // 3:4\n // Additional validated sizes\n '512*512',\n '768*768',\n '1024*1024',\n '1536*1536',\n '2048*2048',\n '4096*4096',\n '512*768',\n '768*512',\n '1024*768',\n '768*1024',\n]);\n\nexport class RunpodImageModel implements ImageModelV3 {\n readonly specificationVersion = 'v3';\n readonly maxImagesPerCall = 1;\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private config: RunpodImageModelConfig\n ) {}\n\n async doGenerate({\n prompt,\n n,\n size,\n aspectRatio,\n seed,\n files,\n mask,\n providerOptions,\n headers,\n abortSignal,\n }: ImageModelV3CallOptions): Promise<\n Awaited<ReturnType<ImageModelV3['doGenerate']>>\n > {\n const warnings: Array<SharedV3Warning> = [];\n\n // Convert standardized files to Runpod format (base64 data URLs or raw base64)\n const standardizedImages = this.convertFilesToRunpodFormat(files);\n\n if (mask) {\n warnings.push({\n type: 'unsupported',\n feature: 'mask',\n details: 'Mask input for inpainting is not yet supported.',\n });\n }\n\n // Check if this is a Pruna model (skip standard size/aspectRatio validation)\n const isPrunaModel =\n this.modelId.includes('pruna') || this.modelId.includes('p-image');\n\n // Check if this is a Nano Banana Pro model (skip standard size/aspectRatio validation)\n const isNanoBananaProModel = this.modelId.includes('nano-banana-pro');\n\n // Determine the size to use\n let runpodSize: string;\n\n if (isPrunaModel || isNanoBananaProModel) {\n // These models use aspect_ratio string directly, skip size validation\n // Pass through the aspectRatio or use default, validation happens at API level\n runpodSize = aspectRatio || '1:1';\n } else if (size) {\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\n const runpodSizeCandidate = size.replace('x', '*');\n\n // Validate size is supported\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\n throw new InvalidArgumentError({\n argument: 'size',\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\n SUPPORTED_SIZES\n )\n .map((s) => s.replace('*', 'x'))\n .join(', ')}`,\n });\n }\n\n runpodSize = runpodSizeCandidate;\n } else if (aspectRatio) {\n // Validate aspect ratio is supported\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\n throw new InvalidArgumentError({\n argument: 'aspectRatio',\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\n });\n }\n\n // Use supported aspect ratio mapping\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\n } else {\n // Default to square format\n runpodSize = '1328*1328';\n }\n\n // Handle multiple images warning\n if (n > 1) {\n warnings.push({\n type: 'unsupported',\n feature: 'multiple images (n > 1)',\n details:\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\n });\n }\n\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n // Runpod uses a different request format - /runsync endpoint with input wrapper\n const inputPayload = this.buildInputPayload(\n prompt ?? '',\n runpodSize,\n seed,\n providerOptions.runpod as Record<string, unknown> | undefined,\n aspectRatio,\n standardizedImages\n );\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url: `${this.config.baseURL}/runsync`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n input: inputPayload,\n },\n failedResponseHandler: runpodImageFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageResponseSchema as any\n ),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n // Handle both sync and async responses from Runpod\n const typedResponse = response as any;\n if (\n typedResponse.status === 'COMPLETED' &&\n (typedResponse.output?.result || typedResponse.output?.image_url)\n ) {\n // Sync response - image is ready\n // Different models use different response formats: result vs image_url\n const imageUrl =\n typedResponse.output.result || typedResponse.output.image_url;\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n cost: typedResponse.output?.cost,\n },\n ],\n },\n },\n };\n } else if (\n typedResponse.status === 'IN_QUEUE' ||\n typedResponse.status === 'IN_PROGRESS'\n ) {\n // Async response - need to poll for completion\n const pollOptions = {\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\n pollIntervalMillis: providerOptions.runpod\n ?.pollIntervalMillis as number,\n };\n const imageUrl = await this.pollForCompletion(\n typedResponse.id,\n abortSignal,\n pollOptions\n );\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n jobId: typedResponse.id,\n },\n ],\n },\n },\n };\n } else {\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\n }\n }\n\n private async downloadImage(\n imageUrl: string,\n abortSignal?: AbortSignal\n ): Promise<Uint8Array> {\n const { value: imageData } = await getFromApi({\n url: imageUrl,\n successfulResponseHandler: createBinaryResponseHandler(),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n return imageData;\n }\n\n private async pollForCompletion(\n jobId: string,\n abortSignal?: AbortSignal,\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number }\n ): Promise<string> {\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (abortSignal?.aborted) {\n throw new Error('Image generation was aborted');\n }\n\n const { value: statusResponse } = await getFromApi({\n url: `${this.config.baseURL}/status/${jobId}`,\n headers: this.config.headers(),\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageStatusSchema as any\n ),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n\n const typedStatusResponse = statusResponse as any;\n if (\n typedStatusResponse.status === 'COMPLETED' &&\n (typedStatusResponse.output?.result ||\n typedStatusResponse.output?.image_url)\n ) {\n return (\n typedStatusResponse.output.result ||\n typedStatusResponse.output.image_url\n );\n }\n\n if (typedStatusResponse.status === 'FAILED') {\n throw new Error(\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\n );\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new Error(\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\n );\n }\n\n /**\n * Converts standardized ImageModelV3File[] to Runpod-compatible format.\n * Returns URLs or base64 data URLs that Runpod API accepts.\n */\n private convertFilesToRunpodFormat(\n files: ImageModelV3File[] | undefined\n ): string[] | undefined {\n if (!files || files.length === 0) {\n return undefined;\n }\n\n return files.map((file) => {\n // Handle URL type - return URL directly\n if (file.type === 'url') {\n return file.url;\n }\n\n // Handle file type with data\n if (typeof file.data === 'string') {\n // If it's already a data URL, return as-is\n if (file.data.startsWith('data:')) {\n return file.data;\n }\n // Otherwise, wrap as data URL with media type\n return `data:${file.mediaType};base64,${file.data}`;\n }\n\n // Convert Uint8Array to base64 data URL\n const base64 = this.uint8ArrayToBase64(file.data);\n return `data:${file.mediaType};base64,${base64}`;\n });\n }\n\n /**\n * Converts Uint8Array to base64 string.\n */\n private uint8ArrayToBase64(data: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]);\n }\n return btoa(binary);\n }\n\n private buildInputPayload(\n prompt: string,\n runpodSize: string,\n seed?: number,\n runpodOptions?: Record<string, unknown>,\n aspectRatio?: string,\n standardizedImages?: string[]\n ): Record<string, unknown> {\n // Check if this is a Flux model that uses different parameters\n const isFluxModel =\n this.modelId.includes('flux') ||\n this.modelId.includes('black-forest-labs');\n\n if (isFluxModel) {\n // Check if this is Flux Kontext (uses different parameters)\n const isKontext = this.modelId.includes('kontext');\n\n if (isKontext) {\n // Flux Kontext uses size format and has image input\n // Prioritize standardized files over providerOptions\n const kontextPayload: Record<string, unknown> = {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: 28,\n guidance: 2,\n size: runpodSize,\n output_format: 'png',\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n\n // Use standardized files if provided (first image), otherwise use providerOptions.image\n if (standardizedImages && standardizedImages.length > 0) {\n kontextPayload.image = standardizedImages[0];\n }\n\n return kontextPayload;\n } else {\n // Regular Flux models use width/height\n const [width, height] = runpodSize.split('*').map(Number);\n\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\n guidance: this.modelId.includes('schnell') ? 7 : 2,\n width,\n height,\n image_format: 'png',\n ...runpodOptions,\n };\n }\n }\n\n // Check if this is a Pruna model\n const isPrunaModel =\n this.modelId.includes('pruna') || this.modelId.includes('p-image');\n if (isPrunaModel) {\n const isPrunaEdit = this.modelId.includes('edit');\n\n if (isPrunaEdit) {\n // Pruna image edit\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\"\n // Supports 1-5 images via providerOptions.runpod.images\n const editPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n disable_safety_checker:\n (runpodOptions?.disable_safety_checker as boolean) ?? false,\n };\n\n // Add seed if provided\n if (seed !== undefined) {\n editPayload.seed = seed;\n } else if (runpodOptions?.seed !== undefined) {\n editPayload.seed = runpodOptions.seed;\n }\n\n // Use standardized files if provided, otherwise use providerOptions.images\n if (standardizedImages && standardizedImages.length > 0) {\n editPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n editPayload.images = runpodOptions.images;\n }\n\n return editPayload;\n } else {\n // Pruna text-to-image\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"custom\"\n // For custom: width/height 256-1440, must be multiple of 16\n const t2iPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n disable_safety_checker:\n (runpodOptions?.disable_safety_checker as boolean) ?? false,\n };\n\n // Add seed if provided\n if (seed !== undefined) {\n t2iPayload.seed = seed;\n } else if (runpodOptions?.seed !== undefined) {\n t2iPayload.seed = runpodOptions.seed;\n }\n\n // Handle custom aspect ratio with width/height\n if (t2iPayload.aspect_ratio === 'custom') {\n if (runpodOptions?.width) {\n t2iPayload.width = runpodOptions.width;\n }\n if (runpodOptions?.height) {\n t2iPayload.height = runpodOptions.height;\n }\n }\n\n return t2iPayload;\n }\n }\n\n // Check if this is a Nano Banana Pro model (google/nano-banana-pro-edit)\n const isNanaBananaProModel = this.modelId.includes('nano-banana-pro');\n if (isNanaBananaProModel) {\n // Nano Banana Pro image edit\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"21:9\", \"9:21\"\n // Supported resolution: \"1k\", \"2k\", \"4k\"\n // Supported output_format: \"jpeg\", \"png\", \"webp\"\n const nanoBananaPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n resolution: (runpodOptions?.resolution as string) ?? '1k',\n output_format: (runpodOptions?.output_format as string) ?? 'jpeg',\n enable_base64_output:\n (runpodOptions?.enable_base64_output as boolean) ?? false,\n enable_sync_mode:\n (runpodOptions?.enable_sync_mode as boolean) ?? false,\n };\n\n // Use standardized files if provided, otherwise use providerOptions.images\n if (standardizedImages && standardizedImages.length > 0) {\n nanoBananaPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n nanoBananaPayload.images = runpodOptions.images;\n }\n\n return nanoBananaPayload;\n }\n\n // Default format for Qwen and other models\n const defaultPayload: Record<string, unknown> = {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n\n // For edit models, use standardized files if provided\n if (standardizedImages && standardizedImages.length > 0) {\n // Single image models use 'image', multi-image models use 'images'\n if (standardizedImages.length === 1) {\n defaultPayload.image = standardizedImages[0];\n } else {\n defaultPayload.images = standardizedImages;\n }\n }\n\n return defaultPayload;\n }\n}\n\n// Runpod image API response schema (handles both sync and async responses)\nconst runpodImageResponseSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n delayTime: z.number().optional(),\n executionTime: z.number().optional(),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(), // URL to the generated image (Qwen format)\n image_url: z.string().optional(), // URL to the generated image (Flux format)\n })\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\n});\n\n// Schema for polling status endpoint\nconst runpodImageStatusSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(),\n image_url: z.string().optional(),\n })\n .optional(),\n error: z.string().optional(), // Error message if FAILED\n});\n","import { z } from 'zod';\r\nimport { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';\r\n\r\n// Runpod image API error schema (supports both error formats)\r\nexport const runpodImageErrorSchema = z.object({\r\n error: z.string().optional(),\r\n message: z.string().optional(),\r\n});\r\n\r\nexport type RunpodImageErrorData = z.infer<typeof runpodImageErrorSchema>;\r\n\r\nexport const runpodImageFailedResponseHandler = createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: RunpodImageErrorData) => {\r\n // Prefer message if available (more descriptive)\r\n if (data.message) {\r\n return data.message;\r\n }\r\n \r\n // If error field exists, try to extract nested JSON message\r\n if (data.error) {\r\n // Runpod sometimes returns nested JSON in the error field like:\r\n // \"Error submitting task: 400, {\\\"code\\\":400,\\\"message\\\":\\\"...\\\"}\"\r\n // Try to extract the inner message for cleaner error messages\r\n // Find the last occurrence of { which likely starts the JSON object\r\n const lastBraceIndex = data.error.lastIndexOf('{');\r\n if (lastBraceIndex !== -1) {\r\n try {\r\n const jsonStr = data.error.substring(lastBraceIndex);\r\n const nestedError = JSON.parse(jsonStr);\r\n if (nestedError.message && typeof nestedError.message === 'string') {\r\n return nestedError.message;\r\n }\r\n } catch {\r\n // If parsing fails, fall back to the original error string\r\n }\r\n }\r\n return data.error;\r\n }\r\n \r\n return 'Unknown Runpod error';\r\n },\r\n});\r\n\r\n","import {\n JSONValue,\n SpeechModelV3,\n SpeechModelV3CallOptions,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport { FetchFunction, withoutTrailingSlash } from '@ai-sdk/provider-utils';\n\nexport interface RunpodSpeechModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction replaceNewlinesWithSpaces(value: string): string {\n return value.replace(/[\\r\\n]+/g, ' ');\n}\n\nexport class RunpodSpeechModel implements SpeechModelV3 {\n readonly specificationVersion = 'v3';\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private readonly config: RunpodSpeechModelConfig\n ) {}\n\n private getRunpodRunSyncUrl(): string {\n // `withoutTrailingSlash` is typed to potentially return undefined, but in our\n // case `config.baseURL` is always a string.\n const baseURL =\n withoutTrailingSlash(this.config.baseURL) ?? this.config.baseURL;\n\n // Allow users to pass /run or /runsync directly.\n if (baseURL.endsWith('/run') || baseURL.endsWith('/runsync')) {\n return baseURL;\n }\n\n return `${baseURL}/runsync`;\n }\n\n async doGenerate(\n options: SpeechModelV3CallOptions\n ): Promise<Awaited<ReturnType<SpeechModelV3['doGenerate']>>> {\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n const warnings: SharedV3Warning[] = [];\n\n const {\n text,\n voice,\n outputFormat,\n instructions,\n speed,\n language,\n providerOptions,\n abortSignal,\n headers,\n } = options;\n\n // This endpoint currently returns wav. Warn and ignore other formats.\n if (outputFormat != null && outputFormat !== 'wav') {\n warnings.push({\n type: 'unsupported',\n feature: 'outputFormat',\n details: `Unsupported outputFormat: ${outputFormat}. This endpoint returns 'wav'.`,\n });\n }\n\n if (instructions != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'instructions',\n details: `Instructions are not supported by this speech endpoint.`,\n });\n }\n\n if (speed != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'speed',\n details: `Speed is not supported by this speech endpoint.`,\n });\n }\n\n if (language != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'language',\n details: `Language selection is not supported by this speech endpoint.`,\n });\n }\n\n const runpodProviderOptions = isRecord(providerOptions)\n ? (providerOptions as any).runpod\n : undefined;\n\n const voiceUrl =\n isRecord(runpodProviderOptions) &&\n (typeof runpodProviderOptions.voice_url === 'string' ||\n typeof runpodProviderOptions.voiceUrl === 'string')\n ? (runpodProviderOptions.voice_url ??\n runpodProviderOptions.voiceUrl ??\n undefined)\n : undefined;\n\n const input: Record<string, unknown> = {\n prompt: replaceNewlinesWithSpaces(text),\n };\n\n // The endpoint supports either a built-in voice name or a voice_url prompt.\n if (voiceUrl) {\n input.voice_url = voiceUrl;\n } else if (voice) {\n input.voice = voice;\n }\n\n const requestBody = { input };\n const url = this.getRunpodRunSyncUrl();\n\n const fetchFn = this.config.fetch ?? fetch;\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.config.headers(),\n };\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (value != null) {\n requestHeaders[key] = value;\n }\n }\n }\n\n const response = await fetchFn(url, {\n method: 'POST',\n headers: requestHeaders,\n body: JSON.stringify(requestBody),\n signal: abortSignal,\n });\n\n const responseHeaders = Object.fromEntries(response.headers.entries());\n const rawBodyText = await response.text();\n\n let parsed: any = undefined;\n try {\n parsed = rawBodyText ? JSON.parse(rawBodyText) : undefined;\n } catch {\n // ignore - we'll surface the raw body in the error below\n }\n\n if (!response.ok) {\n const message =\n (parsed && typeof parsed.error === 'string' && parsed.error) ||\n rawBodyText ||\n `HTTP ${response.status}`;\n throw new Error(`Runpod speech request failed: ${message}`);\n }\n\n const output = parsed?.output ?? parsed;\n\n const audioUrl = output?.audio_url;\n if (typeof audioUrl !== 'string' || audioUrl.length === 0) {\n throw new Error('Runpod speech response did not include an audio_url.');\n }\n\n const audioResponse = await fetchFn(audioUrl, { signal: abortSignal });\n if (!audioResponse.ok) {\n throw new Error(\n `Failed to download generated audio (${audioResponse.status}).`\n );\n }\n\n const audio = new Uint8Array(await audioResponse.arrayBuffer());\n\n const providerMetadata: Record<string, Record<string, JSONValue>> = {\n runpod: {\n audioUrl,\n ...(typeof output?.cost === 'number' ? { cost: output.cost } : {}),\n },\n };\n\n return {\n audio,\n warnings,\n request: {\n body: JSON.stringify(requestBody),\n },\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders as any,\n body: rawBodyText,\n },\n providerMetadata,\n };\n }\n}\n"],"mappings":"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","names":["withoutTrailingSlash","z","z","withoutTrailingSlash"]}
1
+ {"version":3,"sources":["../src/runpod-provider.ts","../src/runpod-image-model.ts","../src/runpod-error.ts","../src/runpod-speech-model.ts"],"sourcesContent":["import { ImageModelV3, LanguageModelV3, SpeechModelV3 } from '@ai-sdk/provider';\nimport {\n OpenAICompatibleChatLanguageModel,\n OpenAICompatibleCompletionLanguageModel,\n} from '@ai-sdk/openai-compatible';\nimport {\n FetchFunction,\n loadApiKey,\n withoutTrailingSlash,\n} from '@ai-sdk/provider-utils';\nimport { RunpodImageModel } from './runpod-image-model';\nimport { RunpodSpeechModel } from './runpod-speech-model';\n\nexport interface RunpodProviderSettings {\n /**\nRunpod API key.\n*/\n apiKey?: string;\n /**\nCustom base URL for Runpod API. Use this to point to custom endpoints or different Runpod deployments.\nExample: 'https://api.runpod.ai/v2/your-endpoint-id/openai/v1'\n*/\n baseURL?: string;\n /**\nCustom headers to include in the requests.\n*/\n headers?: Record<string, string>;\n /**\nCustom fetch implementation. You can use it as a middleware to intercept requests,\nor to provide a custom fetch implementation for e.g. testing.\n*/\n fetch?: FetchFunction;\n}\n\nexport interface RunpodProvider {\n /**\nCreates a model for text generation.\n*/\n (modelId: string): LanguageModelV3;\n\n /**\nCreates a chat model for text generation.\n*/\n chatModel(modelId: string): LanguageModelV3;\n\n /**\nCreates a chat model for text generation.\n*/\n languageModel(modelId: string): LanguageModelV3;\n\n /**\nCreates a completion model for text generation.\n*/\n completionModel(modelId: string): LanguageModelV3;\n\n /**\nCreates an image model for image generation.\n*/\n imageModel(modelId: string): ImageModelV3;\n\n /**\nCreates an image model for image generation.\n*/\n image(modelId: string): ImageModelV3;\n\n /**\nCreates a speech model for speech generation.\n*/\n speechModel(modelId: string): SpeechModelV3;\n\n /**\nCreates a speech model for speech generation.\n*/\n speech(modelId: string): SpeechModelV3;\n}\n\n// Mapping of Runpod model IDs to their endpoint URLs\nconst MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1',\n 'ibm-granite/granite-4.0-h-small':\n 'https://api.runpod.ai/v2/granite-4-0-h-small/openai/v1',\n 'gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'openai/gpt-oss-120b': 'https://api.runpod.ai/v2/gpt-oss-120b/openai/v1',\n 'deepcogito/cogito-671b-v2.1-fp8':\n 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n 'deepcogito/cogito-671b-v2.1-FP8':\n 'https://api.runpod.ai/v2/cogito-671b-v2-1-fp8-dynamic/openai/v1',\n};\n\n// Mapping of Runpod image model IDs to their endpoint URLs\nconst IMAGE_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'qwen/qwen-image': 'https://api.runpod.ai/v2/qwen-image-t2i',\n 'qwen/qwen-image-edit': 'https://api.runpod.ai/v2/qwen-image-edit',\n 'qwen/qwen-image-edit-2511': 'https://api.runpod.ai/v2/qwen-image-edit-2511',\n 'qwen/qwen-image-edit-2511-lora':\n 'https://api.runpod.ai/v2/qwen-image-edit-2511-lora',\n 'bytedance/seedream-3.0': 'https://api.runpod.ai/v2/seedream-3-0-t2i',\n // Seadream v4 (t2i and edit)\n 'bytedance/seedream-4.0': 'https://api.runpod.ai/v2/seedream-v4-t2i',\n 'bytedance/seedream-4.0-edit': 'https://api.runpod.ai/v2/seedream-v4-edit',\n 'black-forest-labs/flux-1-kontext-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-kontext-dev',\n 'black-forest-labs/flux-1-schnell':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-schnell',\n 'black-forest-labs/flux-1-dev':\n 'https://api.runpod.ai/v2/black-forest-labs-flux-1-dev',\n // Alibaba Wan 2.6 (t2i)\n 'alibaba/wan-2.6': 'https://api.runpod.ai/v2/wan-2-6-t2i',\n // Nano Banana (edit only)\n 'google/nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit',\n 'nano-banana-edit': 'https://api.runpod.ai/v2/nano-banana-edit', // backwards compatibility\n // Nano Banana Pro (edit only)\n 'google/nano-banana-pro-edit':\n 'https://api.runpod.ai/v2/nano-banana-pro-edit',\n // Pruna (t2i and edit)\n 'pruna/p-image-t2i': 'https://api.runpod.ai/v2/p-image-t2i',\n 'pruna/p-image-edit': 'https://api.runpod.ai/v2/p-image-edit',\n};\n\n// Mapping of Runpod speech model IDs to their serverless endpoint URLs\n// Note: This is intentionally a temporary mapping for a stealth release.\nconst SPEECH_MODEL_ID_TO_ENDPOINT_URL: Record<string, string> = {\n 'resembleai/chatterbox-turbo': 'https://api.runpod.ai/v2/chatterbox-turbo/',\n};\n\n// Mapping of Runpod model IDs to their OpenAI model names\nconst MODEL_ID_TO_OPENAI_NAME: Record<string, string> = {\n 'qwen/qwen3-32b-awq': 'Qwen/Qwen3-32B-AWQ',\n 'deepcogito/cogito-671b-v2.1-fp8': 'deepcogito/cogito-671b-v2.1-FP8',\n 'deepcogito/cogito-671b-v2.1-FP8': 'deepcogito/cogito-671b-v2.1-FP8',\n};\n\n/**\n * Derives the endpoint URL for a model by replacing slashes with hyphens.\n * Example: 'ibm-granite/granite-4.0-h-small' -> 'https://api.runpod.ai/v2/ibm-granite-granite-4.0-h-small/openai/v1'\n */\nfunction deriveEndpointURL(modelId: string): string {\n const normalized = modelId.replace(/\\//g, '-');\n return `https://api.runpod.ai/v2/${normalized}/openai/v1`;\n}\n\nfunction parseRunpodConsoleEndpointId(modelIdOrUrl: string): string | null {\n if (!modelIdOrUrl.startsWith('http')) {\n return null;\n }\n\n try {\n const url = new URL(modelIdOrUrl);\n if (url.hostname !== 'console.runpod.io') {\n return null;\n }\n\n // Example:\n // https://console.runpod.io/serverless/user/endpoint/<ENDPOINT_ID>\n const parts = url.pathname.split('/').filter(Boolean);\n const idx = parts.lastIndexOf('endpoint');\n const endpointId = idx !== -1 ? parts[idx + 1] : undefined;\n\n return endpointId || null;\n } catch {\n return null;\n }\n}\n\nexport function createRunpod(\n options: RunpodProviderSettings = {}\n): RunpodProvider {\n const getHeaders = () => ({\n Authorization: `Bearer ${loadApiKey({\n apiKey: options.apiKey,\n environmentVariableName: 'RUNPOD_API_KEY',\n description: 'Runpod',\n })}`,\n ...options.headers,\n });\n\n const runpodFetch: FetchFunction = async (url, requestInit) => {\n if (requestInit?.body) {\n try {\n const body = JSON.parse(requestInit.body as string);\n if (body.stream === true && !body.stream_options) {\n body.stream_options = { include_usage: true };\n requestInit.body = JSON.stringify(body);\n }\n } catch {}\n }\n const fetchFn = options.fetch || fetch;\n return fetchFn(url, requestInit);\n };\n\n interface CommonModelConfig {\n provider: string;\n url: ({ path }: { path: string }) => string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n }\n\n const getModelConfig = (\n modelId: string,\n modelType: string\n ): CommonModelConfig => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL = MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return {\n provider: `runpod.${modelType}`,\n url: ({ path }) => `${withoutTrailingSlash(baseURL)}${path}`,\n headers: getHeaders,\n fetch: runpodFetch,\n };\n };\n\n const createChatModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleChatLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'chat'),\n includeUsage: true,\n });\n };\n\n const createCompletionModel = (modelId: string) => {\n const openaiModelName = MODEL_ID_TO_OPENAI_NAME[modelId] || modelId;\n return new OpenAICompatibleCompletionLanguageModel(openaiModelName, {\n ...getModelConfig(modelId, 'completion'),\n includeUsage: true,\n });\n };\n\n const createImageModel = (modelId: string) => {\n let baseURL: string;\n\n if (options.baseURL) {\n baseURL = options.baseURL;\n } else {\n // Use hardcoded mapping if available, otherwise derive endpoint\n baseURL =\n IMAGE_MODEL_ID_TO_ENDPOINT_URL[modelId] || deriveEndpointURL(modelId);\n }\n\n return new RunpodImageModel(modelId, {\n provider: 'runpod.image',\n baseURL,\n headers: getHeaders,\n fetch: options.fetch,\n });\n };\n\n const createSpeechModel = (modelId: string) => {\n const endpointIdFromConsole = parseRunpodConsoleEndpointId(modelId);\n const normalizedModelId = endpointIdFromConsole ?? modelId;\n\n // Prefer explicit mapping for known speech model IDs.\n const mappedBaseURL = SPEECH_MODEL_ID_TO_ENDPOINT_URL[normalizedModelId];\n\n const baseURL =\n mappedBaseURL ??\n (normalizedModelId.startsWith('http')\n ? normalizedModelId\n : `https://api.runpod.ai/v2/${normalizedModelId}`);\n\n return new RunpodSpeechModel(normalizedModelId, {\n provider: 'runpod.speech',\n baseURL,\n headers: getHeaders,\n fetch: runpodFetch,\n });\n };\n\n const provider = (modelId: string) => createChatModel(modelId);\n\n provider.completionModel = createCompletionModel;\n provider.languageModel = createChatModel;\n provider.chatModel = createChatModel;\n provider.imageModel = createImageModel;\n provider.image = createImageModel;\n provider.speechModel = createSpeechModel;\n provider.speech = createSpeechModel;\n\n return provider;\n}\n\nexport const runpod = createRunpod();\n","import {\n ImageModelV3,\n ImageModelV3CallOptions,\n ImageModelV3File,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport {\n combineHeaders,\n createJsonResponseHandler,\n createBinaryResponseHandler,\n FetchFunction,\n postJsonToApi,\n getFromApi,\n} from '@ai-sdk/provider-utils';\nimport { InvalidArgumentError } from '@ai-sdk/provider';\nimport { z } from 'zod';\nimport { runpodImageFailedResponseHandler } from './runpod-error';\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\ninterface RunpodImageModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\n// Runpod supported aspect ratios (only validated working sizes)\nconst SUPPORTED_ASPECT_RATIOS: Record<string, string> = {\n '1:1': '1328*1328', // ✅ Native support\n '4:3': '1472*1140', // ✅ Native support\n '3:4': '1140*1472', // ✅ Native support\n};\n\n// Runpod supported sizes (validated working sizes)\nconst SUPPORTED_SIZES = new Set([\n // Native aspect ratio sizes\n '1328*1328', // 1:1\n '1472*1140', // 4:3\n '1140*1472', // 3:4\n // Additional validated sizes\n '512*512',\n '768*768',\n '1024*1024',\n '1536*1536',\n '2048*2048',\n '4096*4096',\n '512*768',\n '768*512',\n '1024*768',\n '768*1024',\n]);\n\nexport class RunpodImageModel implements ImageModelV3 {\n readonly specificationVersion = 'v3';\n readonly maxImagesPerCall = 1;\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private config: RunpodImageModelConfig\n ) {}\n\n async doGenerate({\n prompt,\n n,\n size,\n aspectRatio,\n seed,\n files,\n mask,\n providerOptions,\n headers,\n abortSignal,\n }: ImageModelV3CallOptions): Promise<\n Awaited<ReturnType<ImageModelV3['doGenerate']>>\n > {\n const warnings: Array<SharedV3Warning> = [];\n\n // Convert standardized files to Runpod format (base64 data URLs or raw base64)\n const standardizedImages = this.convertFilesToRunpodFormat(files);\n\n if (mask) {\n warnings.push({\n type: 'unsupported',\n feature: 'mask',\n details: 'Mask input for inpainting is not yet supported.',\n });\n }\n\n // Check if this is a Pruna model (skip standard size/aspectRatio validation)\n const isPrunaModel =\n this.modelId.includes('pruna') || this.modelId.includes('p-image');\n\n // Check if this is a Nano Banana Pro model (skip standard size/aspectRatio validation)\n const isNanoBananaProModel = this.modelId.includes('nano-banana-pro');\n\n // Determine the size to use\n let runpodSize: string;\n\n if (isPrunaModel || isNanoBananaProModel) {\n // These models use aspect_ratio string directly, skip size validation\n // Pass through the aspectRatio or use default, validation happens at API level\n runpodSize = aspectRatio || '1:1';\n } else if (size) {\n // Convert AI SDK format \"1328x1328\" to Runpod format \"1328*1328\"\n const runpodSizeCandidate = size.replace('x', '*');\n\n // Validate size is supported\n if (!SUPPORTED_SIZES.has(runpodSizeCandidate)) {\n throw new InvalidArgumentError({\n argument: 'size',\n message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(\n SUPPORTED_SIZES\n )\n .map((s) => s.replace('*', 'x'))\n .join(', ')}`,\n });\n }\n\n runpodSize = runpodSizeCandidate;\n } else if (aspectRatio) {\n // Validate aspect ratio is supported\n if (!SUPPORTED_ASPECT_RATIOS[aspectRatio]) {\n throw new InvalidArgumentError({\n argument: 'aspectRatio',\n message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(SUPPORTED_ASPECT_RATIOS).join(', ')}`,\n });\n }\n\n // Use supported aspect ratio mapping\n runpodSize = SUPPORTED_ASPECT_RATIOS[aspectRatio];\n } else {\n // Default to square format\n runpodSize = '1328*1328';\n }\n\n // Handle multiple images warning\n if (n > 1) {\n warnings.push({\n type: 'unsupported',\n feature: 'multiple images (n > 1)',\n details:\n 'Runpod image models only support generating 1 image at a time. Using n=1.',\n });\n }\n\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n // Runpod uses a different request format - /runsync endpoint with input wrapper\n const inputPayload = this.buildInputPayload(\n prompt ?? '',\n runpodSize,\n seed,\n providerOptions.runpod as Record<string, unknown> | undefined,\n aspectRatio,\n standardizedImages\n );\n\n // Determine the effective baseURL (may switch for LoRA models)\n let effectiveBaseURL = this.config.baseURL;\n const runpodOptions = providerOptions.runpod as\n | Record<string, unknown>\n | undefined;\n if (\n this.modelId.includes('qwen-image-edit-2511') &&\n !this.modelId.includes('lora') &&\n runpodOptions?.loras &&\n Array.isArray(runpodOptions.loras) &&\n runpodOptions.loras.length > 0\n ) {\n // Switch to LoRA endpoint when loras are provided\n effectiveBaseURL = this.config.baseURL.replace(\n 'qwen-image-edit-2511',\n 'qwen-image-edit-2511-lora'\n );\n }\n\n const { value: response, responseHeaders } = await postJsonToApi({\n url: `${effectiveBaseURL}/runsync`,\n headers: combineHeaders(this.config.headers(), headers),\n body: {\n input: inputPayload,\n },\n failedResponseHandler: runpodImageFailedResponseHandler,\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageResponseSchema as any\n ),\n abortSignal,\n fetch: this.config.fetch,\n });\n\n // Handle both sync and async responses from Runpod\n const typedResponse = response as any;\n if (\n typedResponse.status === 'COMPLETED' &&\n (typedResponse.output?.result || typedResponse.output?.image_url)\n ) {\n // Sync response - image is ready\n // Different models use different response formats: result vs image_url\n const imageUrl =\n typedResponse.output.result || typedResponse.output.image_url;\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n cost: typedResponse.output?.cost,\n },\n ],\n },\n },\n };\n } else if (\n typedResponse.status === 'IN_QUEUE' ||\n typedResponse.status === 'IN_PROGRESS'\n ) {\n // Async response - need to poll for completion\n const pollOptions = {\n maxAttempts: providerOptions.runpod?.maxPollAttempts as number,\n pollIntervalMillis: providerOptions.runpod\n ?.pollIntervalMillis as number,\n };\n const imageUrl = await this.pollForCompletion(\n typedResponse.id,\n abortSignal,\n pollOptions,\n effectiveBaseURL\n );\n const imageData = await this.downloadImage(imageUrl, abortSignal);\n\n return {\n images: [imageData],\n warnings,\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders,\n },\n providerMetadata: {\n runpod: {\n images: [\n {\n url: imageUrl,\n jobId: typedResponse.id,\n },\n ],\n },\n },\n };\n } else {\n throw new Error(`Unexpected response status: ${typedResponse.status}`);\n }\n }\n\n private async downloadImage(\n imageUrl: string,\n abortSignal?: AbortSignal\n ): Promise<Uint8Array> {\n const { value: imageData } = await getFromApi({\n url: imageUrl,\n successfulResponseHandler: createBinaryResponseHandler(),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n return imageData;\n }\n\n private async pollForCompletion(\n jobId: string,\n abortSignal?: AbortSignal,\n pollOptions?: { maxAttempts?: number; pollIntervalMillis?: number },\n effectiveBaseURL?: string\n ): Promise<string> {\n const maxAttempts = pollOptions?.maxAttempts ?? 60; // 5 minutes with 5-second intervals\n const pollInterval = pollOptions?.pollIntervalMillis ?? 5000; // 5 seconds\n const baseURL = effectiveBaseURL ?? this.config.baseURL;\n\n for (let attempt = 0; attempt < maxAttempts; attempt++) {\n if (abortSignal?.aborted) {\n throw new Error('Image generation was aborted');\n }\n\n const { value: statusResponse } = await getFromApi({\n url: `${baseURL}/status/${jobId}`,\n headers: this.config.headers(),\n successfulResponseHandler: createJsonResponseHandler(\n runpodImageStatusSchema as any\n ),\n failedResponseHandler: runpodImageFailedResponseHandler,\n abortSignal,\n fetch: this.config.fetch,\n });\n\n const typedStatusResponse = statusResponse as any;\n if (\n typedStatusResponse.status === 'COMPLETED' &&\n (typedStatusResponse.output?.result ||\n typedStatusResponse.output?.image_url)\n ) {\n return (\n typedStatusResponse.output.result ||\n typedStatusResponse.output.image_url\n );\n }\n\n if (typedStatusResponse.status === 'FAILED') {\n throw new Error(\n `Image generation failed: ${typedStatusResponse.error || 'Unknown error'}`\n );\n }\n\n // Wait before next poll\n await new Promise((resolve) => setTimeout(resolve, pollInterval));\n }\n\n throw new Error(\n `Image generation timed out after ${maxAttempts} attempts (${(maxAttempts * pollInterval) / 1000}s)`\n );\n }\n\n /**\n * Converts standardized ImageModelV3File[] to Runpod-compatible format.\n * Returns URLs or base64 data URLs that Runpod API accepts.\n */\n private convertFilesToRunpodFormat(\n files: ImageModelV3File[] | undefined\n ): string[] | undefined {\n if (!files || files.length === 0) {\n return undefined;\n }\n\n return files.map((file) => {\n // Handle URL type - return URL directly\n if (file.type === 'url') {\n return file.url;\n }\n\n // Handle file type with data\n if (typeof file.data === 'string') {\n // If it's already a data URL, return as-is\n if (file.data.startsWith('data:')) {\n return file.data;\n }\n // Otherwise, wrap as data URL with media type\n return `data:${file.mediaType};base64,${file.data}`;\n }\n\n // Convert Uint8Array to base64 data URL\n const base64 = this.uint8ArrayToBase64(file.data);\n return `data:${file.mediaType};base64,${base64}`;\n });\n }\n\n /**\n * Converts Uint8Array to base64 string.\n */\n private uint8ArrayToBase64(data: Uint8Array): string {\n let binary = '';\n for (let i = 0; i < data.length; i++) {\n binary += String.fromCharCode(data[i]);\n }\n return btoa(binary);\n }\n\n private buildInputPayload(\n prompt: string,\n runpodSize: string,\n seed?: number,\n runpodOptions?: Record<string, unknown>,\n aspectRatio?: string,\n standardizedImages?: string[]\n ): Record<string, unknown> {\n // Check if this is a Flux model that uses different parameters\n const isFluxModel =\n this.modelId.includes('flux') ||\n this.modelId.includes('black-forest-labs');\n\n if (isFluxModel) {\n // Check if this is Flux Kontext (uses different parameters)\n const isKontext = this.modelId.includes('kontext');\n\n if (isKontext) {\n // Flux Kontext uses size format and has image input\n // Prioritize standardized files over providerOptions\n const kontextPayload: Record<string, unknown> = {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: 28,\n guidance: 2,\n size: runpodSize,\n output_format: 'png',\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n\n // Use standardized files if provided (first image), otherwise use providerOptions.image\n if (standardizedImages && standardizedImages.length > 0) {\n kontextPayload.image = standardizedImages[0];\n }\n\n return kontextPayload;\n } else {\n // Regular Flux models use width/height\n const [width, height] = runpodSize.split('*').map(Number);\n\n return {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n seed: seed ?? -1,\n num_inference_steps: this.modelId.includes('schnell') ? 4 : 28,\n guidance: this.modelId.includes('schnell') ? 7 : 2,\n width,\n height,\n image_format: 'png',\n ...runpodOptions,\n };\n }\n }\n\n // Check if this is a Pruna model\n const isPrunaModel =\n this.modelId.includes('pruna') || this.modelId.includes('p-image');\n if (isPrunaModel) {\n const isPrunaEdit = this.modelId.includes('edit');\n\n if (isPrunaEdit) {\n // Pruna image edit\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\"\n // Supports 1-5 images via providerOptions.runpod.images\n const editPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n disable_safety_checker:\n (runpodOptions?.disable_safety_checker as boolean) ?? false,\n };\n\n // Add seed if provided\n if (seed !== undefined) {\n editPayload.seed = seed;\n } else if (runpodOptions?.seed !== undefined) {\n editPayload.seed = runpodOptions.seed;\n }\n\n // Use standardized files if provided, otherwise use providerOptions.images\n if (standardizedImages && standardizedImages.length > 0) {\n editPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n editPayload.images = runpodOptions.images;\n }\n\n return editPayload;\n } else {\n // Pruna text-to-image\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"custom\"\n // For custom: width/height 256-1440, must be multiple of 16\n const t2iPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n disable_safety_checker:\n (runpodOptions?.disable_safety_checker as boolean) ?? false,\n };\n\n // Add seed if provided\n if (seed !== undefined) {\n t2iPayload.seed = seed;\n } else if (runpodOptions?.seed !== undefined) {\n t2iPayload.seed = runpodOptions.seed;\n }\n\n // Handle custom aspect ratio with width/height\n if (t2iPayload.aspect_ratio === 'custom') {\n if (runpodOptions?.width) {\n t2iPayload.width = runpodOptions.width;\n }\n if (runpodOptions?.height) {\n t2iPayload.height = runpodOptions.height;\n }\n }\n\n return t2iPayload;\n }\n }\n\n // Check if this is a Nano Banana Pro model (google/nano-banana-pro-edit)\n const isNanaBananaProModel = this.modelId.includes('nano-banana-pro');\n if (isNanaBananaProModel) {\n // Nano Banana Pro image edit\n // Supported aspect_ratio: \"1:1\", \"16:9\", \"9:16\", \"4:3\", \"3:4\", \"3:2\", \"2:3\", \"21:9\", \"9:21\"\n // Supported resolution: \"1k\", \"2k\", \"4k\"\n // Supported output_format: \"jpeg\", \"png\", \"webp\"\n const nanoBananaPayload: Record<string, unknown> = {\n prompt,\n aspect_ratio:\n (runpodOptions?.aspect_ratio as string) ?? aspectRatio ?? '1:1',\n resolution: (runpodOptions?.resolution as string) ?? '1k',\n output_format: (runpodOptions?.output_format as string) ?? 'jpeg',\n enable_base64_output:\n (runpodOptions?.enable_base64_output as boolean) ?? false,\n enable_sync_mode:\n (runpodOptions?.enable_sync_mode as boolean) ?? false,\n };\n\n // Use standardized files if provided, otherwise use providerOptions.images\n if (standardizedImages && standardizedImages.length > 0) {\n nanoBananaPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n nanoBananaPayload.images = runpodOptions.images;\n }\n\n return nanoBananaPayload;\n }\n\n // Check if this is a Nano Banana (non-pro) edit model\n const isNanaBananaEditModel =\n this.modelId.includes('nano-banana-edit') &&\n !this.modelId.includes('nano-banana-pro');\n if (isNanaBananaEditModel) {\n // Nano Banana edit uses simple format: prompt, images array, enable_safety_checker\n const nanoBananaEditPayload: Record<string, unknown> = {\n prompt,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n };\n\n // Always use images array format\n if (standardizedImages && standardizedImages.length > 0) {\n nanoBananaEditPayload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n nanoBananaEditPayload.images = runpodOptions.images;\n }\n\n return nanoBananaEditPayload;\n }\n\n // Check if this is a Qwen Image Edit 2511 model (uses images array format)\n const isQwenImageEdit2511 = this.modelId.includes('qwen-image-edit-2511');\n if (isQwenImageEdit2511) {\n // Qwen Image Edit 2511 uses images array, output_format, and sync options\n const qwenEdit2511Payload: Record<string, unknown> = {\n prompt,\n size: runpodSize,\n seed: seed ?? -1,\n output_format: (runpodOptions?.output_format as string) ?? 'jpeg',\n enable_base64_output:\n (runpodOptions?.enable_base64_output as boolean) ?? false,\n enable_sync_mode:\n (runpodOptions?.enable_sync_mode as boolean) ?? false,\n ...runpodOptions,\n };\n\n // Always use images array format for this model\n if (standardizedImages && standardizedImages.length > 0) {\n qwenEdit2511Payload.images = standardizedImages;\n } else if (runpodOptions?.images) {\n qwenEdit2511Payload.images = runpodOptions.images;\n }\n\n return qwenEdit2511Payload;\n }\n\n // Check if this is an Alibaba Wan model\n const isWanModel = this.modelId.includes('wan-2');\n if (isWanModel) {\n // Alibaba Wan 2.6 uses standard t2i format with negative prompt in prompt string\n return {\n prompt,\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n }\n\n // Default format for Qwen and other models\n const defaultPayload: Record<string, unknown> = {\n prompt,\n negative_prompt: runpodOptions?.negative_prompt ?? '',\n size: runpodSize,\n seed: seed ?? -1,\n enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,\n ...runpodOptions,\n };\n\n // For edit models, use standardized files if provided\n if (standardizedImages && standardizedImages.length > 0) {\n // Single image models use 'image', multi-image models use 'images'\n if (standardizedImages.length === 1) {\n defaultPayload.image = standardizedImages[0];\n } else {\n defaultPayload.images = standardizedImages;\n }\n }\n\n return defaultPayload;\n }\n}\n\n// Runpod image API response schema (handles both sync and async responses)\nconst runpodImageResponseSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n delayTime: z.number().optional(),\n executionTime: z.number().optional(),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(), // URL to the generated image (Qwen format)\n image_url: z.string().optional(), // URL to the generated image (Flux format)\n })\n .optional(), // Optional for IN_QUEUE/IN_PROGRESS responses\n});\n\n// Schema for polling status endpoint\nconst runpodImageStatusSchema = z.object({\n id: z.string(),\n status: z.enum(['COMPLETED', 'IN_QUEUE', 'IN_PROGRESS', 'FAILED']),\n output: z\n .object({\n cost: z.number().optional(),\n result: z.string().optional(),\n image_url: z.string().optional(),\n })\n .optional(),\n error: z.string().optional(), // Error message if FAILED\n});\n","import { z } from 'zod';\r\nimport { createJsonErrorResponseHandler } from '@ai-sdk/provider-utils';\r\n\r\n// Runpod image API error schema (supports both error formats)\r\nexport const runpodImageErrorSchema = z.object({\r\n error: z.string().optional(),\r\n message: z.string().optional(),\r\n});\r\n\r\nexport type RunpodImageErrorData = z.infer<typeof runpodImageErrorSchema>;\r\n\r\nexport const runpodImageFailedResponseHandler = createJsonErrorResponseHandler({\r\n errorSchema: runpodImageErrorSchema as any,\r\n errorToMessage: (data: RunpodImageErrorData) => {\r\n // Prefer message if available (more descriptive)\r\n if (data.message) {\r\n return data.message;\r\n }\r\n \r\n // If error field exists, try to extract nested JSON message\r\n if (data.error) {\r\n // Runpod sometimes returns nested JSON in the error field like:\r\n // \"Error submitting task: 400, {\\\"code\\\":400,\\\"message\\\":\\\"...\\\"}\"\r\n // Try to extract the inner message for cleaner error messages\r\n // Find the last occurrence of { which likely starts the JSON object\r\n const lastBraceIndex = data.error.lastIndexOf('{');\r\n if (lastBraceIndex !== -1) {\r\n try {\r\n const jsonStr = data.error.substring(lastBraceIndex);\r\n const nestedError = JSON.parse(jsonStr);\r\n if (nestedError.message && typeof nestedError.message === 'string') {\r\n return nestedError.message;\r\n }\r\n } catch {\r\n // If parsing fails, fall back to the original error string\r\n }\r\n }\r\n return data.error;\r\n }\r\n \r\n return 'Unknown Runpod error';\r\n },\r\n});\r\n\r\n","import {\n JSONValue,\n SpeechModelV3,\n SpeechModelV3CallOptions,\n SharedV3Warning,\n} from '@ai-sdk/provider';\nimport { FetchFunction, withoutTrailingSlash } from '@ai-sdk/provider-utils';\n\nexport interface RunpodSpeechModelConfig {\n provider: string;\n baseURL: string;\n headers: () => Record<string, string>;\n fetch?: FetchFunction;\n _internal?: {\n currentDate?: () => Date;\n };\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return typeof value === 'object' && value !== null;\n}\n\nfunction replaceNewlinesWithSpaces(value: string): string {\n return value.replace(/[\\r\\n]+/g, ' ');\n}\n\nexport class RunpodSpeechModel implements SpeechModelV3 {\n readonly specificationVersion = 'v3';\n\n get provider(): string {\n return this.config.provider;\n }\n\n constructor(\n readonly modelId: string,\n private readonly config: RunpodSpeechModelConfig\n ) {}\n\n private getRunpodRunSyncUrl(): string {\n // `withoutTrailingSlash` is typed to potentially return undefined, but in our\n // case `config.baseURL` is always a string.\n const baseURL =\n withoutTrailingSlash(this.config.baseURL) ?? this.config.baseURL;\n\n // Allow users to pass /run or /runsync directly.\n if (baseURL.endsWith('/run') || baseURL.endsWith('/runsync')) {\n return baseURL;\n }\n\n return `${baseURL}/runsync`;\n }\n\n async doGenerate(\n options: SpeechModelV3CallOptions\n ): Promise<Awaited<ReturnType<SpeechModelV3['doGenerate']>>> {\n const currentDate = this.config._internal?.currentDate?.() ?? new Date();\n\n const warnings: SharedV3Warning[] = [];\n\n const {\n text,\n voice,\n outputFormat,\n instructions,\n speed,\n language,\n providerOptions,\n abortSignal,\n headers,\n } = options;\n\n // This endpoint currently returns wav. Warn and ignore other formats.\n if (outputFormat != null && outputFormat !== 'wav') {\n warnings.push({\n type: 'unsupported',\n feature: 'outputFormat',\n details: `Unsupported outputFormat: ${outputFormat}. This endpoint returns 'wav'.`,\n });\n }\n\n if (instructions != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'instructions',\n details: `Instructions are not supported by this speech endpoint.`,\n });\n }\n\n if (speed != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'speed',\n details: `Speed is not supported by this speech endpoint.`,\n });\n }\n\n if (language != null) {\n warnings.push({\n type: 'unsupported',\n feature: 'language',\n details: `Language selection is not supported by this speech endpoint.`,\n });\n }\n\n const runpodProviderOptions = isRecord(providerOptions)\n ? (providerOptions as any).runpod\n : undefined;\n\n const voiceUrl =\n isRecord(runpodProviderOptions) &&\n (typeof runpodProviderOptions.voice_url === 'string' ||\n typeof runpodProviderOptions.voiceUrl === 'string')\n ? (runpodProviderOptions.voice_url ??\n runpodProviderOptions.voiceUrl ??\n undefined)\n : undefined;\n\n const input: Record<string, unknown> = {\n prompt: replaceNewlinesWithSpaces(text),\n };\n\n // The endpoint supports either a built-in voice name or a voice_url prompt.\n if (voiceUrl) {\n input.voice_url = voiceUrl;\n } else if (voice) {\n input.voice = voice;\n }\n\n const requestBody = { input };\n const url = this.getRunpodRunSyncUrl();\n\n const fetchFn = this.config.fetch ?? fetch;\n\n const requestHeaders: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...this.config.headers(),\n };\n\n if (headers) {\n for (const [key, value] of Object.entries(headers)) {\n if (value != null) {\n requestHeaders[key] = value;\n }\n }\n }\n\n const response = await fetchFn(url, {\n method: 'POST',\n headers: requestHeaders,\n body: JSON.stringify(requestBody),\n signal: abortSignal,\n });\n\n const responseHeaders = Object.fromEntries(response.headers.entries());\n const rawBodyText = await response.text();\n\n let parsed: any = undefined;\n try {\n parsed = rawBodyText ? JSON.parse(rawBodyText) : undefined;\n } catch {\n // ignore - we'll surface the raw body in the error below\n }\n\n if (!response.ok) {\n const message =\n (parsed && typeof parsed.error === 'string' && parsed.error) ||\n rawBodyText ||\n `HTTP ${response.status}`;\n throw new Error(`Runpod speech request failed: ${message}`);\n }\n\n const output = parsed?.output ?? parsed;\n\n const audioUrl = output?.audio_url;\n if (typeof audioUrl !== 'string' || audioUrl.length === 0) {\n throw new Error('Runpod speech response did not include an audio_url.');\n }\n\n const audioResponse = await fetchFn(audioUrl, { signal: abortSignal });\n if (!audioResponse.ok) {\n throw new Error(\n `Failed to download generated audio (${audioResponse.status}).`\n );\n }\n\n const audio = new Uint8Array(await audioResponse.arrayBuffer());\n\n const providerMetadata: Record<string, Record<string, JSONValue>> = {\n runpod: {\n audioUrl,\n ...(typeof output?.cost === 'number' ? { cost: output.cost } : {}),\n },\n };\n\n return {\n audio,\n warnings,\n request: {\n body: JSON.stringify(requestBody),\n },\n response: {\n timestamp: currentDate,\n modelId: this.modelId,\n headers: responseHeaders as any,\n body: rawBodyText,\n },\n providerMetadata,\n };\n }\n}\n"],"mappings":"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","names":["withoutTrailingSlash","z","z","withoutTrailingSlash"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@runpod/ai-sdk-provider",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "license": "Apache-2.0",
5
5
  "sideEffects": false,
6
6
  "main": "./dist/index.js",