@ai-sdk/fal 3.0.0-beta.3 → 3.0.0-beta.30

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/dist/index.mjs DELETED
@@ -1,1126 +0,0 @@
1
- // src/fal-provider.ts
2
- import {
3
- NoSuchModelError
4
- } from "@ai-sdk/provider";
5
- import {
6
- withoutTrailingSlash,
7
- withUserAgentSuffix
8
- } from "@ai-sdk/provider-utils";
9
-
10
- // src/fal-image-model.ts
11
- import {
12
- combineHeaders,
13
- convertImageModelFileToDataUri,
14
- createBinaryResponseHandler,
15
- createJsonErrorResponseHandler,
16
- createJsonResponseHandler,
17
- createStatusCodeErrorResponseHandler,
18
- getFromApi,
19
- parseProviderOptions,
20
- postJsonToApi,
21
- resolve
22
- } from "@ai-sdk/provider-utils";
23
- import { z as z2 } from "zod/v4";
24
-
25
- // src/fal-image-options.ts
26
- import { lazySchema, zodSchema } from "@ai-sdk/provider-utils";
27
- import { z } from "zod/v4";
28
- var falImageModelOptionsSchema = lazySchema(
29
- () => zodSchema(
30
- z.object({
31
- /** @deprecated use prompt.images instead */
32
- imageUrl: z.string().nullish().meta({
33
- deprecated: true,
34
- description: "Use `prompt.images` instead"
35
- }),
36
- maskUrl: z.string().nullish().meta({ deprecated: true, description: "Use `prompt.mask` instead" }),
37
- guidanceScale: z.number().min(1).max(20).nullish(),
38
- numInferenceSteps: z.number().min(1).max(50).nullish(),
39
- enableSafetyChecker: z.boolean().nullish(),
40
- outputFormat: z.enum(["jpeg", "png"]).nullish(),
41
- syncMode: z.boolean().nullish(),
42
- strength: z.number().nullish(),
43
- acceleration: z.enum(["none", "regular", "high"]).nullish(),
44
- safetyTolerance: z.enum(["1", "2", "3", "4", "5", "6"]).or(z.number().min(1).max(6)).nullish(),
45
- /**
46
- * When true, converts multiple input images to `image_urls` array instead of `image_url` string.
47
- */
48
- useMultipleImages: z.boolean().nullish(),
49
- // Deprecated snake_case versions
50
- image_url: z.string().nullish(),
51
- mask_url: z.string().nullish(),
52
- guidance_scale: z.number().min(1).max(20).nullish(),
53
- num_inference_steps: z.number().min(1).max(50).nullish(),
54
- enable_safety_checker: z.boolean().nullish(),
55
- output_format: z.enum(["jpeg", "png"]).nullish(),
56
- sync_mode: z.boolean().nullish(),
57
- safety_tolerance: z.enum(["1", "2", "3", "4", "5", "6"]).or(z.number().min(1).max(6)).nullish()
58
- }).passthrough().transform((data) => {
59
- const result = {};
60
- const deprecatedKeys = [];
61
- const mapKey = (snakeKey, camelKey) => {
62
- const snakeValue = data[snakeKey];
63
- const camelValue = data[camelKey];
64
- if (snakeValue !== void 0 && snakeValue !== null) {
65
- deprecatedKeys.push(snakeKey);
66
- result[camelKey] = snakeValue;
67
- } else if (camelValue !== void 0 && camelValue !== null) {
68
- result[camelKey] = camelValue;
69
- }
70
- };
71
- mapKey("image_url", "imageUrl");
72
- mapKey("mask_url", "maskUrl");
73
- mapKey("guidance_scale", "guidanceScale");
74
- mapKey("num_inference_steps", "numInferenceSteps");
75
- mapKey("enable_safety_checker", "enableSafetyChecker");
76
- mapKey("output_format", "outputFormat");
77
- mapKey("sync_mode", "syncMode");
78
- mapKey("safety_tolerance", "safetyTolerance");
79
- if (data.strength !== void 0 && data.strength !== null) {
80
- result.strength = data.strength;
81
- }
82
- if (data.acceleration !== void 0 && data.acceleration !== null) {
83
- result.acceleration = data.acceleration;
84
- }
85
- if (data.useMultipleImages !== void 0 && data.useMultipleImages !== null) {
86
- result.useMultipleImages = data.useMultipleImages;
87
- }
88
- for (const [key, value] of Object.entries(data)) {
89
- if (![
90
- // camelCase known keys
91
- "imageUrl",
92
- "maskUrl",
93
- "guidanceScale",
94
- "numInferenceSteps",
95
- "enableSafetyChecker",
96
- "outputFormat",
97
- "syncMode",
98
- "strength",
99
- "acceleration",
100
- "safetyTolerance",
101
- "useMultipleImages",
102
- // snake_case known keys
103
- "image_url",
104
- "mask_url",
105
- "guidance_scale",
106
- "num_inference_steps",
107
- "enable_safety_checker",
108
- "output_format",
109
- "sync_mode",
110
- "safety_tolerance"
111
- ].includes(key)) {
112
- result[key] = value;
113
- }
114
- }
115
- if (deprecatedKeys.length > 0) {
116
- result.__deprecatedKeys = deprecatedKeys;
117
- }
118
- return result;
119
- })
120
- )
121
- );
122
-
123
- // src/fal-image-model.ts
124
- var FalImageModel = class {
125
- constructor(modelId, config) {
126
- this.modelId = modelId;
127
- this.config = config;
128
- this.specificationVersion = "v3";
129
- this.maxImagesPerCall = 1;
130
- }
131
- get provider() {
132
- return this.config.provider;
133
- }
134
- async getArgs({
135
- prompt,
136
- n,
137
- size,
138
- aspectRatio,
139
- seed,
140
- providerOptions,
141
- files,
142
- mask
143
- }) {
144
- var _a;
145
- const warnings = [];
146
- let imageSize;
147
- if (size) {
148
- const [width, height] = size.split("x").map(Number);
149
- imageSize = { width, height };
150
- } else if (aspectRatio) {
151
- imageSize = convertAspectRatioToSize(aspectRatio);
152
- }
153
- const falOptions = await parseProviderOptions({
154
- provider: "fal",
155
- providerOptions,
156
- schema: falImageModelOptionsSchema
157
- });
158
- const requestBody = {
159
- prompt,
160
- seed,
161
- image_size: imageSize,
162
- num_images: n
163
- };
164
- if (files != null && files.length > 0) {
165
- const useMultipleImages = (falOptions == null ? void 0 : falOptions.useMultipleImages) === true;
166
- if (useMultipleImages) {
167
- requestBody.image_urls = files.map(
168
- (file) => convertImageModelFileToDataUri(file)
169
- );
170
- } else {
171
- requestBody.image_url = convertImageModelFileToDataUri(files[0]);
172
- if (files.length > 1) {
173
- warnings.push({
174
- type: "other",
175
- message: "Multiple input images provided but useMultipleImages is not enabled. Only the first image will be used. Set providerOptions.fal.useMultipleImages to true for models that support multiple images (e.g., fal-ai/flux-2/edit)."
176
- });
177
- }
178
- }
179
- }
180
- if (mask != null) {
181
- requestBody.mask_url = convertImageModelFileToDataUri(mask);
182
- }
183
- if (falOptions) {
184
- const deprecatedKeys = "__deprecatedKeys" in falOptions ? falOptions.__deprecatedKeys : void 0;
185
- if (deprecatedKeys && deprecatedKeys.length > 0) {
186
- warnings.push({
187
- type: "other",
188
- message: `The following provider options use deprecated snake_case and will be removed in @ai-sdk/fal v2.0. Please use camelCase instead: ${deprecatedKeys.map((key) => {
189
- const camelCase = key.replace(
190
- /_([a-z])/g,
191
- (_, letter) => letter.toUpperCase()
192
- );
193
- return `'${key}' (use '${camelCase}')`;
194
- }).join(", ")}`
195
- });
196
- }
197
- const fieldMapping = {
198
- imageUrl: "image_url",
199
- maskUrl: "mask_url",
200
- guidanceScale: "guidance_scale",
201
- numInferenceSteps: "num_inference_steps",
202
- enableSafetyChecker: "enable_safety_checker",
203
- outputFormat: "output_format",
204
- syncMode: "sync_mode",
205
- safetyTolerance: "safety_tolerance"
206
- };
207
- for (const [key, value] of Object.entries(falOptions)) {
208
- if (key === "__deprecatedKeys") continue;
209
- if (key === "useMultipleImages") continue;
210
- const apiKey = (_a = fieldMapping[key]) != null ? _a : key;
211
- if (value !== void 0) {
212
- requestBody[apiKey] = value;
213
- }
214
- }
215
- }
216
- return { requestBody, warnings };
217
- }
218
- async doGenerate(options) {
219
- var _a, _b, _c;
220
- const { requestBody, warnings } = await this.getArgs(options);
221
- const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
222
- const { value, responseHeaders } = await postJsonToApi({
223
- url: `${this.config.baseURL}/${this.modelId}`,
224
- headers: combineHeaders(
225
- await resolve(this.config.headers),
226
- options.headers
227
- ),
228
- body: requestBody,
229
- failedResponseHandler: falFailedResponseHandler,
230
- successfulResponseHandler: createJsonResponseHandler(
231
- falImageResponseSchema
232
- ),
233
- abortSignal: options.abortSignal,
234
- fetch: this.config.fetch
235
- });
236
- const {
237
- images: targetImages,
238
- // prompt is just passed through and not a revised prompt per image
239
- prompt: _prompt,
240
- // NSFW information is normalized merged into `providerMetadata.fal.images`
241
- has_nsfw_concepts,
242
- nsfw_content_detected,
243
- // pass through other properties to providerMetadata
244
- ...responseMetaData
245
- } = value;
246
- const downloadedImages = await Promise.all(
247
- targetImages.map(
248
- (image) => this.downloadImage(image.url, options.abortSignal)
249
- )
250
- );
251
- return {
252
- images: downloadedImages,
253
- warnings,
254
- response: {
255
- modelId: this.modelId,
256
- timestamp: currentDate,
257
- headers: responseHeaders
258
- },
259
- providerMetadata: {
260
- fal: {
261
- images: targetImages.map((image, index) => {
262
- var _a2;
263
- const {
264
- url,
265
- content_type: contentType,
266
- file_name: fileName,
267
- file_data: fileData,
268
- file_size: fileSize,
269
- ...imageMetaData
270
- } = image;
271
- const nsfw = (_a2 = has_nsfw_concepts == null ? void 0 : has_nsfw_concepts[index]) != null ? _a2 : nsfw_content_detected == null ? void 0 : nsfw_content_detected[index];
272
- return {
273
- ...imageMetaData,
274
- ...removeOnlyUndefined({
275
- contentType,
276
- fileName,
277
- fileData,
278
- fileSize,
279
- nsfw
280
- })
281
- };
282
- }),
283
- ...responseMetaData
284
- }
285
- }
286
- };
287
- }
288
- async downloadImage(url, abortSignal) {
289
- const { value: response } = await getFromApi({
290
- url,
291
- // No specific headers should be needed for this request as it's a
292
- // generated image provided by fal.ai.
293
- abortSignal,
294
- failedResponseHandler: createStatusCodeErrorResponseHandler(),
295
- successfulResponseHandler: createBinaryResponseHandler(),
296
- fetch: this.config.fetch
297
- });
298
- return response;
299
- }
300
- };
301
- function removeOnlyUndefined(obj) {
302
- return Object.fromEntries(
303
- Object.entries(obj).filter(([, v]) => v !== void 0)
304
- );
305
- }
306
- function convertAspectRatioToSize(aspectRatio) {
307
- switch (aspectRatio) {
308
- case "1:1":
309
- return "square_hd";
310
- case "16:9":
311
- return "landscape_16_9";
312
- case "9:16":
313
- return "portrait_16_9";
314
- case "4:3":
315
- return "landscape_4_3";
316
- case "3:4":
317
- return "portrait_4_3";
318
- case "16:10":
319
- return { width: 1280, height: 800 };
320
- case "10:16":
321
- return { width: 800, height: 1280 };
322
- case "21:9":
323
- return { width: 2560, height: 1080 };
324
- case "9:21":
325
- return { width: 1080, height: 2560 };
326
- }
327
- return void 0;
328
- }
329
- var falValidationErrorSchema = z2.object({
330
- detail: z2.array(
331
- z2.object({
332
- loc: z2.array(z2.string()),
333
- msg: z2.string(),
334
- type: z2.string()
335
- })
336
- )
337
- });
338
- var falHttpErrorSchema = z2.object({
339
- message: z2.string()
340
- });
341
- var falErrorSchema = z2.union([falValidationErrorSchema, falHttpErrorSchema]);
342
- var falImageSchema = z2.object({
343
- url: z2.string(),
344
- width: z2.number().nullish(),
345
- height: z2.number().nullish(),
346
- // e.g. https://fal.ai/models/fal-ai/fashn/tryon/v1.6/api#schema-output
347
- content_type: z2.string().nullish(),
348
- // e.g. https://fal.ai/models/fal-ai/flowedit/api#schema-output
349
- file_name: z2.string().nullish(),
350
- file_data: z2.string().optional(),
351
- file_size: z2.number().nullish()
352
- });
353
- var loraFileSchema = z2.object({
354
- url: z2.string(),
355
- content_type: z2.string().optional(),
356
- file_name: z2.string().nullable().optional(),
357
- file_data: z2.string().optional(),
358
- file_size: z2.number().nullable().optional()
359
- });
360
- var commonResponseSchema = z2.object({
361
- timings: z2.object({
362
- inference: z2.number().optional()
363
- }).optional(),
364
- seed: z2.number().optional(),
365
- has_nsfw_concepts: z2.array(z2.boolean()).optional(),
366
- prompt: z2.string().optional(),
367
- // https://fal.ai/models/fal-ai/lcm/api#schema-output
368
- nsfw_content_detected: z2.array(z2.boolean()).optional(),
369
- num_inference_steps: z2.number().optional(),
370
- // https://fal.ai/models/fal-ai/lora/api#schema-output
371
- debug_latents: loraFileSchema.optional(),
372
- debug_per_pass_latents: loraFileSchema.optional()
373
- });
374
- var base = z2.looseObject(commonResponseSchema.shape);
375
- var falImageResponseSchema = z2.union([
376
- base.extend({ images: z2.array(falImageSchema) }),
377
- base.extend({ image: falImageSchema })
378
- ]).transform((v) => "images" in v ? v : { ...v, images: [v.image] }).pipe(base.extend({ images: z2.array(falImageSchema) }));
379
- function isValidationError(error) {
380
- return falValidationErrorSchema.safeParse(error).success;
381
- }
382
- var falFailedResponseHandler = createJsonErrorResponseHandler({
383
- errorSchema: falErrorSchema,
384
- errorToMessage: (error) => {
385
- var _a;
386
- if (isValidationError(error)) {
387
- return error.detail.map((detail) => `${detail.loc.join(".")}: ${detail.msg}`).join("\n");
388
- }
389
- return (_a = error.message) != null ? _a : "Unknown fal error";
390
- }
391
- });
392
-
393
- // src/fal-transcription-model.ts
394
- import {
395
- AISDKError
396
- } from "@ai-sdk/provider";
397
- import {
398
- combineHeaders as combineHeaders2,
399
- convertUint8ArrayToBase64,
400
- createJsonErrorResponseHandler as createJsonErrorResponseHandler3,
401
- createJsonResponseHandler as createJsonResponseHandler2,
402
- delay,
403
- getFromApi as getFromApi2,
404
- parseProviderOptions as parseProviderOptions2,
405
- postJsonToApi as postJsonToApi2
406
- } from "@ai-sdk/provider-utils";
407
- import { z as z4 } from "zod/v4";
408
-
409
- // src/fal-error.ts
410
- import { z as z3 } from "zod/v4";
411
- import { createJsonErrorResponseHandler as createJsonErrorResponseHandler2 } from "@ai-sdk/provider-utils";
412
- var falErrorDataSchema = z3.object({
413
- error: z3.object({
414
- message: z3.string(),
415
- code: z3.number()
416
- })
417
- });
418
- var falFailedResponseHandler2 = createJsonErrorResponseHandler2({
419
- errorSchema: falErrorDataSchema,
420
- errorToMessage: (data) => data.error.message
421
- });
422
-
423
- // src/fal-transcription-model.ts
424
- var falTranscriptionModelOptionsSchema = z4.object({
425
- /**
426
- * Language of the audio file. If set to null, the language will be automatically detected. Defaults to null.
427
- *
428
- * If translate is selected as the task, the audio will be translated to English, regardless of the language selected.
429
- */
430
- language: z4.union([z4.enum(["en"]), z4.string()]).nullish().default("en"),
431
- /**
432
- * Whether to diarize the audio file. Defaults to true.
433
- */
434
- diarize: z4.boolean().nullish().default(true),
435
- /**
436
- * Level of the chunks to return. Either segment or word. Default value: "segment"
437
- */
438
- chunkLevel: z4.enum(["segment", "word"]).nullish().default("segment"),
439
- /**
440
- * Version of the model to use. All of the models are the Whisper large variant. Default value: "3"
441
- */
442
- version: z4.enum(["3"]).nullish().default("3"),
443
- /**
444
- * Default value: 64
445
- */
446
- batchSize: z4.number().nullish().default(64),
447
- /**
448
- * Number of speakers in the audio file. Defaults to null. If not provided, the number of speakers will be automatically detected.
449
- */
450
- numSpeakers: z4.number().nullable().nullish()
451
- });
452
- var FalTranscriptionModel = class {
453
- constructor(modelId, config) {
454
- this.modelId = modelId;
455
- this.config = config;
456
- this.specificationVersion = "v3";
457
- }
458
- get provider() {
459
- return this.config.provider;
460
- }
461
- async getArgs({
462
- providerOptions
463
- }) {
464
- var _a, _b, _c;
465
- const warnings = [];
466
- const falOptions = await parseProviderOptions2({
467
- provider: "fal",
468
- providerOptions,
469
- schema: falTranscriptionModelOptionsSchema
470
- });
471
- const body = {
472
- task: "transcribe",
473
- diarize: true,
474
- chunk_level: "word"
475
- };
476
- if (falOptions) {
477
- body.language = falOptions.language;
478
- body.version = (_a = falOptions.version) != null ? _a : void 0;
479
- body.batch_size = (_b = falOptions.batchSize) != null ? _b : void 0;
480
- body.num_speakers = (_c = falOptions.numSpeakers) != null ? _c : void 0;
481
- if (typeof falOptions.diarize === "boolean") {
482
- body.diarize = falOptions.diarize;
483
- }
484
- if (falOptions.chunkLevel) {
485
- body.chunk_level = falOptions.chunkLevel;
486
- }
487
- }
488
- return {
489
- body,
490
- warnings
491
- };
492
- }
493
- async doGenerate(options) {
494
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
495
- const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
496
- const { body, warnings } = await this.getArgs(options);
497
- const base64Audio = typeof options.audio === "string" ? options.audio : convertUint8ArrayToBase64(options.audio);
498
- const audioUrl = `data:${options.mediaType};base64,${base64Audio}`;
499
- const { value: queueResponse } = await postJsonToApi2({
500
- url: this.config.url({
501
- path: `https://queue.fal.run/fal-ai/${this.modelId}`,
502
- modelId: this.modelId
503
- }),
504
- headers: combineHeaders2(this.config.headers(), options.headers),
505
- body: {
506
- ...body,
507
- audio_url: audioUrl
508
- },
509
- failedResponseHandler: falFailedResponseHandler2,
510
- successfulResponseHandler: createJsonResponseHandler2(falJobResponseSchema),
511
- abortSignal: options.abortSignal,
512
- fetch: this.config.fetch
513
- });
514
- const startTime = Date.now();
515
- const timeoutMs = 6e4;
516
- const pollIntervalMs = 1e3;
517
- let response;
518
- let responseHeaders;
519
- let rawResponse;
520
- while (true) {
521
- try {
522
- const {
523
- value: statusResponse,
524
- responseHeaders: statusHeaders,
525
- rawValue: statusRawResponse
526
- } = await getFromApi2({
527
- url: this.config.url({
528
- path: `https://queue.fal.run/fal-ai/${this.modelId}/requests/${queueResponse.request_id}`,
529
- modelId: this.modelId
530
- }),
531
- headers: combineHeaders2(this.config.headers(), options.headers),
532
- failedResponseHandler: async ({
533
- requestBodyValues,
534
- response: response2,
535
- url
536
- }) => {
537
- const clone = response2.clone();
538
- const body2 = await clone.json();
539
- if (body2.detail === "Request is still in progress") {
540
- return {
541
- value: new Error("Request is still in progress"),
542
- rawValue: body2,
543
- responseHeaders: {}
544
- };
545
- }
546
- return createJsonErrorResponseHandler3({
547
- errorSchema: falErrorDataSchema,
548
- errorToMessage: (data) => data.error.message
549
- })({ requestBodyValues, response: response2, url });
550
- },
551
- successfulResponseHandler: createJsonResponseHandler2(
552
- falTranscriptionResponseSchema
553
- ),
554
- abortSignal: options.abortSignal,
555
- fetch: this.config.fetch
556
- });
557
- response = statusResponse;
558
- responseHeaders = statusHeaders;
559
- rawResponse = statusRawResponse;
560
- break;
561
- } catch (error) {
562
- if (error instanceof Error && error.message === "Request is still in progress") {
563
- } else {
564
- throw error;
565
- }
566
- }
567
- if (Date.now() - startTime > timeoutMs) {
568
- throw new AISDKError({
569
- message: "Transcription request timed out after 60 seconds",
570
- name: "TranscriptionRequestTimedOut",
571
- cause: response
572
- });
573
- }
574
- await delay(pollIntervalMs);
575
- }
576
- return {
577
- text: response.text,
578
- segments: (_e = (_d = response.chunks) == null ? void 0 : _d.map((chunk) => {
579
- var _a2, _b2, _c2, _d2;
580
- return {
581
- text: chunk.text,
582
- startSecond: (_b2 = (_a2 = chunk.timestamp) == null ? void 0 : _a2.at(0)) != null ? _b2 : 0,
583
- endSecond: (_d2 = (_c2 = chunk.timestamp) == null ? void 0 : _c2.at(1)) != null ? _d2 : 0
584
- };
585
- })) != null ? _e : [],
586
- language: (_g = (_f = response.inferred_languages) == null ? void 0 : _f.at(0)) != null ? _g : void 0,
587
- durationInSeconds: (_k = (_j = (_i = (_h = response.chunks) == null ? void 0 : _h.at(-1)) == null ? void 0 : _i.timestamp) == null ? void 0 : _j.at(1)) != null ? _k : void 0,
588
- warnings,
589
- response: {
590
- timestamp: currentDate,
591
- modelId: this.modelId,
592
- headers: responseHeaders,
593
- body: rawResponse
594
- }
595
- };
596
- }
597
- };
598
- var falJobResponseSchema = z4.object({
599
- request_id: z4.string().nullish()
600
- });
601
- var falTranscriptionResponseSchema = z4.object({
602
- text: z4.string(),
603
- chunks: z4.array(
604
- z4.object({
605
- text: z4.string(),
606
- timestamp: z4.array(z4.number()).nullish()
607
- })
608
- ).nullish(),
609
- inferred_languages: z4.array(z4.string()).nullish()
610
- });
611
-
612
- // src/fal-speech-model.ts
613
- import {
614
- combineHeaders as combineHeaders3,
615
- createBinaryResponseHandler as createBinaryResponseHandler2,
616
- createJsonResponseHandler as createJsonResponseHandler3,
617
- createStatusCodeErrorResponseHandler as createStatusCodeErrorResponseHandler2,
618
- getFromApi as getFromApi3,
619
- parseProviderOptions as parseProviderOptions3,
620
- postJsonToApi as postJsonToApi3
621
- } from "@ai-sdk/provider-utils";
622
- import { z as z5 } from "zod/v4";
623
-
624
- // src/fal-api-types.ts
625
- var FAL_LANGUAGE_BOOSTS = [
626
- "Chinese",
627
- "Chinese,Yue",
628
- "English",
629
- "Arabic",
630
- "Russian",
631
- "Spanish",
632
- "French",
633
- "Portuguese",
634
- "German",
635
- "Turkish",
636
- "Dutch",
637
- "Ukrainian",
638
- "Vietnamese",
639
- "Indonesian",
640
- "Japanese",
641
- "Italian",
642
- "Korean",
643
- "Thai",
644
- "Polish",
645
- "Romanian",
646
- "Greek",
647
- "Czech",
648
- "Finnish",
649
- "Hindi",
650
- "auto"
651
- ];
652
- var FAL_EMOTIONS = [
653
- "happy",
654
- "sad",
655
- "angry",
656
- "fearful",
657
- "disgusted",
658
- "surprised",
659
- "neutral"
660
- ];
661
-
662
- // src/fal-speech-model.ts
663
- var falSpeechModelOptionsSchema = z5.looseObject({
664
- voice_setting: z5.object({
665
- speed: z5.number().nullish(),
666
- vol: z5.number().nullish(),
667
- voice_id: z5.string().nullish(),
668
- pitch: z5.number().nullish(),
669
- english_normalization: z5.boolean().nullish(),
670
- emotion: z5.enum(FAL_EMOTIONS).nullish()
671
- }).partial().nullish(),
672
- audio_setting: z5.record(z5.string(), z5.unknown()).nullish(),
673
- language_boost: z5.enum(FAL_LANGUAGE_BOOSTS).nullish(),
674
- pronunciation_dict: z5.record(z5.string(), z5.string()).nullish()
675
- });
676
- var FalSpeechModel = class {
677
- constructor(modelId, config) {
678
- this.modelId = modelId;
679
- this.config = config;
680
- this.specificationVersion = "v3";
681
- }
682
- get provider() {
683
- return this.config.provider;
684
- }
685
- async getArgs({
686
- text,
687
- voice,
688
- outputFormat,
689
- speed,
690
- language,
691
- providerOptions
692
- }) {
693
- const warnings = [];
694
- const falOptions = await parseProviderOptions3({
695
- provider: "fal",
696
- providerOptions,
697
- schema: falSpeechModelOptionsSchema
698
- });
699
- const requestBody = {
700
- text,
701
- output_format: outputFormat === "hex" ? "hex" : "url",
702
- voice,
703
- speed,
704
- ...falOptions
705
- };
706
- if (language) {
707
- warnings.push({
708
- type: "unsupported",
709
- feature: "language",
710
- details: "fal speech models don't support 'language' directly; consider providerOptions.fal.language_boost"
711
- });
712
- }
713
- if (outputFormat && outputFormat !== "url" && outputFormat !== "hex") {
714
- warnings.push({
715
- type: "unsupported",
716
- feature: "outputFormat",
717
- details: `Unsupported outputFormat: ${outputFormat}. Using 'url' instead.`
718
- });
719
- }
720
- return { requestBody, warnings };
721
- }
722
- async doGenerate(options) {
723
- var _a, _b, _c;
724
- const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
725
- const { requestBody, warnings } = await this.getArgs(options);
726
- const {
727
- value: json,
728
- responseHeaders,
729
- rawValue
730
- } = await postJsonToApi3({
731
- url: this.config.url({
732
- path: `https://fal.run/${this.modelId}`,
733
- modelId: this.modelId
734
- }),
735
- headers: combineHeaders3(this.config.headers(), options.headers),
736
- body: requestBody,
737
- failedResponseHandler: falFailedResponseHandler2,
738
- successfulResponseHandler: createJsonResponseHandler3(
739
- falSpeechResponseSchema
740
- ),
741
- abortSignal: options.abortSignal,
742
- fetch: this.config.fetch
743
- });
744
- const audioUrl = json.audio.url;
745
- const { value: audio } = await getFromApi3({
746
- url: audioUrl,
747
- failedResponseHandler: createStatusCodeErrorResponseHandler2(),
748
- successfulResponseHandler: createBinaryResponseHandler2(),
749
- abortSignal: options.abortSignal,
750
- fetch: this.config.fetch
751
- });
752
- return {
753
- audio,
754
- warnings,
755
- request: {
756
- body: JSON.stringify(requestBody)
757
- },
758
- response: {
759
- timestamp: currentDate,
760
- modelId: this.modelId,
761
- headers: responseHeaders,
762
- body: rawValue
763
- }
764
- };
765
- }
766
- };
767
- var falSpeechResponseSchema = z5.object({
768
- audio: z5.object({ url: z5.string() }),
769
- duration_ms: z5.number().optional(),
770
- request_id: z5.string().optional()
771
- });
772
-
773
- // src/fal-video-model.ts
774
- import {
775
- AISDKError as AISDKError2
776
- } from "@ai-sdk/provider";
777
- import {
778
- combineHeaders as combineHeaders4,
779
- convertImageModelFileToDataUri as convertImageModelFileToDataUri2,
780
- createJsonErrorResponseHandler as createJsonErrorResponseHandler4,
781
- createJsonResponseHandler as createJsonResponseHandler4,
782
- delay as delay2,
783
- getFromApi as getFromApi4,
784
- lazySchema as lazySchema2,
785
- parseProviderOptions as parseProviderOptions4,
786
- postJsonToApi as postJsonToApi4,
787
- zodSchema as zodSchema2
788
- } from "@ai-sdk/provider-utils";
789
- import { z as z6 } from "zod/v4";
790
- var falVideoModelOptionsSchema = lazySchema2(
791
- () => zodSchema2(
792
- z6.object({
793
- // Video loop - only for Luma models
794
- loop: z6.boolean().nullish(),
795
- // Motion strength (provider-specific)
796
- motionStrength: z6.number().min(0).max(1).nullish(),
797
- // Polling configuration
798
- pollIntervalMs: z6.number().positive().nullish(),
799
- pollTimeoutMs: z6.number().positive().nullish(),
800
- // Resolution (model-specific, e.g., '480p', '720p', '1080p')
801
- resolution: z6.string().nullish(),
802
- // Model-specific parameters
803
- negativePrompt: z6.string().nullish(),
804
- promptOptimizer: z6.boolean().nullish()
805
- }).passthrough()
806
- )
807
- );
808
- var FalVideoModel = class {
809
- constructor(modelId, config) {
810
- this.modelId = modelId;
811
- this.config = config;
812
- this.specificationVersion = "v3";
813
- this.maxVideosPerCall = 1;
814
- }
815
- // FAL video models support 1 video at a time
816
- get provider() {
817
- return this.config.provider;
818
- }
819
- get normalizedModelId() {
820
- return this.modelId.replace(/^fal-ai\//, "").replace(/^fal\//, "");
821
- }
822
- async doGenerate(options) {
823
- var _a, _b, _c, _d, _e, _f, _g;
824
- const currentDate = (_c = (_b = (_a = this.config._internal) == null ? void 0 : _a.currentDate) == null ? void 0 : _b.call(_a)) != null ? _c : /* @__PURE__ */ new Date();
825
- const warnings = [];
826
- const falOptions = await parseProviderOptions4({
827
- provider: "fal",
828
- providerOptions: options.providerOptions,
829
- schema: falVideoModelOptionsSchema
830
- });
831
- const body = {};
832
- if (options.prompt != null) {
833
- body.prompt = options.prompt;
834
- }
835
- if (options.image != null) {
836
- if (options.image.type === "url") {
837
- body.image_url = options.image.url;
838
- } else {
839
- body.image_url = convertImageModelFileToDataUri2(options.image);
840
- }
841
- }
842
- if (options.aspectRatio) {
843
- body.aspect_ratio = options.aspectRatio;
844
- }
845
- if (options.duration) {
846
- body.duration = `${options.duration}s`;
847
- }
848
- if (options.seed) {
849
- body.seed = options.seed;
850
- }
851
- if (falOptions != null) {
852
- const opts = falOptions;
853
- if (opts.loop !== void 0 && opts.loop !== null) {
854
- body.loop = opts.loop;
855
- }
856
- if (opts.motionStrength !== void 0 && opts.motionStrength !== null) {
857
- body.motion_strength = opts.motionStrength;
858
- }
859
- if (opts.resolution !== void 0 && opts.resolution !== null) {
860
- body.resolution = opts.resolution;
861
- }
862
- if (opts.negativePrompt !== void 0 && opts.negativePrompt !== null) {
863
- body.negative_prompt = opts.negativePrompt;
864
- }
865
- if (opts.promptOptimizer !== void 0 && opts.promptOptimizer !== null) {
866
- body.prompt_optimizer = opts.promptOptimizer;
867
- }
868
- for (const [key, value] of Object.entries(opts)) {
869
- if (![
870
- "loop",
871
- "motionStrength",
872
- "pollIntervalMs",
873
- "pollTimeoutMs",
874
- "resolution",
875
- "negativePrompt",
876
- "promptOptimizer"
877
- ].includes(key)) {
878
- body[key] = value;
879
- }
880
- }
881
- }
882
- const { value: queueResponse } = await postJsonToApi4({
883
- url: this.config.url({
884
- path: `https://queue.fal.run/fal-ai/${this.normalizedModelId}`,
885
- modelId: this.modelId
886
- }),
887
- headers: combineHeaders4(this.config.headers(), options.headers),
888
- body,
889
- failedResponseHandler: falFailedResponseHandler2,
890
- successfulResponseHandler: createJsonResponseHandler4(falJobResponseSchema2),
891
- abortSignal: options.abortSignal,
892
- fetch: this.config.fetch
893
- });
894
- const responseUrl = queueResponse.response_url;
895
- if (!responseUrl) {
896
- throw new AISDKError2({
897
- name: "FAL_VIDEO_GENERATION_ERROR",
898
- message: "No response URL returned from queue endpoint"
899
- });
900
- }
901
- const pollIntervalMs = (_d = falOptions == null ? void 0 : falOptions.pollIntervalMs) != null ? _d : 2e3;
902
- const pollTimeoutMs = (_e = falOptions == null ? void 0 : falOptions.pollTimeoutMs) != null ? _e : 3e5;
903
- const startTime = Date.now();
904
- let response;
905
- let responseHeaders;
906
- while (true) {
907
- try {
908
- const { value: statusResponse, responseHeaders: statusHeaders } = await getFromApi4({
909
- url: this.config.url({
910
- path: responseUrl,
911
- modelId: this.modelId
912
- }),
913
- headers: combineHeaders4(this.config.headers(), options.headers),
914
- failedResponseHandler: async ({
915
- response: response2,
916
- url,
917
- requestBodyValues
918
- }) => {
919
- const body2 = await response2.clone().json();
920
- if (body2.detail === "Request is still in progress") {
921
- return {
922
- value: new Error("Request is still in progress"),
923
- rawValue: body2,
924
- responseHeaders: {}
925
- };
926
- }
927
- return createJsonErrorResponseHandler4({
928
- errorSchema: falErrorDataSchema,
929
- errorToMessage: (data) => data.error.message
930
- })({ response: response2, url, requestBodyValues });
931
- },
932
- successfulResponseHandler: createJsonResponseHandler4(
933
- falVideoResponseSchema
934
- ),
935
- abortSignal: options.abortSignal,
936
- fetch: this.config.fetch
937
- });
938
- response = statusResponse;
939
- responseHeaders = statusHeaders;
940
- break;
941
- } catch (error) {
942
- if (error instanceof Error && error.message === "Request is still in progress") {
943
- } else {
944
- throw error;
945
- }
946
- }
947
- if (Date.now() - startTime > pollTimeoutMs) {
948
- throw new AISDKError2({
949
- name: "FAL_VIDEO_GENERATION_TIMEOUT",
950
- message: `Video generation request timed out after ${pollTimeoutMs}ms`
951
- });
952
- }
953
- await delay2(pollIntervalMs);
954
- if ((_f = options.abortSignal) == null ? void 0 : _f.aborted) {
955
- throw new AISDKError2({
956
- name: "FAL_VIDEO_GENERATION_ABORTED",
957
- message: "Video generation request was aborted"
958
- });
959
- }
960
- }
961
- const videoUrl = (_g = response.video) == null ? void 0 : _g.url;
962
- if (!videoUrl || !response.video) {
963
- throw new AISDKError2({
964
- name: "FAL_VIDEO_GENERATION_ERROR",
965
- message: "No video URL in response"
966
- });
967
- }
968
- return {
969
- videos: [
970
- {
971
- type: "url",
972
- url: videoUrl,
973
- mediaType: response.video.content_type || "video/mp4"
974
- }
975
- ],
976
- warnings,
977
- response: {
978
- timestamp: currentDate,
979
- modelId: this.modelId,
980
- headers: responseHeaders
981
- },
982
- providerMetadata: {
983
- fal: {
984
- videos: [
985
- {
986
- url: videoUrl,
987
- width: response.video.width,
988
- height: response.video.height,
989
- duration: response.video.duration,
990
- fps: response.video.fps,
991
- contentType: response.video.content_type
992
- }
993
- ],
994
- ...response.seed !== void 0 ? { seed: response.seed } : {},
995
- ...response.timings ? { timings: response.timings } : {},
996
- ...response.has_nsfw_concepts !== void 0 ? { has_nsfw_concepts: response.has_nsfw_concepts } : {},
997
- ...response.prompt ? { prompt: response.prompt } : {}
998
- }
999
- }
1000
- };
1001
- }
1002
- };
1003
- var falJobResponseSchema2 = z6.object({
1004
- request_id: z6.string().nullish(),
1005
- response_url: z6.string().nullish()
1006
- });
1007
- var falVideoResponseSchema = z6.object({
1008
- video: z6.object({
1009
- url: z6.string(),
1010
- width: z6.number().nullish(),
1011
- height: z6.number().nullish(),
1012
- duration: z6.number().nullish(),
1013
- fps: z6.number().nullish(),
1014
- content_type: z6.string().nullish()
1015
- }).nullish(),
1016
- seed: z6.number().nullish(),
1017
- timings: z6.object({
1018
- inference: z6.number().nullish()
1019
- }).nullish(),
1020
- has_nsfw_concepts: z6.array(z6.boolean()).nullish(),
1021
- prompt: z6.string().nullish()
1022
- });
1023
-
1024
- // src/version.ts
1025
- var VERSION = true ? "3.0.0-beta.3" : "0.0.0-test";
1026
-
1027
- // src/fal-provider.ts
1028
- var defaultBaseURL = "https://fal.run";
1029
- function loadFalApiKey({
1030
- apiKey,
1031
- description = "fal.ai"
1032
- }) {
1033
- if (typeof apiKey === "string") {
1034
- return apiKey;
1035
- }
1036
- if (apiKey != null) {
1037
- throw new Error(`${description} API key must be a string.`);
1038
- }
1039
- if (typeof process === "undefined") {
1040
- throw new Error(
1041
- `${description} API key is missing. Pass it using the 'apiKey' parameter. Environment variables are not supported in this environment.`
1042
- );
1043
- }
1044
- let envApiKey = process.env.FAL_API_KEY;
1045
- if (envApiKey == null) {
1046
- envApiKey = process.env.FAL_KEY;
1047
- }
1048
- if (envApiKey == null) {
1049
- throw new Error(
1050
- `${description} API key is missing. Pass it using the 'apiKey' parameter or set either the FAL_API_KEY or FAL_KEY environment variable.`
1051
- );
1052
- }
1053
- if (typeof envApiKey !== "string") {
1054
- throw new Error(
1055
- `${description} API key must be a string. The value of the environment variable is not a string.`
1056
- );
1057
- }
1058
- return envApiKey;
1059
- }
1060
- function createFal(options = {}) {
1061
- var _a;
1062
- const baseURL = withoutTrailingSlash((_a = options.baseURL) != null ? _a : defaultBaseURL);
1063
- const getHeaders = () => withUserAgentSuffix(
1064
- {
1065
- Authorization: `Key ${loadFalApiKey({
1066
- apiKey: options.apiKey
1067
- })}`,
1068
- ...options.headers
1069
- },
1070
- `ai-sdk/fal/${VERSION}`
1071
- );
1072
- const createImageModel = (modelId) => new FalImageModel(modelId, {
1073
- provider: "fal.image",
1074
- baseURL: baseURL != null ? baseURL : defaultBaseURL,
1075
- headers: getHeaders,
1076
- fetch: options.fetch
1077
- });
1078
- const createSpeechModel = (modelId) => new FalSpeechModel(modelId, {
1079
- provider: `fal.speech`,
1080
- url: ({ path }) => path,
1081
- headers: getHeaders,
1082
- fetch: options.fetch
1083
- });
1084
- const createTranscriptionModel = (modelId) => new FalTranscriptionModel(modelId, {
1085
- provider: `fal.transcription`,
1086
- url: ({ path }) => path,
1087
- headers: getHeaders,
1088
- fetch: options.fetch
1089
- });
1090
- const createVideoModel = (modelId) => new FalVideoModel(modelId, {
1091
- provider: "fal.video",
1092
- url: ({ path }) => path,
1093
- headers: getHeaders,
1094
- fetch: options.fetch
1095
- });
1096
- const embeddingModel = (modelId) => {
1097
- throw new NoSuchModelError({
1098
- modelId,
1099
- modelType: "embeddingModel"
1100
- });
1101
- };
1102
- return {
1103
- specificationVersion: "v3",
1104
- imageModel: createImageModel,
1105
- image: createImageModel,
1106
- languageModel: (modelId) => {
1107
- throw new NoSuchModelError({
1108
- modelId,
1109
- modelType: "languageModel"
1110
- });
1111
- },
1112
- speech: createSpeechModel,
1113
- embeddingModel,
1114
- textEmbeddingModel: embeddingModel,
1115
- transcription: createTranscriptionModel,
1116
- video: createVideoModel,
1117
- videoModel: createVideoModel
1118
- };
1119
- }
1120
- var fal = createFal();
1121
- export {
1122
- VERSION,
1123
- createFal,
1124
- fal
1125
- };
1126
- //# sourceMappingURL=index.mjs.map