@tanstack/ai-fal 0.7.3 → 0.7.4
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/esm/adapters/image.js.map +1 -1
- package/dist/esm/image/image-provider-options.js +10 -6
- package/dist/esm/image/image-provider-options.js.map +1 -1
- package/dist/esm/model-meta.d.ts +11 -9
- package/dist/esm/video/video-provider-options.js +11 -7
- package/dist/esm/video/video-provider-options.js.map +1 -1
- package/package.json +4 -4
- package/src/adapters/image.ts +4 -1
- package/src/image/image-provider-options.ts +14 -9
- package/src/model-meta.ts +17 -10
- package/src/video/video-provider-options.ts +14 -14
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image.js","sources":["../../../src/adapters/image.ts"],"sourcesContent":["import { fal } from '@fal-ai/client'\nimport { BaseImageAdapter } from '@tanstack/ai/adapters'\nimport { configureFalClient, generateId as utilGenerateId } from '../utils'\nimport { mapSizeToFalFormat } from '../image/image-provider-options'\nimport type { OutputType, Result } from '@fal-ai/client'\nimport type { FalClientConfig } from '../utils'\nimport type {\n GeneratedImage,\n ImageGenerationOptions,\n ImageGenerationResult,\n} from '@tanstack/ai'\nimport type {\n FalImageProviderOptions,\n FalModel,\n FalModelImageSize,\n FalModelInput,\n} from '../model-meta'\n\n/**\n * fal.ai image generation adapter with full type inference.\n *\n * Uses fal.ai's comprehensive type system to provide autocomplete\n *\n * and type safety for all 600+ supported models.\n *\n * @example\n * ```typescript\n * const adapter = falImage('fal-ai/flux/dev')\n * const result = await adapter.generateImages({\n * model: 'fal-ai/flux/dev',\n * prompt: 'a cat',\n * modelOptions: {\n * num_inference_steps: 28, // Type-safe! Autocomplete works\n * guidance_scale: 3.5,\n * },\n * })\n * ```\n */\nexport class FalImageAdapter<TModel extends FalModel> extends BaseImageAdapter<\n TModel,\n FalImageProviderOptions<TModel>,\n Record<TModel, FalImageProviderOptions<TModel>>,\n Record<TModel, FalModelImageSize<TModel>>\n> {\n readonly kind = 'image' as const\n readonly name = 'fal' as const\n\n constructor(model: TModel, config?: FalClientConfig) {\n super(model, {})\n configureFalClient(config)\n }\n\n async generateImages(\n options: ImageGenerationOptions<\n FalImageProviderOptions<TModel>,\n FalModelImageSize<TModel>\n >,\n ): Promise<ImageGenerationResult> {\n const { logger } = options\n\n logger.request(`activity=generateImage provider=fal model=${this.model}`, {\n provider: 'fal',\n model: this.model,\n })\n\n try {\n const input = this.buildInput(options)\n const result = await fal.subscribe(this.model, { input })\n return this.transformResponse(result)\n } catch (error) {\n logger.errors('fal.generateImage fatal', {\n error,\n source: 'fal.generateImage',\n })\n throw error\n }\n }\n\n private buildInput(\n options: ImageGenerationOptions
|
|
1
|
+
{"version":3,"file":"image.js","sources":["../../../src/adapters/image.ts"],"sourcesContent":["import { fal } from '@fal-ai/client'\nimport { BaseImageAdapter } from '@tanstack/ai/adapters'\nimport { configureFalClient, generateId as utilGenerateId } from '../utils'\nimport { mapSizeToFalFormat } from '../image/image-provider-options'\nimport type { OutputType, Result } from '@fal-ai/client'\nimport type { FalClientConfig } from '../utils'\nimport type {\n GeneratedImage,\n ImageGenerationOptions,\n ImageGenerationResult,\n} from '@tanstack/ai'\nimport type {\n FalImageProviderOptions,\n FalModel,\n FalModelImageSize,\n FalModelInput,\n} from '../model-meta'\n\n/**\n * fal.ai image generation adapter with full type inference.\n *\n * Uses fal.ai's comprehensive type system to provide autocomplete\n *\n * and type safety for all 600+ supported models.\n *\n * @example\n * ```typescript\n * const adapter = falImage('fal-ai/flux/dev')\n * const result = await adapter.generateImages({\n * model: 'fal-ai/flux/dev',\n * prompt: 'a cat',\n * modelOptions: {\n * num_inference_steps: 28, // Type-safe! Autocomplete works\n * guidance_scale: 3.5,\n * },\n * })\n * ```\n */\nexport class FalImageAdapter<TModel extends FalModel> extends BaseImageAdapter<\n TModel,\n FalImageProviderOptions<TModel>,\n Record<TModel, FalImageProviderOptions<TModel>>,\n Record<TModel, FalModelImageSize<TModel>>\n> {\n readonly kind = 'image' as const\n readonly name = 'fal' as const\n\n constructor(model: TModel, config?: FalClientConfig) {\n super(model, {})\n configureFalClient(config)\n }\n\n async generateImages(\n options: ImageGenerationOptions<\n FalImageProviderOptions<TModel>,\n FalModelImageSize<TModel>\n >,\n ): Promise<ImageGenerationResult> {\n const { logger } = options\n\n logger.request(`activity=generateImage provider=fal model=${this.model}`, {\n provider: 'fal',\n model: this.model,\n })\n\n try {\n const input = this.buildInput(options)\n const result = await fal.subscribe(this.model, { input })\n return this.transformResponse(result)\n } catch (error) {\n logger.errors('fal.generateImage fatal', {\n error,\n source: 'fal.generateImage',\n })\n throw error\n }\n }\n\n private buildInput(\n options: ImageGenerationOptions<\n FalImageProviderOptions<TModel>,\n FalModelImageSize<TModel>\n >,\n ): FalModelInput<TModel> {\n const sizeParams = mapSizeToFalFormat(options.size)\n const input = {\n ...options.modelOptions,\n ...sizeParams,\n prompt: options.prompt,\n num_images: options.numberOfImages,\n } as FalModelInput<TModel>\n return input\n }\n\n protected override generateId(): string {\n return utilGenerateId(this.name)\n }\n\n private transformResponse(\n response: Result<OutputType<TModel>>,\n ): ImageGenerationResult {\n const images: Array<GeneratedImage> = []\n const data = response.data\n\n // Handle array of images (most models return { images: [...] })\n if ('images' in data && Array.isArray(data.images)) {\n for (const img of data.images) {\n images.push(this.parseImage(img))\n }\n }\n // Handle single image response (some models return { image: {...} })\n else if ('image' in data && data.image && typeof data.image === 'object') {\n images.push(this.parseImage(data.image))\n }\n\n if (images.length === 0) {\n throw new Error(\n 'Unexpected fal image response shape. Expected images[] or image{}. Got keys: ' +\n Object.keys(data).join(','),\n )\n }\n\n return {\n id: response.requestId || this.generateId(),\n model: this.model,\n images,\n }\n }\n\n private parseImage(img: unknown): GeneratedImage {\n let url: string\n if (typeof img === 'string') {\n url = img\n } else if (\n img &&\n typeof img === 'object' &&\n 'url' in img &&\n typeof (img as { url: unknown }).url === 'string'\n ) {\n url = (img as { url: string }).url\n } else {\n throw new Error(\n `Invalid image payload from fal response: expected string or { url: string }, received ${\n img === null ? 'null' : typeof img\n }`,\n )\n }\n\n if (url.startsWith('data:')) {\n const base64Match = url.match(/^data:image\\/[^;]+;base64,(.+)$/)\n if (base64Match && base64Match[1]) {\n return { b64Json: base64Match[1] }\n }\n }\n return { url }\n }\n}\n\nexport function createFalImage<TModel extends FalModel>(\n model: TModel,\n config?: FalClientConfig,\n): FalImageAdapter<TModel> {\n return new FalImageAdapter(model, config)\n}\n\nexport function falImage<TModel extends FalModel>(\n model: TModel,\n config?: FalClientConfig,\n): FalImageAdapter<TModel> {\n return createFalImage(model, config)\n}\n"],"names":["utilGenerateId"],"mappings":";;;;AAsCO,MAAM,wBAAiD,iBAK5D;AAAA,EAIA,YAAY,OAAe,QAA0B;AACnD,UAAM,OAAO,EAAE;AAJjB,SAAS,OAAO;AAChB,SAAS,OAAO;AAId,uBAAmB,MAAM;AAAA,EAC3B;AAAA,EAEA,MAAM,eACJ,SAIgC;AAChC,UAAM,EAAE,WAAW;AAEnB,WAAO,QAAQ,6CAA6C,KAAK,KAAK,IAAI;AAAA,MACxE,UAAU;AAAA,MACV,OAAO,KAAK;AAAA,IAAA,CACb;AAED,QAAI;AACF,YAAM,QAAQ,KAAK,WAAW,OAAO;AACrC,YAAM,SAAS,MAAM,IAAI,UAAU,KAAK,OAAO,EAAE,OAAO;AACxD,aAAO,KAAK,kBAAkB,MAAM;AAAA,IACtC,SAAS,OAAO;AACd,aAAO,OAAO,2BAA2B;AAAA,QACvC;AAAA,QACA,QAAQ;AAAA,MAAA,CACT;AACD,YAAM;AAAA,IACR;AAAA,EACF;AAAA,EAEQ,WACN,SAIuB;AACvB,UAAM,aAAa,mBAAmB,QAAQ,IAAI;AAClD,UAAM,QAAQ;AAAA,MACZ,GAAG,QAAQ;AAAA,MACX,GAAG;AAAA,MACH,QAAQ,QAAQ;AAAA,MAChB,YAAY,QAAQ;AAAA,IAAA;AAEtB,WAAO;AAAA,EACT;AAAA,EAEmB,aAAqB;AACtC,WAAOA,WAAe,KAAK,IAAI;AAAA,EACjC;AAAA,EAEQ,kBACN,UACuB;AACvB,UAAM,SAAgC,CAAA;AACtC,UAAM,OAAO,SAAS;AAGtB,QAAI,YAAY,QAAQ,MAAM,QAAQ,KAAK,MAAM,GAAG;AAClD,iBAAW,OAAO,KAAK,QAAQ;AAC7B,eAAO,KAAK,KAAK,WAAW,GAAG,CAAC;AAAA,MAClC;AAAA,IACF,WAES,WAAW,QAAQ,KAAK,SAAS,OAAO,KAAK,UAAU,UAAU;AACxE,aAAO,KAAK,KAAK,WAAW,KAAK,KAAK,CAAC;AAAA,IACzC;AAEA,QAAI,OAAO,WAAW,GAAG;AACvB,YAAM,IAAI;AAAA,QACR,kFACE,OAAO,KAAK,IAAI,EAAE,KAAK,GAAG;AAAA,MAAA;AAAA,IAEhC;AAEA,WAAO;AAAA,MACL,IAAI,SAAS,aAAa,KAAK,WAAA;AAAA,MAC/B,OAAO,KAAK;AAAA,MACZ;AAAA,IAAA;AAAA,EAEJ;AAAA,EAEQ,WAAW,KAA8B;AAC/C,QAAI;AACJ,QAAI,OAAO,QAAQ,UAAU;AAC3B,YAAM;AAAA,IACR,WACE,OACA,OAAO,QAAQ,YACf,SAAS,OACT,OAAQ,IAAyB,QAAQ,UACzC;AACA,YAAO,IAAwB;AAAA,IACjC,OAAO;AACL,YAAM,IAAI;AAAA,QACR,yFACE,QAAQ,OAAO,SAAS,OAAO,GACjC;AAAA,MAAA;AAAA,IAEJ;AAEA,QAAI,IAAI,WAAW,OAAO,GAAG;AAC3B,YAAM,cAAc,IAAI,MAAM,iCAAiC;AAC/D,UAAI,eAAe,YAAY,CAAC,GAAG;AACjC,eAAO,EAAE,SAAS,YAAY,CAAC,EAAA;AAAA,MACjC;AAAA,IACF;AACA,WAAO,EAAE,IAAA;AAAA,EACX;AACF;AAEO,SAAS,eACd,OACA,QACyB;AACzB,SAAO,IAAI,gBAAgB,OAAO,MAAM;AAC1C;AAEO,SAAS,SACd,OACA,QACyB;AACzB,SAAO,eAAe,OAAO,MAAM;AACrC;"}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
function mapSizeToFalFormat(size) {
|
|
2
2
|
if (!size) return void 0;
|
|
3
3
|
if (typeof size === "string") {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
if (size.includes("_")) {
|
|
5
|
+
const [first, second] = size.split("_");
|
|
6
|
+
if (first && first.includes(":")) {
|
|
7
|
+
return {
|
|
8
|
+
aspect_ratio: first,
|
|
9
|
+
resolution: second
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
} else if (size.includes(":")) {
|
|
13
|
+
return { aspect_ratio: size };
|
|
10
14
|
}
|
|
11
15
|
}
|
|
12
16
|
return {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"image-provider-options.js","sources":["../../../src/image/image-provider-options.ts"],"sourcesContent":["import type { FalModelImageSize, FalModelImageSizeInput } from '../model-meta'\n\nexport function mapSizeToFalFormat<TModel extends string>(\n size: FalModelImageSize<TModel> | undefined,\n): FalModelImageSizeInput<TModel> | undefined {\n if (!size) return undefined\n\n // \"16:9_4K\"
|
|
1
|
+
{"version":3,"file":"image-provider-options.js","sources":["../../../src/image/image-provider-options.ts"],"sourcesContent":["import type { FalModelImageSize, FalModelImageSizeInput } from '../model-meta'\n\nexport function mapSizeToFalFormat<TModel extends string>(\n size: FalModelImageSize<TModel> | undefined,\n): FalModelImageSizeInput<TModel> | undefined {\n if (!size) return undefined\n\n // \"16:9_4K\" → { aspect_ratio, resolution }\n // \"16:9\" → { aspect_ratio }\n // \"4K\" → { resolution } (no colon, no underscore, model has `resolution`)\n // \"square_hd\" → { image_size } (no colon, no resolution field on model)\n if (typeof size === 'string') {\n if (size.includes('_')) {\n const [first, second] = size.split('_')\n if (first && first.includes(':')) {\n return {\n aspect_ratio: first,\n resolution: second,\n } as FalModelImageSizeInput<TModel>\n }\n } else if (size.includes(':')) {\n return { aspect_ratio: size } as FalModelImageSizeInput<TModel>\n }\n }\n\n return {\n image_size: size,\n } as FalModelImageSizeInput<TModel>\n}\n"],"names":[],"mappings":"AAEO,SAAS,mBACd,MAC4C;AAC5C,MAAI,CAAC,KAAM,QAAO;AAMlB,MAAI,OAAO,SAAS,UAAU;AAC5B,QAAI,KAAK,SAAS,GAAG,GAAG;AACtB,YAAM,CAAC,OAAO,MAAM,IAAI,KAAK,MAAM,GAAG;AACtC,UAAI,SAAS,MAAM,SAAS,GAAG,GAAG;AAChC,eAAO;AAAA,UACL,cAAc;AAAA,UACd,YAAY;AAAA,QAAA;AAAA,MAEhB;AAAA,IACF,WAAW,KAAK,SAAS,GAAG,GAAG;AAC7B,aAAO,EAAE,cAAc,KAAA;AAAA,IACzB;AAAA,EACF;AAEA,SAAO;AAAA,IACL,YAAY;AAAA,EAAA;AAEhB;"}
|
package/dist/esm/model-meta.d.ts
CHANGED
|
@@ -29,15 +29,15 @@ export type FalModelOutput<TModel extends string> = TModel extends keyof Endpoin
|
|
|
29
29
|
* type FluxSize = FalModelImageSize<'fal-ai/flux/dev'>
|
|
30
30
|
* // "square_hd" | "square" | "portrait_4_3" | "portrait_16_9" | ...
|
|
31
31
|
*/
|
|
32
|
-
export type FalModelImageSize<TModel extends string> = TModel extends keyof EndpointTypeMap ? 'image_size' extends keyof EndpointTypeMap[TModel]['input'] ? NonNullable<Exclude<FalModelInput<TModel>['image_size'], object>> : 'aspect_ratio' extends keyof EndpointTypeMap[TModel]['input'] ? 'resolution' extends keyof EndpointTypeMap[TModel]['input'] ? `${NonNullable<FalModelInput<TModel>['aspect_ratio']>}_${NonNullable<FalModelInput<TModel>['resolution']>}` : NonNullable<FalModelInput<TModel>['aspect_ratio']> :
|
|
32
|
+
export type FalModelImageSize<TModel extends string> = TModel extends keyof EndpointTypeMap ? 'image_size' extends keyof EndpointTypeMap[TModel]['input'] ? NonNullable<Exclude<FalModelInput<TModel>['image_size'], object>> : 'aspect_ratio' extends keyof EndpointTypeMap[TModel]['input'] ? 'resolution' extends keyof EndpointTypeMap[TModel]['input'] ? `${Extract<NonNullable<FalModelInput<TModel>['aspect_ratio']>, string>}_${Extract<NonNullable<FalModelInput<TModel>['resolution']>, string>}` : Extract<NonNullable<FalModelInput<TModel>['aspect_ratio']>, string> : undefined : string;
|
|
33
33
|
export type FalModelImageSizeInput<TModel extends string> = TModel extends keyof EndpointTypeMap ? 'aspect_ratio' extends keyof EndpointTypeMap[TModel]['input'] ? 'resolution' extends keyof EndpointTypeMap[TModel]['input'] ? {
|
|
34
34
|
aspect_ratio: FalModelInput<TModel>['aspect_ratio'];
|
|
35
35
|
resolution: FalModelInput<TModel>['resolution'];
|
|
36
36
|
} : {
|
|
37
37
|
aspect_ratio: NonNullable<FalModelInput<TModel>['aspect_ratio']>;
|
|
38
|
-
} : {
|
|
38
|
+
} : 'image_size' extends keyof EndpointTypeMap[TModel]['input'] ? {
|
|
39
39
|
image_size: FalModelImageSize<TModel>;
|
|
40
|
-
} : {
|
|
40
|
+
} : never : {
|
|
41
41
|
image_size: string;
|
|
42
42
|
};
|
|
43
43
|
/**
|
|
@@ -53,21 +53,23 @@ export type FalImageProviderOptions<TModel extends string> = Omit<FalModelInput<
|
|
|
53
53
|
* Extract the video size type supported by a specific fal model.
|
|
54
54
|
* Video models typically use aspect_ratio and/or resolution fields.
|
|
55
55
|
*
|
|
56
|
-
* Follows the same pattern as FalModelImageSize:
|
|
57
56
|
* - aspect_ratio + resolution → "16:9_720p"
|
|
58
57
|
* - aspect_ratio only → "16:9"
|
|
58
|
+
* - resolution only → "720p"
|
|
59
|
+
* - neither → undefined (the model takes no size param)
|
|
59
60
|
* - unknown models → string
|
|
60
61
|
*/
|
|
61
|
-
export type FalModelVideoSize<TModel extends string> = TModel extends keyof EndpointTypeMap ? 'aspect_ratio' extends keyof EndpointTypeMap[TModel]['input'] ? 'resolution' extends keyof EndpointTypeMap[TModel]['input'] ? `${NonNullable<FalModelInput<TModel>['aspect_ratio']>}_${NonNullable<FalModelInput<TModel>['resolution']>}` : NonNullable<FalModelInput<TModel>['aspect_ratio']> :
|
|
62
|
+
export type FalModelVideoSize<TModel extends string> = TModel extends keyof EndpointTypeMap ? 'aspect_ratio' extends keyof EndpointTypeMap[TModel]['input'] ? 'resolution' extends keyof EndpointTypeMap[TModel]['input'] ? `${Extract<NonNullable<FalModelInput<TModel>['aspect_ratio']>, string>}_${Extract<NonNullable<FalModelInput<TModel>['resolution']>, string>}` : Extract<NonNullable<FalModelInput<TModel>['aspect_ratio']>, string> : 'resolution' extends keyof EndpointTypeMap[TModel]['input'] ? Extract<NonNullable<FalModelInput<TModel>['resolution']>, string> : undefined : string;
|
|
62
63
|
export type FalModelVideoSizeInput<TModel extends string> = TModel extends keyof EndpointTypeMap ? 'aspect_ratio' extends keyof EndpointTypeMap[TModel]['input'] ? 'resolution' extends keyof EndpointTypeMap[TModel]['input'] ? {
|
|
63
64
|
aspect_ratio: FalModelInput<TModel>['aspect_ratio'];
|
|
64
65
|
resolution: FalModelInput<TModel>['resolution'];
|
|
65
66
|
} : {
|
|
66
67
|
aspect_ratio: NonNullable<FalModelInput<TModel>['aspect_ratio']>;
|
|
67
|
-
} : {
|
|
68
|
-
|
|
69
|
-
} : {
|
|
70
|
-
aspect_ratio
|
|
68
|
+
} : 'resolution' extends keyof EndpointTypeMap[TModel]['input'] ? {
|
|
69
|
+
resolution: NonNullable<FalModelInput<TModel>['resolution']>;
|
|
70
|
+
} : never : {
|
|
71
|
+
aspect_ratio?: string;
|
|
72
|
+
resolution?: string;
|
|
71
73
|
};
|
|
72
74
|
/**
|
|
73
75
|
* Provider options for video generation, excluding fields TanStack AI handles.
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
function mapVideoSizeToFalFormat(size) {
|
|
2
2
|
if (!size) return void 0;
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
resolution
|
|
8
|
-
}
|
|
9
|
-
}
|
|
3
|
+
if (size.includes("_")) {
|
|
4
|
+
const [aspect_ratio, resolution] = size.split("_");
|
|
5
|
+
return {
|
|
6
|
+
aspect_ratio,
|
|
7
|
+
resolution
|
|
8
|
+
};
|
|
9
|
+
}
|
|
10
|
+
if (size.includes(":")) {
|
|
11
|
+
return { aspect_ratio: size };
|
|
12
|
+
}
|
|
13
|
+
return { resolution: size };
|
|
10
14
|
}
|
|
11
15
|
export {
|
|
12
16
|
mapVideoSizeToFalFormat
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"video-provider-options.js","sources":["../../../src/video/video-provider-options.ts"],"sourcesContent":["import type {
|
|
1
|
+
{"version":3,"file":"video-provider-options.js","sources":["../../../src/video/video-provider-options.ts"],"sourcesContent":["import type { FalModelVideoSize, FalModelVideoSizeInput } from '../model-meta'\n\nexport function mapVideoSizeToFalFormat<TModel extends string>(\n size: FalModelVideoSize<TModel> | undefined,\n): FalModelVideoSizeInput<TModel> | undefined {\n if (!size) return undefined\n\n // \"16:9_720p\" → { aspect_ratio, resolution }\n // \"16:9\" → { aspect_ratio }\n // \"720p\" → { resolution }\n if (size.includes('_')) {\n const [aspect_ratio, resolution] = size.split('_')\n return {\n aspect_ratio,\n resolution,\n } as unknown as FalModelVideoSizeInput<TModel>\n }\n\n if (size.includes(':')) {\n return { aspect_ratio: size } as unknown as FalModelVideoSizeInput<TModel>\n }\n\n return { resolution: size } as unknown as FalModelVideoSizeInput<TModel>\n}\n"],"names":[],"mappings":"AAEO,SAAS,wBACd,MAC4C;AAC5C,MAAI,CAAC,KAAM,QAAO;AAKlB,MAAI,KAAK,SAAS,GAAG,GAAG;AACtB,UAAM,CAAC,cAAc,UAAU,IAAI,KAAK,MAAM,GAAG;AACjD,WAAO;AAAA,MACL;AAAA,MACA;AAAA,IAAA;AAAA,EAEJ;AAEA,MAAI,KAAK,SAAS,GAAG,GAAG;AACtB,WAAO,EAAE,cAAc,KAAA;AAAA,EACzB;AAEA,SAAO,EAAE,YAAY,KAAA;AACvB;"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack/ai-fal",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.4",
|
|
4
4
|
"description": "fal.ai adapter for TanStack AI",
|
|
5
5
|
"author": "",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,16 +36,16 @@
|
|
|
36
36
|
"transcription"
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@fal-ai/client": "^1.
|
|
39
|
+
"@fal-ai/client": "^1.10.1",
|
|
40
40
|
"@tanstack/ai-utils": "0.2.0"
|
|
41
41
|
},
|
|
42
42
|
"devDependencies": {
|
|
43
43
|
"@vitest/coverage-v8": "4.0.14",
|
|
44
44
|
"vite": "^7.2.7",
|
|
45
|
-
"@tanstack/ai": "0.
|
|
45
|
+
"@tanstack/ai": "0.17.0"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
|
-
"@tanstack/ai": "0.
|
|
48
|
+
"@tanstack/ai": "0.17.0"
|
|
49
49
|
},
|
|
50
50
|
"scripts": {
|
|
51
51
|
"build": "vite build",
|
package/src/adapters/image.ts
CHANGED
|
@@ -77,7 +77,10 @@ export class FalImageAdapter<TModel extends FalModel> extends BaseImageAdapter<
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
private buildInput(
|
|
80
|
-
options: ImageGenerationOptions<
|
|
80
|
+
options: ImageGenerationOptions<
|
|
81
|
+
FalImageProviderOptions<TModel>,
|
|
82
|
+
FalModelImageSize<TModel>
|
|
83
|
+
>,
|
|
81
84
|
): FalModelInput<TModel> {
|
|
82
85
|
const sizeParams = mapSizeToFalFormat(options.size)
|
|
83
86
|
const input = {
|
|
@@ -5,16 +5,21 @@ export function mapSizeToFalFormat<TModel extends string>(
|
|
|
5
5
|
): FalModelImageSizeInput<TModel> | undefined {
|
|
6
6
|
if (!size) return undefined
|
|
7
7
|
|
|
8
|
-
// "16:9_4K"
|
|
9
|
-
// "16:9"
|
|
10
|
-
//
|
|
8
|
+
// "16:9_4K" → { aspect_ratio, resolution }
|
|
9
|
+
// "16:9" → { aspect_ratio }
|
|
10
|
+
// "4K" → { resolution } (no colon, no underscore, model has `resolution`)
|
|
11
|
+
// "square_hd" → { image_size } (no colon, no resolution field on model)
|
|
11
12
|
if (typeof size === 'string') {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
13
|
+
if (size.includes('_')) {
|
|
14
|
+
const [first, second] = size.split('_')
|
|
15
|
+
if (first && first.includes(':')) {
|
|
16
|
+
return {
|
|
17
|
+
aspect_ratio: first,
|
|
18
|
+
resolution: second,
|
|
19
|
+
} as FalModelImageSizeInput<TModel>
|
|
20
|
+
}
|
|
21
|
+
} else if (size.includes(':')) {
|
|
22
|
+
return { aspect_ratio: size } as FalModelImageSizeInput<TModel>
|
|
18
23
|
}
|
|
19
24
|
}
|
|
20
25
|
|
package/src/model-meta.ts
CHANGED
|
@@ -51,9 +51,9 @@ export type FalModelImageSize<TModel extends string> =
|
|
|
51
51
|
? NonNullable<Exclude<FalModelInput<TModel>['image_size'], object>>
|
|
52
52
|
: 'aspect_ratio' extends keyof EndpointTypeMap[TModel]['input']
|
|
53
53
|
? 'resolution' extends keyof EndpointTypeMap[TModel]['input']
|
|
54
|
-
? `${NonNullable<FalModelInput<TModel>['aspect_ratio']>}_${NonNullable<FalModelInput<TModel>['resolution']>}`
|
|
55
|
-
: NonNullable<FalModelInput<TModel>['aspect_ratio']>
|
|
56
|
-
:
|
|
54
|
+
? `${Extract<NonNullable<FalModelInput<TModel>['aspect_ratio']>, string>}_${Extract<NonNullable<FalModelInput<TModel>['resolution']>, string>}`
|
|
55
|
+
: Extract<NonNullable<FalModelInput<TModel>['aspect_ratio']>, string>
|
|
56
|
+
: undefined
|
|
57
57
|
: string
|
|
58
58
|
|
|
59
59
|
export type FalModelImageSizeInput<TModel extends string> =
|
|
@@ -65,7 +65,9 @@ export type FalModelImageSizeInput<TModel extends string> =
|
|
|
65
65
|
resolution: FalModelInput<TModel>['resolution']
|
|
66
66
|
}
|
|
67
67
|
: { aspect_ratio: NonNullable<FalModelInput<TModel>['aspect_ratio']> }
|
|
68
|
-
:
|
|
68
|
+
: 'image_size' extends keyof EndpointTypeMap[TModel]['input']
|
|
69
|
+
? { image_size: FalModelImageSize<TModel> }
|
|
70
|
+
: never
|
|
69
71
|
: { image_size: string }
|
|
70
72
|
|
|
71
73
|
/**
|
|
@@ -85,18 +87,21 @@ export type FalImageProviderOptions<TModel extends string> = Omit<
|
|
|
85
87
|
* Extract the video size type supported by a specific fal model.
|
|
86
88
|
* Video models typically use aspect_ratio and/or resolution fields.
|
|
87
89
|
*
|
|
88
|
-
* Follows the same pattern as FalModelImageSize:
|
|
89
90
|
* - aspect_ratio + resolution → "16:9_720p"
|
|
90
91
|
* - aspect_ratio only → "16:9"
|
|
92
|
+
* - resolution only → "720p"
|
|
93
|
+
* - neither → undefined (the model takes no size param)
|
|
91
94
|
* - unknown models → string
|
|
92
95
|
*/
|
|
93
96
|
export type FalModelVideoSize<TModel extends string> =
|
|
94
97
|
TModel extends keyof EndpointTypeMap
|
|
95
98
|
? 'aspect_ratio' extends keyof EndpointTypeMap[TModel]['input']
|
|
96
99
|
? 'resolution' extends keyof EndpointTypeMap[TModel]['input']
|
|
97
|
-
? `${NonNullable<FalModelInput<TModel>['aspect_ratio']>}_${NonNullable<FalModelInput<TModel>['resolution']>}`
|
|
98
|
-
: NonNullable<FalModelInput<TModel>['aspect_ratio']>
|
|
99
|
-
:
|
|
100
|
+
? `${Extract<NonNullable<FalModelInput<TModel>['aspect_ratio']>, string>}_${Extract<NonNullable<FalModelInput<TModel>['resolution']>, string>}`
|
|
101
|
+
: Extract<NonNullable<FalModelInput<TModel>['aspect_ratio']>, string>
|
|
102
|
+
: 'resolution' extends keyof EndpointTypeMap[TModel]['input']
|
|
103
|
+
? Extract<NonNullable<FalModelInput<TModel>['resolution']>, string>
|
|
104
|
+
: undefined
|
|
100
105
|
: string
|
|
101
106
|
|
|
102
107
|
export type FalModelVideoSizeInput<TModel extends string> =
|
|
@@ -108,8 +113,10 @@ export type FalModelVideoSizeInput<TModel extends string> =
|
|
|
108
113
|
resolution: FalModelInput<TModel>['resolution']
|
|
109
114
|
}
|
|
110
115
|
: { aspect_ratio: NonNullable<FalModelInput<TModel>['aspect_ratio']> }
|
|
111
|
-
:
|
|
112
|
-
|
|
116
|
+
: 'resolution' extends keyof EndpointTypeMap[TModel]['input']
|
|
117
|
+
? { resolution: NonNullable<FalModelInput<TModel>['resolution']> }
|
|
118
|
+
: never
|
|
119
|
+
: { aspect_ratio?: string; resolution?: string }
|
|
113
120
|
|
|
114
121
|
/**
|
|
115
122
|
* Provider options for video generation, excluding fields TanStack AI handles.
|
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
FalModelInput,
|
|
3
|
-
FalModelVideoSize,
|
|
4
|
-
FalModelVideoSizeInput,
|
|
5
|
-
} from '../model-meta'
|
|
1
|
+
import type { FalModelVideoSize, FalModelVideoSizeInput } from '../model-meta'
|
|
6
2
|
|
|
7
3
|
export function mapVideoSizeToFalFormat<TModel extends string>(
|
|
8
4
|
size: FalModelVideoSize<TModel> | undefined,
|
|
@@ -11,14 +7,18 @@ export function mapVideoSizeToFalFormat<TModel extends string>(
|
|
|
11
7
|
|
|
12
8
|
// "16:9_720p" → { aspect_ratio, resolution }
|
|
13
9
|
// "16:9" → { aspect_ratio }
|
|
14
|
-
|
|
10
|
+
// "720p" → { resolution }
|
|
11
|
+
if (size.includes('_')) {
|
|
12
|
+
const [aspect_ratio, resolution] = size.split('_')
|
|
13
|
+
return {
|
|
14
|
+
aspect_ratio,
|
|
15
|
+
resolution,
|
|
16
|
+
} as unknown as FalModelVideoSizeInput<TModel>
|
|
17
|
+
}
|
|
15
18
|
|
|
16
|
-
|
|
17
|
-
aspect_ratio:
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
resolution: match[2] as NonNullable<FalModelInput<TModel>['resolution']>,
|
|
22
|
-
}),
|
|
23
|
-
} as FalModelVideoSizeInput<TModel>
|
|
19
|
+
if (size.includes(':')) {
|
|
20
|
+
return { aspect_ratio: size } as unknown as FalModelVideoSizeInput<TModel>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return { resolution: size } as unknown as FalModelVideoSizeInput<TModel>
|
|
24
24
|
}
|