@runpod/ai-sdk-provider 1.1.0 → 1.3.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 +16 -0
- package/README.md +236 -16
- package/dist/index.d.mts +113 -3
- package/dist/index.d.ts +113 -3
- package/dist/index.js +649 -27
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +658 -26
- package/dist/index.mjs.map +1 -1
- package/package.json +10 -10
package/dist/index.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "@ai-sdk/openai-compatible";
|
|
6
6
|
import {
|
|
7
7
|
loadApiKey,
|
|
8
|
-
withoutTrailingSlash as
|
|
8
|
+
withoutTrailingSlash as withoutTrailingSlash4
|
|
9
9
|
} from "@ai-sdk/provider-utils";
|
|
10
10
|
|
|
11
11
|
// src/runpod-image-model.ts
|
|
@@ -26,28 +26,37 @@ var runpodImageErrorSchema = z.object({
|
|
|
26
26
|
error: z.string().optional(),
|
|
27
27
|
message: z.string().optional()
|
|
28
28
|
});
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (nestedError.message && typeof nestedError.message === "string") {
|
|
42
|
-
return nestedError.message;
|
|
43
|
-
}
|
|
44
|
-
} catch {
|
|
29
|
+
function extractErrorMessage(data) {
|
|
30
|
+
if (data.message) {
|
|
31
|
+
return data.message;
|
|
32
|
+
}
|
|
33
|
+
if (data.error) {
|
|
34
|
+
const lastBraceIndex = data.error.lastIndexOf("{");
|
|
35
|
+
if (lastBraceIndex !== -1) {
|
|
36
|
+
try {
|
|
37
|
+
const jsonStr = data.error.substring(lastBraceIndex);
|
|
38
|
+
const nestedError = JSON.parse(jsonStr);
|
|
39
|
+
if (nestedError.message && typeof nestedError.message === "string") {
|
|
40
|
+
return nestedError.message;
|
|
45
41
|
}
|
|
42
|
+
} catch {
|
|
46
43
|
}
|
|
47
|
-
return data.error;
|
|
48
44
|
}
|
|
49
|
-
return
|
|
45
|
+
return data.error;
|
|
50
46
|
}
|
|
47
|
+
return "Unknown Runpod error";
|
|
48
|
+
}
|
|
49
|
+
var runpodImageFailedResponseHandler = createJsonErrorResponseHandler({
|
|
50
|
+
errorSchema: runpodImageErrorSchema,
|
|
51
|
+
errorToMessage: extractErrorMessage
|
|
52
|
+
});
|
|
53
|
+
var runpodTranscriptionFailedResponseHandler = createJsonErrorResponseHandler({
|
|
54
|
+
errorSchema: runpodImageErrorSchema,
|
|
55
|
+
errorToMessage: extractErrorMessage
|
|
56
|
+
});
|
|
57
|
+
var runpodVideoFailedResponseHandler = createJsonErrorResponseHandler({
|
|
58
|
+
errorSchema: runpodImageErrorSchema,
|
|
59
|
+
errorToMessage: extractErrorMessage
|
|
51
60
|
});
|
|
52
61
|
|
|
53
62
|
// src/runpod-image-model.ts
|
|
@@ -71,6 +80,8 @@ var SUPPORTED_SIZES = /* @__PURE__ */ new Set([
|
|
|
71
80
|
"512*512",
|
|
72
81
|
"768*768",
|
|
73
82
|
"1024*1024",
|
|
83
|
+
"1280*1280",
|
|
84
|
+
// wan-2.6 max
|
|
74
85
|
"1536*1536",
|
|
75
86
|
"2048*2048",
|
|
76
87
|
"4096*4096",
|
|
@@ -79,6 +90,100 @@ var SUPPORTED_SIZES = /* @__PURE__ */ new Set([
|
|
|
79
90
|
"1024*768",
|
|
80
91
|
"768*1024"
|
|
81
92
|
]);
|
|
93
|
+
var Z_IMAGE_TURBO_SUPPORTED_SIZES = /* @__PURE__ */ new Set([
|
|
94
|
+
"1328*1328",
|
|
95
|
+
// 1:1
|
|
96
|
+
"1472*1140",
|
|
97
|
+
// 4:3
|
|
98
|
+
"1140*1472",
|
|
99
|
+
// 3:4
|
|
100
|
+
"512*512",
|
|
101
|
+
"768*768",
|
|
102
|
+
"1024*1024",
|
|
103
|
+
"1280*1280",
|
|
104
|
+
"1536*1536",
|
|
105
|
+
"512*768",
|
|
106
|
+
"768*512",
|
|
107
|
+
"1024*768",
|
|
108
|
+
"768*1024",
|
|
109
|
+
"768*432",
|
|
110
|
+
"1024*576",
|
|
111
|
+
"1280*720",
|
|
112
|
+
"1536*864",
|
|
113
|
+
"432*768",
|
|
114
|
+
"576*1024",
|
|
115
|
+
"720*1280",
|
|
116
|
+
"864*1536"
|
|
117
|
+
]);
|
|
118
|
+
var Z_IMAGE_TURBO_ASPECT_RATIOS = {
|
|
119
|
+
"1:1": "1328*1328",
|
|
120
|
+
"4:3": "1472*1140",
|
|
121
|
+
"3:4": "1140*1472",
|
|
122
|
+
"3:2": "768*512",
|
|
123
|
+
"2:3": "512*768",
|
|
124
|
+
"16:9": "1280*720",
|
|
125
|
+
"9:16": "720*1280"
|
|
126
|
+
};
|
|
127
|
+
var MODEL_SUPPORTED_SIZES = {
|
|
128
|
+
"tongyi-mai/z-image-turbo": Z_IMAGE_TURBO_SUPPORTED_SIZES,
|
|
129
|
+
"z-image-turbo": Z_IMAGE_TURBO_SUPPORTED_SIZES
|
|
130
|
+
// alias, not advertised
|
|
131
|
+
};
|
|
132
|
+
var MODEL_SUPPORTED_ASPECT_RATIOS = {
|
|
133
|
+
"tongyi-mai/z-image-turbo": Z_IMAGE_TURBO_ASPECT_RATIOS,
|
|
134
|
+
"z-image-turbo": Z_IMAGE_TURBO_ASPECT_RATIOS
|
|
135
|
+
// alias, not advertised
|
|
136
|
+
};
|
|
137
|
+
var WAN_ASPECT_RATIOS = {
|
|
138
|
+
"1:1": "1280*1280",
|
|
139
|
+
// 1,638,400 pixels
|
|
140
|
+
"2:3": "800*1200",
|
|
141
|
+
// 960,000 pixels
|
|
142
|
+
"3:2": "1200*800",
|
|
143
|
+
// 960,000 pixels
|
|
144
|
+
"3:4": "960*1280",
|
|
145
|
+
// 1,228,800 pixels
|
|
146
|
+
"4:3": "1280*960",
|
|
147
|
+
// 1,228,800 pixels
|
|
148
|
+
"9:16": "720*1280",
|
|
149
|
+
// 921,600 pixels
|
|
150
|
+
"16:9": "1280*720",
|
|
151
|
+
// 921,600 pixels
|
|
152
|
+
"21:9": "1344*576",
|
|
153
|
+
// 774,144 pixels
|
|
154
|
+
"9:21": "576*1344"
|
|
155
|
+
// 774,144 pixels
|
|
156
|
+
};
|
|
157
|
+
var WAN_MIN_PIXELS = 768 * 768;
|
|
158
|
+
var WAN_MAX_PIXELS = 1280 * 1280;
|
|
159
|
+
var WAN_MIN_ASPECT_RATIO = 1 / 4;
|
|
160
|
+
var WAN_MAX_ASPECT_RATIO = 4 / 1;
|
|
161
|
+
function validateWanSize(size) {
|
|
162
|
+
const [widthStr, heightStr] = size.split("*");
|
|
163
|
+
const width = parseInt(widthStr, 10);
|
|
164
|
+
const height = parseInt(heightStr, 10);
|
|
165
|
+
if (isNaN(width) || isNaN(height)) {
|
|
166
|
+
throw new InvalidArgumentError({
|
|
167
|
+
argument: "size",
|
|
168
|
+
message: `Invalid size format: ${size}. Expected format: WIDTHxHEIGHT or WIDTH*HEIGHT`
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
const totalPixels = width * height;
|
|
172
|
+
const aspectRatio = width / height;
|
|
173
|
+
if (totalPixels < WAN_MIN_PIXELS || totalPixels > WAN_MAX_PIXELS) {
|
|
174
|
+
throw new InvalidArgumentError({
|
|
175
|
+
argument: "size",
|
|
176
|
+
message: `Size ${size} has ${totalPixels} pixels, which is outside the valid range for WAN 2.6 (${WAN_MIN_PIXELS} to ${WAN_MAX_PIXELS} pixels). Try 1280x1280 or 1024x1024.`
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
if (aspectRatio < WAN_MIN_ASPECT_RATIO || aspectRatio > WAN_MAX_ASPECT_RATIO) {
|
|
180
|
+
throw new InvalidArgumentError({
|
|
181
|
+
argument: "size",
|
|
182
|
+
message: `Size ${size} has aspect ratio ${aspectRatio.toFixed(2)}, which is outside the valid range for WAN 2.6 (1:4 to 4:1).`
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
return true;
|
|
186
|
+
}
|
|
82
187
|
var RunpodImageModel = class {
|
|
83
188
|
constructor(modelId, config) {
|
|
84
189
|
this.modelId = modelId;
|
|
@@ -112,28 +217,49 @@ var RunpodImageModel = class {
|
|
|
112
217
|
}
|
|
113
218
|
const isPrunaModel = this.modelId.includes("pruna") || this.modelId.includes("p-image");
|
|
114
219
|
const isNanoBananaProModel = this.modelId.includes("nano-banana-pro");
|
|
220
|
+
const isWanModel = this.modelId.includes("wan-2");
|
|
115
221
|
let runpodSize;
|
|
116
222
|
if (isPrunaModel || isNanoBananaProModel) {
|
|
117
223
|
runpodSize = aspectRatio || "1:1";
|
|
224
|
+
} else if (isWanModel) {
|
|
225
|
+
if (size) {
|
|
226
|
+
const runpodSizeCandidate = size.replace("x", "*");
|
|
227
|
+
validateWanSize(runpodSizeCandidate);
|
|
228
|
+
runpodSize = runpodSizeCandidate;
|
|
229
|
+
} else if (aspectRatio) {
|
|
230
|
+
if (!WAN_ASPECT_RATIOS[aspectRatio]) {
|
|
231
|
+
throw new InvalidArgumentError({
|
|
232
|
+
argument: "aspectRatio",
|
|
233
|
+
message: `Aspect ratio ${aspectRatio} is not supported by WAN 2.6. Supported aspect ratios: ${Object.keys(WAN_ASPECT_RATIOS).join(", ")}`
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
runpodSize = WAN_ASPECT_RATIOS[aspectRatio];
|
|
237
|
+
} else {
|
|
238
|
+
runpodSize = "1280*1280";
|
|
239
|
+
}
|
|
118
240
|
} else if (size) {
|
|
119
241
|
const runpodSizeCandidate = size.replace("x", "*");
|
|
120
|
-
|
|
242
|
+
const supportedSizes = MODEL_SUPPORTED_SIZES[this.modelId] ?? SUPPORTED_SIZES;
|
|
243
|
+
if (!supportedSizes.has(runpodSizeCandidate)) {
|
|
121
244
|
throw new InvalidArgumentError({
|
|
122
245
|
argument: "size",
|
|
123
246
|
message: `Size ${size} is not supported by Runpod. Supported sizes: ${Array.from(
|
|
124
|
-
|
|
247
|
+
supportedSizes
|
|
125
248
|
).map((s) => s.replace("*", "x")).join(", ")}`
|
|
126
249
|
});
|
|
127
250
|
}
|
|
128
251
|
runpodSize = runpodSizeCandidate;
|
|
129
252
|
} else if (aspectRatio) {
|
|
130
|
-
|
|
253
|
+
const supportedAspectRatios = MODEL_SUPPORTED_ASPECT_RATIOS[this.modelId] ?? SUPPORTED_ASPECT_RATIOS;
|
|
254
|
+
if (!supportedAspectRatios[aspectRatio]) {
|
|
131
255
|
throw new InvalidArgumentError({
|
|
132
256
|
argument: "aspectRatio",
|
|
133
|
-
message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(
|
|
257
|
+
message: `Aspect ratio ${aspectRatio} is not supported by Runpod. Supported aspect ratios: ${Object.keys(
|
|
258
|
+
supportedAspectRatios
|
|
259
|
+
).join(", ")}`
|
|
134
260
|
});
|
|
135
261
|
}
|
|
136
|
-
runpodSize =
|
|
262
|
+
runpodSize = supportedAspectRatios[aspectRatio];
|
|
137
263
|
} else {
|
|
138
264
|
runpodSize = "1328*1328";
|
|
139
265
|
}
|
|
@@ -228,6 +354,10 @@ var RunpodImageModel = class {
|
|
|
228
354
|
}
|
|
229
355
|
}
|
|
230
356
|
};
|
|
357
|
+
} else if (typedResponse.status === "FAILED") {
|
|
358
|
+
throw new Error(
|
|
359
|
+
`Image generation failed: ${typedResponse.error || "Unknown error"}`
|
|
360
|
+
);
|
|
231
361
|
} else {
|
|
232
362
|
throw new Error(`Unexpected response status: ${typedResponse.status}`);
|
|
233
363
|
}
|
|
@@ -432,6 +562,25 @@ var RunpodImageModel = class {
|
|
|
432
562
|
}
|
|
433
563
|
return qwenEdit2511Payload;
|
|
434
564
|
}
|
|
565
|
+
const isZImageTurbo = this.modelId === "tongyi-mai/z-image-turbo" || this.modelId === "z-image-turbo";
|
|
566
|
+
if (isZImageTurbo) {
|
|
567
|
+
const zImageTurboPayload = {
|
|
568
|
+
prompt,
|
|
569
|
+
size: runpodSize,
|
|
570
|
+
seed: seed ?? -1,
|
|
571
|
+
output_format: runpodOptions?.output_format ?? "png",
|
|
572
|
+
enable_safety_checker: runpodOptions?.enable_safety_checker ?? true,
|
|
573
|
+
...runpodOptions
|
|
574
|
+
};
|
|
575
|
+
if (standardizedImages && standardizedImages.length > 0) {
|
|
576
|
+
if (standardizedImages.length === 1) {
|
|
577
|
+
zImageTurboPayload.image = standardizedImages[0];
|
|
578
|
+
} else {
|
|
579
|
+
zImageTurboPayload.images = standardizedImages;
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
return zImageTurboPayload;
|
|
583
|
+
}
|
|
435
584
|
const isWanModel = this.modelId.includes("wan-2");
|
|
436
585
|
if (isWanModel) {
|
|
437
586
|
return {
|
|
@@ -471,8 +620,10 @@ var runpodImageResponseSchema = z2.object({
|
|
|
471
620
|
// URL to the generated image (Qwen format)
|
|
472
621
|
image_url: z2.string().optional()
|
|
473
622
|
// URL to the generated image (Flux format)
|
|
474
|
-
}).optional()
|
|
623
|
+
}).optional(),
|
|
475
624
|
// Optional for IN_QUEUE/IN_PROGRESS responses
|
|
625
|
+
error: z2.string().optional()
|
|
626
|
+
// Error message if FAILED
|
|
476
627
|
});
|
|
477
628
|
var runpodImageStatusSchema = z2.object({
|
|
478
629
|
id: z2.string(),
|
|
@@ -593,6 +744,11 @@ var RunpodSpeechModel = class {
|
|
|
593
744
|
const message = parsed && typeof parsed.error === "string" && parsed.error || rawBodyText || `HTTP ${response.status}`;
|
|
594
745
|
throw new Error(`Runpod speech request failed: ${message}`);
|
|
595
746
|
}
|
|
747
|
+
if (parsed?.status === "FAILED") {
|
|
748
|
+
throw new Error(
|
|
749
|
+
`Speech generation failed: ${parsed.error || "Unknown error"}`
|
|
750
|
+
);
|
|
751
|
+
}
|
|
596
752
|
const output = parsed?.output ?? parsed;
|
|
597
753
|
const audioUrl = output?.audio_url;
|
|
598
754
|
if (typeof audioUrl !== "string" || audioUrl.length === 0) {
|
|
@@ -628,6 +784,430 @@ var RunpodSpeechModel = class {
|
|
|
628
784
|
}
|
|
629
785
|
};
|
|
630
786
|
|
|
787
|
+
// src/runpod-transcription-model.ts
|
|
788
|
+
import {
|
|
789
|
+
withoutTrailingSlash as withoutTrailingSlash2,
|
|
790
|
+
createJsonResponseHandler as createJsonResponseHandler2,
|
|
791
|
+
getFromApi as getFromApi2,
|
|
792
|
+
postJsonToApi as postJsonToApi2
|
|
793
|
+
} from "@ai-sdk/provider-utils";
|
|
794
|
+
import { z as z3 } from "zod";
|
|
795
|
+
function isRecord2(value) {
|
|
796
|
+
return typeof value === "object" && value !== null;
|
|
797
|
+
}
|
|
798
|
+
var runpodJobResponseSchema = z3.object({
|
|
799
|
+
id: z3.string(),
|
|
800
|
+
status: z3.string().optional()
|
|
801
|
+
});
|
|
802
|
+
var runpodStatusResponseSchema = z3.object({
|
|
803
|
+
id: z3.string().optional(),
|
|
804
|
+
status: z3.string(),
|
|
805
|
+
output: z3.object({
|
|
806
|
+
text: z3.string().optional(),
|
|
807
|
+
result: z3.string().optional(),
|
|
808
|
+
// Some workers use 'result' instead of 'text'
|
|
809
|
+
segments: z3.array(
|
|
810
|
+
z3.object({
|
|
811
|
+
text: z3.string().optional(),
|
|
812
|
+
start: z3.number().optional(),
|
|
813
|
+
end: z3.number().optional()
|
|
814
|
+
})
|
|
815
|
+
).optional(),
|
|
816
|
+
language: z3.string().optional(),
|
|
817
|
+
duration: z3.number().optional(),
|
|
818
|
+
cost: z3.number().optional()
|
|
819
|
+
}).optional(),
|
|
820
|
+
error: z3.string().optional()
|
|
821
|
+
});
|
|
822
|
+
var RunpodTranscriptionModel = class {
|
|
823
|
+
constructor(modelId, config) {
|
|
824
|
+
this.modelId = modelId;
|
|
825
|
+
this.config = config;
|
|
826
|
+
this.specificationVersion = "v3";
|
|
827
|
+
}
|
|
828
|
+
get provider() {
|
|
829
|
+
return this.config.provider;
|
|
830
|
+
}
|
|
831
|
+
getRunpodRunUrl() {
|
|
832
|
+
const baseURL = withoutTrailingSlash2(this.config.baseURL) ?? this.config.baseURL;
|
|
833
|
+
if (baseURL.endsWith("/run") || baseURL.endsWith("/runsync")) {
|
|
834
|
+
return baseURL;
|
|
835
|
+
}
|
|
836
|
+
return `${baseURL}/run`;
|
|
837
|
+
}
|
|
838
|
+
async doGenerate(options) {
|
|
839
|
+
const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
|
|
840
|
+
const warnings = [];
|
|
841
|
+
const { audio, providerOptions, abortSignal, headers } = options;
|
|
842
|
+
const runpodOptions = this.extractRunpodOptions(providerOptions);
|
|
843
|
+
const input = {};
|
|
844
|
+
if (runpodOptions.audio && typeof runpodOptions.audio === "string") {
|
|
845
|
+
input.audio = runpodOptions.audio;
|
|
846
|
+
} else {
|
|
847
|
+
const base64Audio = this.convertAudioToBase64(audio);
|
|
848
|
+
input.audio_base64 = base64Audio;
|
|
849
|
+
}
|
|
850
|
+
if (runpodOptions.prompt || runpodOptions.initial_prompt) {
|
|
851
|
+
input.initial_prompt = runpodOptions.prompt ?? runpodOptions.initial_prompt;
|
|
852
|
+
}
|
|
853
|
+
if (runpodOptions.language) {
|
|
854
|
+
input.language = runpodOptions.language;
|
|
855
|
+
}
|
|
856
|
+
if (runpodOptions.word_timestamps !== void 0) {
|
|
857
|
+
input.word_timestamps = runpodOptions.word_timestamps;
|
|
858
|
+
}
|
|
859
|
+
if (runpodOptions.model) {
|
|
860
|
+
input.model = runpodOptions.model;
|
|
861
|
+
}
|
|
862
|
+
if (runpodOptions.transcription) {
|
|
863
|
+
input.transcription = runpodOptions.transcription;
|
|
864
|
+
}
|
|
865
|
+
if (runpodOptions.translate !== void 0) {
|
|
866
|
+
input.translate = runpodOptions.translate;
|
|
867
|
+
}
|
|
868
|
+
if (runpodOptions.enable_vad !== void 0) {
|
|
869
|
+
input.enable_vad = runpodOptions.enable_vad;
|
|
870
|
+
}
|
|
871
|
+
const requestBody = { input };
|
|
872
|
+
const url = this.getRunpodRunUrl();
|
|
873
|
+
const effectiveBaseURL = withoutTrailingSlash2(this.config.baseURL) ?? this.config.baseURL;
|
|
874
|
+
const { value: response, responseHeaders } = await postJsonToApi2({
|
|
875
|
+
url,
|
|
876
|
+
headers: {
|
|
877
|
+
...this.config.headers(),
|
|
878
|
+
...headers
|
|
879
|
+
},
|
|
880
|
+
body: requestBody,
|
|
881
|
+
failedResponseHandler: runpodTranscriptionFailedResponseHandler,
|
|
882
|
+
successfulResponseHandler: createJsonResponseHandler2(
|
|
883
|
+
runpodJobResponseSchema
|
|
884
|
+
),
|
|
885
|
+
abortSignal,
|
|
886
|
+
fetch: this.config.fetch
|
|
887
|
+
});
|
|
888
|
+
const typedResponse = response;
|
|
889
|
+
const jobId = typedResponse.id;
|
|
890
|
+
if (!jobId) {
|
|
891
|
+
throw new Error(
|
|
892
|
+
"Runpod transcription response did not include a job id."
|
|
893
|
+
);
|
|
894
|
+
}
|
|
895
|
+
const pollOptions = {
|
|
896
|
+
maxAttempts: runpodOptions.maxPollAttempts ?? 120,
|
|
897
|
+
pollIntervalMillis: runpodOptions.pollIntervalMillis ?? 2e3
|
|
898
|
+
};
|
|
899
|
+
const result = await this.pollForCompletion(
|
|
900
|
+
jobId,
|
|
901
|
+
abortSignal,
|
|
902
|
+
pollOptions,
|
|
903
|
+
effectiveBaseURL
|
|
904
|
+
);
|
|
905
|
+
const output = result.output;
|
|
906
|
+
const text = output?.text ?? output?.result ?? "";
|
|
907
|
+
const segments = this.parseSegments(output);
|
|
908
|
+
const language = output?.language;
|
|
909
|
+
const durationInSeconds = output?.duration;
|
|
910
|
+
const providerMetadata = {
|
|
911
|
+
runpod: {
|
|
912
|
+
jobId
|
|
913
|
+
}
|
|
914
|
+
};
|
|
915
|
+
return {
|
|
916
|
+
text,
|
|
917
|
+
segments,
|
|
918
|
+
language,
|
|
919
|
+
durationInSeconds,
|
|
920
|
+
warnings,
|
|
921
|
+
request: {
|
|
922
|
+
body: JSON.stringify(requestBody)
|
|
923
|
+
},
|
|
924
|
+
response: {
|
|
925
|
+
timestamp: currentDate,
|
|
926
|
+
modelId: this.modelId,
|
|
927
|
+
headers: responseHeaders,
|
|
928
|
+
body: JSON.stringify(result)
|
|
929
|
+
},
|
|
930
|
+
providerMetadata
|
|
931
|
+
};
|
|
932
|
+
}
|
|
933
|
+
convertAudioToBase64(audio) {
|
|
934
|
+
if (typeof audio === "string") {
|
|
935
|
+
return audio;
|
|
936
|
+
}
|
|
937
|
+
return this.uint8ArrayToBase64(audio);
|
|
938
|
+
}
|
|
939
|
+
uint8ArrayToBase64(data) {
|
|
940
|
+
if (typeof Buffer !== "undefined") {
|
|
941
|
+
return Buffer.from(data).toString("base64");
|
|
942
|
+
}
|
|
943
|
+
let binary = "";
|
|
944
|
+
for (let i = 0; i < data.length; i++) {
|
|
945
|
+
binary += String.fromCharCode(data[i]);
|
|
946
|
+
}
|
|
947
|
+
return btoa(binary);
|
|
948
|
+
}
|
|
949
|
+
extractRunpodOptions(providerOptions) {
|
|
950
|
+
if (!providerOptions) return {};
|
|
951
|
+
const runpod2 = providerOptions.runpod;
|
|
952
|
+
if (isRecord2(runpod2)) {
|
|
953
|
+
return runpod2;
|
|
954
|
+
}
|
|
955
|
+
return {};
|
|
956
|
+
}
|
|
957
|
+
async pollForCompletion(jobId, abortSignal, pollOptions, effectiveBaseURL) {
|
|
958
|
+
const maxAttempts = pollOptions?.maxAttempts ?? 120;
|
|
959
|
+
const pollInterval = pollOptions?.pollIntervalMillis ?? 2e3;
|
|
960
|
+
const baseURL = effectiveBaseURL ?? this.config.baseURL;
|
|
961
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
962
|
+
if (abortSignal?.aborted) {
|
|
963
|
+
throw new Error("Transcription was aborted");
|
|
964
|
+
}
|
|
965
|
+
const { value: statusResponse } = await getFromApi2({
|
|
966
|
+
url: `${baseURL}/status/${jobId}`,
|
|
967
|
+
headers: this.config.headers(),
|
|
968
|
+
successfulResponseHandler: createJsonResponseHandler2(
|
|
969
|
+
runpodStatusResponseSchema
|
|
970
|
+
),
|
|
971
|
+
failedResponseHandler: runpodTranscriptionFailedResponseHandler,
|
|
972
|
+
abortSignal,
|
|
973
|
+
fetch: this.config.fetch
|
|
974
|
+
});
|
|
975
|
+
const typedStatusResponse = statusResponse;
|
|
976
|
+
if (typedStatusResponse.status === "COMPLETED") {
|
|
977
|
+
return typedStatusResponse;
|
|
978
|
+
}
|
|
979
|
+
if (typedStatusResponse.status === "FAILED") {
|
|
980
|
+
throw new Error(
|
|
981
|
+
`Transcription failed: ${typedStatusResponse.error || "Unknown error"}`
|
|
982
|
+
);
|
|
983
|
+
}
|
|
984
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
985
|
+
}
|
|
986
|
+
throw new Error(
|
|
987
|
+
`Transcription timed out after ${maxAttempts} attempts (${maxAttempts * pollInterval / 1e3}s)`
|
|
988
|
+
);
|
|
989
|
+
}
|
|
990
|
+
parseSegments(output) {
|
|
991
|
+
if (!output?.segments || !Array.isArray(output.segments)) {
|
|
992
|
+
return [];
|
|
993
|
+
}
|
|
994
|
+
return output.segments.map((segment) => ({
|
|
995
|
+
text: segment.text ?? "",
|
|
996
|
+
startSecond: segment.start ?? 0,
|
|
997
|
+
endSecond: segment.end ?? 0
|
|
998
|
+
}));
|
|
999
|
+
}
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
// src/runpod-video-model.ts
|
|
1003
|
+
import {
|
|
1004
|
+
withoutTrailingSlash as withoutTrailingSlash3,
|
|
1005
|
+
createJsonResponseHandler as createJsonResponseHandler3,
|
|
1006
|
+
getFromApi as getFromApi3,
|
|
1007
|
+
postJsonToApi as postJsonToApi3
|
|
1008
|
+
} from "@ai-sdk/provider-utils";
|
|
1009
|
+
import { z as z4 } from "zod";
|
|
1010
|
+
function isRecord3(value) {
|
|
1011
|
+
return typeof value === "object" && value !== null;
|
|
1012
|
+
}
|
|
1013
|
+
var runpodVideoJobResponseSchema = z4.object({
|
|
1014
|
+
id: z4.string(),
|
|
1015
|
+
status: z4.string().optional()
|
|
1016
|
+
});
|
|
1017
|
+
var runpodVideoStatusSchema = z4.object({
|
|
1018
|
+
id: z4.string().optional(),
|
|
1019
|
+
status: z4.string(),
|
|
1020
|
+
output: z4.unknown().optional(),
|
|
1021
|
+
error: z4.string().optional()
|
|
1022
|
+
});
|
|
1023
|
+
var RunpodVideoModel = class {
|
|
1024
|
+
constructor(modelId, config) {
|
|
1025
|
+
this.modelId = modelId;
|
|
1026
|
+
this.config = config;
|
|
1027
|
+
this.specificationVersion = "v3";
|
|
1028
|
+
this.maxVideosPerCall = 1;
|
|
1029
|
+
}
|
|
1030
|
+
get provider() {
|
|
1031
|
+
return this.config.provider;
|
|
1032
|
+
}
|
|
1033
|
+
getRunpodRunUrl() {
|
|
1034
|
+
const baseURL = withoutTrailingSlash3(this.config.baseURL) ?? this.config.baseURL;
|
|
1035
|
+
if (baseURL.endsWith("/run") || baseURL.endsWith("/runsync")) {
|
|
1036
|
+
return baseURL;
|
|
1037
|
+
}
|
|
1038
|
+
return `${baseURL}/run`;
|
|
1039
|
+
}
|
|
1040
|
+
async doGenerate(options) {
|
|
1041
|
+
const currentDate = this.config._internal?.currentDate?.() ?? /* @__PURE__ */ new Date();
|
|
1042
|
+
const warnings = [];
|
|
1043
|
+
if (options.n > 1) {
|
|
1044
|
+
warnings.push({
|
|
1045
|
+
type: "unsupported",
|
|
1046
|
+
feature: "n > 1",
|
|
1047
|
+
details: "Runpod video models only support generating 1 video per call. Only 1 video will be generated."
|
|
1048
|
+
});
|
|
1049
|
+
}
|
|
1050
|
+
const { providerOptions, abortSignal, headers } = options;
|
|
1051
|
+
const runpodOptions = this.extractRunpodOptions(providerOptions);
|
|
1052
|
+
const input = this.buildInputPayload(options, runpodOptions);
|
|
1053
|
+
const requestBody = { input };
|
|
1054
|
+
const url = this.getRunpodRunUrl();
|
|
1055
|
+
const effectiveBaseURL = withoutTrailingSlash3(this.config.baseURL) ?? this.config.baseURL;
|
|
1056
|
+
const { value: response, responseHeaders } = await postJsonToApi3({
|
|
1057
|
+
url,
|
|
1058
|
+
headers: {
|
|
1059
|
+
...this.config.headers(),
|
|
1060
|
+
...headers
|
|
1061
|
+
},
|
|
1062
|
+
body: requestBody,
|
|
1063
|
+
failedResponseHandler: runpodVideoFailedResponseHandler,
|
|
1064
|
+
successfulResponseHandler: createJsonResponseHandler3(
|
|
1065
|
+
runpodVideoJobResponseSchema
|
|
1066
|
+
),
|
|
1067
|
+
abortSignal,
|
|
1068
|
+
fetch: this.config.fetch
|
|
1069
|
+
});
|
|
1070
|
+
const typedResponse = response;
|
|
1071
|
+
const jobId = typedResponse.id;
|
|
1072
|
+
if (!jobId) {
|
|
1073
|
+
throw new Error("Runpod video response did not include a job id.");
|
|
1074
|
+
}
|
|
1075
|
+
const pollOptions = {
|
|
1076
|
+
maxAttempts: runpodOptions.maxPollAttempts ?? 120,
|
|
1077
|
+
pollIntervalMillis: runpodOptions.pollIntervalMillis ?? 5e3
|
|
1078
|
+
};
|
|
1079
|
+
const result = await this.pollForCompletion(
|
|
1080
|
+
jobId,
|
|
1081
|
+
abortSignal,
|
|
1082
|
+
pollOptions,
|
|
1083
|
+
effectiveBaseURL
|
|
1084
|
+
);
|
|
1085
|
+
const videoUrl = this.extractVideoUrl(result.output);
|
|
1086
|
+
const providerMetadata = {
|
|
1087
|
+
runpod: {
|
|
1088
|
+
jobId
|
|
1089
|
+
}
|
|
1090
|
+
};
|
|
1091
|
+
return {
|
|
1092
|
+
videos: [{ type: "url", url: videoUrl, mediaType: "video/mp4" }],
|
|
1093
|
+
warnings,
|
|
1094
|
+
response: {
|
|
1095
|
+
timestamp: currentDate,
|
|
1096
|
+
modelId: this.modelId,
|
|
1097
|
+
headers: responseHeaders
|
|
1098
|
+
},
|
|
1099
|
+
providerMetadata
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
buildInputPayload(options, runpodOptions) {
|
|
1103
|
+
const apiOptions = Object.fromEntries(
|
|
1104
|
+
Object.entries(runpodOptions).filter(
|
|
1105
|
+
([key]) => key !== "maxPollAttempts" && key !== "pollIntervalMillis"
|
|
1106
|
+
)
|
|
1107
|
+
);
|
|
1108
|
+
const input = {
|
|
1109
|
+
...apiOptions
|
|
1110
|
+
};
|
|
1111
|
+
if (options.prompt) {
|
|
1112
|
+
input.prompt = options.prompt;
|
|
1113
|
+
}
|
|
1114
|
+
if (options.duration !== void 0) {
|
|
1115
|
+
input.duration = options.duration;
|
|
1116
|
+
}
|
|
1117
|
+
if (options.fps !== void 0) {
|
|
1118
|
+
input.fps = options.fps;
|
|
1119
|
+
}
|
|
1120
|
+
if (options.seed !== void 0) {
|
|
1121
|
+
input.seed = options.seed;
|
|
1122
|
+
}
|
|
1123
|
+
if (options.resolution) {
|
|
1124
|
+
input.size = options.resolution.replace("x", "*");
|
|
1125
|
+
} else if (options.aspectRatio) {
|
|
1126
|
+
input.aspect_ratio = options.aspectRatio;
|
|
1127
|
+
}
|
|
1128
|
+
if (options.image) {
|
|
1129
|
+
input.image = this.convertFileToRunpodFormat(options.image);
|
|
1130
|
+
}
|
|
1131
|
+
return input;
|
|
1132
|
+
}
|
|
1133
|
+
convertFileToRunpodFormat(file) {
|
|
1134
|
+
if (file.type === "url") {
|
|
1135
|
+
return file.url;
|
|
1136
|
+
}
|
|
1137
|
+
const mediaType = file.mediaType;
|
|
1138
|
+
const data = file.data;
|
|
1139
|
+
if (typeof data === "string") {
|
|
1140
|
+
return `data:${mediaType};base64,${data}`;
|
|
1141
|
+
}
|
|
1142
|
+
const base64 = this.uint8ArrayToBase64(data);
|
|
1143
|
+
return `data:${mediaType};base64,${base64}`;
|
|
1144
|
+
}
|
|
1145
|
+
uint8ArrayToBase64(data) {
|
|
1146
|
+
if (typeof Buffer !== "undefined") {
|
|
1147
|
+
return Buffer.from(data).toString("base64");
|
|
1148
|
+
}
|
|
1149
|
+
let binary = "";
|
|
1150
|
+
for (let i = 0; i < data.length; i++) {
|
|
1151
|
+
binary += String.fromCharCode(data[i]);
|
|
1152
|
+
}
|
|
1153
|
+
return btoa(binary);
|
|
1154
|
+
}
|
|
1155
|
+
extractRunpodOptions(providerOptions) {
|
|
1156
|
+
if (!providerOptions) return {};
|
|
1157
|
+
const runpod2 = providerOptions.runpod;
|
|
1158
|
+
if (isRecord3(runpod2)) {
|
|
1159
|
+
return runpod2;
|
|
1160
|
+
}
|
|
1161
|
+
return {};
|
|
1162
|
+
}
|
|
1163
|
+
extractVideoUrl(output) {
|
|
1164
|
+
if (isRecord3(output)) {
|
|
1165
|
+
if (typeof output.video_url === "string") return output.video_url;
|
|
1166
|
+
if (typeof output.result === "string") return output.result;
|
|
1167
|
+
if (typeof output.url === "string") return output.url;
|
|
1168
|
+
}
|
|
1169
|
+
if (typeof output === "string") {
|
|
1170
|
+
return output;
|
|
1171
|
+
}
|
|
1172
|
+
throw new Error(
|
|
1173
|
+
`Runpod video generation completed but no video URL was found in the output: ${JSON.stringify(output)}`
|
|
1174
|
+
);
|
|
1175
|
+
}
|
|
1176
|
+
async pollForCompletion(jobId, abortSignal, pollOptions, effectiveBaseURL) {
|
|
1177
|
+
const maxAttempts = pollOptions?.maxAttempts ?? 120;
|
|
1178
|
+
const pollInterval = pollOptions?.pollIntervalMillis ?? 5e3;
|
|
1179
|
+
const baseURL = effectiveBaseURL ?? this.config.baseURL;
|
|
1180
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
1181
|
+
if (abortSignal?.aborted) {
|
|
1182
|
+
throw new Error("Video generation was aborted");
|
|
1183
|
+
}
|
|
1184
|
+
const { value: statusResponse } = await getFromApi3({
|
|
1185
|
+
url: `${baseURL}/status/${jobId}`,
|
|
1186
|
+
headers: this.config.headers(),
|
|
1187
|
+
successfulResponseHandler: createJsonResponseHandler3(
|
|
1188
|
+
runpodVideoStatusSchema
|
|
1189
|
+
),
|
|
1190
|
+
failedResponseHandler: runpodVideoFailedResponseHandler,
|
|
1191
|
+
abortSignal,
|
|
1192
|
+
fetch: this.config.fetch
|
|
1193
|
+
});
|
|
1194
|
+
const typedStatusResponse = statusResponse;
|
|
1195
|
+
if (typedStatusResponse.status === "COMPLETED") {
|
|
1196
|
+
return typedStatusResponse;
|
|
1197
|
+
}
|
|
1198
|
+
if (typedStatusResponse.status === "FAILED") {
|
|
1199
|
+
throw new Error(
|
|
1200
|
+
`Video generation failed: ${typedStatusResponse.error || "Unknown error"}`
|
|
1201
|
+
);
|
|
1202
|
+
}
|
|
1203
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
1204
|
+
}
|
|
1205
|
+
throw new Error(
|
|
1206
|
+
`Video generation timed out after ${maxAttempts} attempts (${maxAttempts * pollInterval / 1e3}s)`
|
|
1207
|
+
);
|
|
1208
|
+
}
|
|
1209
|
+
};
|
|
1210
|
+
|
|
631
1211
|
// src/runpod-provider.ts
|
|
632
1212
|
var MODEL_ID_TO_ENDPOINT_URL = {
|
|
633
1213
|
"qwen/qwen3-32b-awq": "https://api.runpod.ai/v2/qwen3-32b-awq/openai/v1",
|
|
@@ -651,6 +1231,10 @@ var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
|
|
|
651
1231
|
"black-forest-labs/flux-1-dev": "https://api.runpod.ai/v2/black-forest-labs-flux-1-dev",
|
|
652
1232
|
// Alibaba Wan 2.6 (t2i)
|
|
653
1233
|
"alibaba/wan-2.6": "https://api.runpod.ai/v2/wan-2-6-t2i",
|
|
1234
|
+
// Tongyi Z-Image Turbo (t2i)
|
|
1235
|
+
"tongyi-mai/z-image-turbo": "https://api.runpod.ai/v2/z-image-turbo",
|
|
1236
|
+
"z-image-turbo": "https://api.runpod.ai/v2/z-image-turbo",
|
|
1237
|
+
// alias, not advertised
|
|
654
1238
|
// Nano Banana (edit only)
|
|
655
1239
|
"google/nano-banana-edit": "https://api.runpod.ai/v2/nano-banana-edit",
|
|
656
1240
|
"nano-banana-edit": "https://api.runpod.ai/v2/nano-banana-edit",
|
|
@@ -664,6 +1248,26 @@ var IMAGE_MODEL_ID_TO_ENDPOINT_URL = {
|
|
|
664
1248
|
var SPEECH_MODEL_ID_TO_ENDPOINT_URL = {
|
|
665
1249
|
"resembleai/chatterbox-turbo": "https://api.runpod.ai/v2/chatterbox-turbo/"
|
|
666
1250
|
};
|
|
1251
|
+
var TRANSCRIPTION_MODEL_ID_TO_ENDPOINT_URL = {
|
|
1252
|
+
"pruna/whisper-v3-large": "https://api.runpod.ai/v2/whisper-v3-large"
|
|
1253
|
+
};
|
|
1254
|
+
var VIDEO_MODEL_ID_TO_ENDPOINT_URL = {
|
|
1255
|
+
"pruna/p-video": "https://api.runpod.ai/v2/p-video",
|
|
1256
|
+
"vidu/q3-t2v": "https://api.runpod.ai/v2/vidu-q3-t2v",
|
|
1257
|
+
"vidu/q3-i2v": "https://api.runpod.ai/v2/vidu-q3-i2v",
|
|
1258
|
+
"kwaivgi/kling-v2.6-std-motion-control": "https://api.runpod.ai/v2/kling-v2-6-std-motion-control",
|
|
1259
|
+
"kwaivgi/kling-video-o1-r2v": "https://api.runpod.ai/v2/kling-video-o1-r2v",
|
|
1260
|
+
"kwaivgi/kling-v2.1-i2v-pro": "https://api.runpod.ai/v2/kling-v2-1-i2v-pro",
|
|
1261
|
+
"alibaba/wan-2.6-t2v": "https://api.runpod.ai/v2/wan-2-6-t2v",
|
|
1262
|
+
"alibaba/wan-2.6-i2v": "https://api.runpod.ai/v2/wan-2-6-i2v",
|
|
1263
|
+
"alibaba/wan-2.5": "https://api.runpod.ai/v2/wan-2-5",
|
|
1264
|
+
"alibaba/wan-2.2-t2v-720-lora": "https://api.runpod.ai/v2/wan-2-2-t2v-720-lora",
|
|
1265
|
+
"alibaba/wan-2.2-i2v-720": "https://api.runpod.ai/v2/wan-2-2-i2v-720",
|
|
1266
|
+
"alibaba/wan-2.1-i2v-720": "https://api.runpod.ai/v2/wan-2-1-i2v-720",
|
|
1267
|
+
"bytedance/seedance-v1.5-pro-i2v": "https://api.runpod.ai/v2/seedance-v1-5-pro-i2v",
|
|
1268
|
+
"openai/sora-2-pro-i2v": "https://api.runpod.ai/v2/sora-2-pro-i2v",
|
|
1269
|
+
"openai/sora-2-i2v": "https://api.runpod.ai/v2/sora-2-i2v"
|
|
1270
|
+
};
|
|
667
1271
|
var MODEL_ID_TO_OPENAI_NAME = {
|
|
668
1272
|
"qwen/qwen3-32b-awq": "Qwen/Qwen3-32B-AWQ",
|
|
669
1273
|
"deepcogito/cogito-671b-v2.1-fp8": "deepcogito/cogito-671b-v2.1-FP8",
|
|
@@ -722,7 +1326,7 @@ function createRunpod(options = {}) {
|
|
|
722
1326
|
}
|
|
723
1327
|
return {
|
|
724
1328
|
provider: `runpod.${modelType}`,
|
|
725
|
-
url: ({ path }) => `${
|
|
1329
|
+
url: ({ path }) => `${withoutTrailingSlash4(baseURL)}${path}`,
|
|
726
1330
|
headers: getHeaders,
|
|
727
1331
|
fetch: runpodFetch
|
|
728
1332
|
};
|
|
@@ -767,6 +1371,30 @@ function createRunpod(options = {}) {
|
|
|
767
1371
|
fetch: runpodFetch
|
|
768
1372
|
});
|
|
769
1373
|
};
|
|
1374
|
+
const createTranscriptionModel = (modelId) => {
|
|
1375
|
+
const endpointIdFromConsole = parseRunpodConsoleEndpointId(modelId);
|
|
1376
|
+
const normalizedModelId = endpointIdFromConsole ?? modelId;
|
|
1377
|
+
const mappedBaseURL = TRANSCRIPTION_MODEL_ID_TO_ENDPOINT_URL[normalizedModelId];
|
|
1378
|
+
const baseURL = mappedBaseURL ?? (normalizedModelId.startsWith("http") ? normalizedModelId : `https://api.runpod.ai/v2/${normalizedModelId}`);
|
|
1379
|
+
return new RunpodTranscriptionModel(normalizedModelId, {
|
|
1380
|
+
provider: "runpod.transcription",
|
|
1381
|
+
baseURL,
|
|
1382
|
+
headers: getHeaders,
|
|
1383
|
+
fetch: runpodFetch
|
|
1384
|
+
});
|
|
1385
|
+
};
|
|
1386
|
+
const createVideoModel = (modelId) => {
|
|
1387
|
+
const endpointIdFromConsole = parseRunpodConsoleEndpointId(modelId);
|
|
1388
|
+
const normalizedModelId = endpointIdFromConsole ?? modelId;
|
|
1389
|
+
const mappedBaseURL = VIDEO_MODEL_ID_TO_ENDPOINT_URL[normalizedModelId];
|
|
1390
|
+
const baseURL = mappedBaseURL ?? (normalizedModelId.startsWith("http") ? normalizedModelId : `https://api.runpod.ai/v2/${normalizedModelId}`);
|
|
1391
|
+
return new RunpodVideoModel(normalizedModelId, {
|
|
1392
|
+
provider: "runpod.video",
|
|
1393
|
+
baseURL,
|
|
1394
|
+
headers: getHeaders,
|
|
1395
|
+
fetch: options.fetch
|
|
1396
|
+
});
|
|
1397
|
+
};
|
|
770
1398
|
const provider = (modelId) => createChatModel(modelId);
|
|
771
1399
|
provider.completionModel = createCompletionModel;
|
|
772
1400
|
provider.languageModel = createChatModel;
|
|
@@ -775,6 +1403,10 @@ function createRunpod(options = {}) {
|
|
|
775
1403
|
provider.image = createImageModel;
|
|
776
1404
|
provider.speechModel = createSpeechModel;
|
|
777
1405
|
provider.speech = createSpeechModel;
|
|
1406
|
+
provider.transcriptionModel = createTranscriptionModel;
|
|
1407
|
+
provider.transcription = createTranscriptionModel;
|
|
1408
|
+
provider.videoModel = createVideoModel;
|
|
1409
|
+
provider.video = createVideoModel;
|
|
778
1410
|
return provider;
|
|
779
1411
|
}
|
|
780
1412
|
var runpod = createRunpod();
|