@tanstack/ai-fal 0.9.12 → 0.9.13

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.
@@ -1,83 +1,99 @@
1
+ import { buildFalUsage, takeBillableUnits } from "../utils/billing.js";
2
+ import { configureFalClient, deriveAudioContentType, generateId } from "../utils/client.js";
1
3
  import { fal } from "@fal-ai/client";
2
4
  import { BaseAudioAdapter } from "@tanstack/ai/adapters";
3
- import { configureFalClient, generateId, deriveAudioContentType } from "../utils/client.js";
4
- import { buildFalUsage, takeBillableUnits } from "../utils/billing.js";
5
- const DURATION_FRAGMENT_BUILDERS = {
6
- "fal-ai/elevenlabs/music": (seconds) => ({ music_length_ms: seconds * 1e3 }),
7
- "fal-ai/stable-audio-25/text-to-audio": (seconds) => ({
8
- seconds_total: seconds
9
- })
5
+ //#region src/adapters/audio.ts
6
+ /**
7
+ * Fal audio models don't agree on the name for "how long should this be."
8
+ * Most accept seconds via `duration`, but a few use model-specific fields —
9
+ * e.g. ElevenLabs wants milliseconds via `music_length_ms`, Stable Audio wants
10
+ * `seconds_total`. Explicit per-model entries override the default; user
11
+ * `modelOptions` still win over either.
12
+ */
13
+ var DURATION_FRAGMENT_BUILDERS = {
14
+ "fal-ai/elevenlabs/music": (seconds) => ({ music_length_ms: seconds * 1e3 }),
15
+ "fal-ai/stable-audio-25/text-to-audio": (seconds) => ({ seconds_total: seconds })
10
16
  };
11
17
  function buildDurationFragment(model, duration) {
12
- if (duration == null) return {};
13
- const builder = DURATION_FRAGMENT_BUILDERS[model];
14
- return builder ? builder(duration) : { duration };
15
- }
16
- class FalAudioAdapter extends BaseAudioAdapter {
17
- name = "fal";
18
- constructor(model, config) {
19
- super(model, {});
20
- configureFalClient(config);
21
- }
22
- async generateAudio(options) {
23
- const { logger } = options;
24
- logger.request(`activity=generateAudio provider=fal model=${this.model}`, {
25
- provider: "fal",
26
- model: this.model
27
- });
28
- try {
29
- const input = this.buildInput(options);
30
- const result = await fal.subscribe(this.model, { input });
31
- return this.transformResponse(result);
32
- } catch (error) {
33
- logger.errors("fal.generateAudio fatal", {
34
- error,
35
- source: "fal.generateAudio"
36
- });
37
- throw error;
38
- }
39
- }
40
- buildInput(options) {
41
- return {
42
- ...buildDurationFragment(this.model, options.duration),
43
- ...options.modelOptions,
44
- prompt: options.prompt
45
- };
46
- }
47
- generateId() {
48
- return generateId(this.name);
49
- }
50
- transformResponse(response) {
51
- const data = response.data;
52
- let audioUrl;
53
- let contentType;
54
- if (data["audio"] && typeof data["audio"] === "object" && "url" in data["audio"]) {
55
- const audioObj = data["audio"];
56
- audioUrl = audioObj.url;
57
- contentType = audioObj.content_type;
58
- } else if (typeof data.audio_url === "string") {
59
- audioUrl = data.audio_url;
60
- }
61
- if (!audioUrl) {
62
- throw new Error("Audio URL not found in fal audio generation response");
63
- }
64
- const usage = buildFalUsage(takeBillableUnits(response.requestId));
65
- return {
66
- id: response.requestId || this.generateId(),
67
- model: this.model,
68
- audio: {
69
- url: audioUrl,
70
- contentType: deriveAudioContentType(contentType, audioUrl)
71
- },
72
- ...usage ? { usage } : {}
73
- };
74
- }
18
+ if (duration == null) return {};
19
+ const builder = DURATION_FRAGMENT_BUILDERS[model];
20
+ return builder ? builder(duration) : { duration };
75
21
  }
22
+ /**
23
+ * fal.ai audio generation adapter.
24
+ *
25
+ * Supports fal.ai audio models like diffrhythm (music), sound effects, etc.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * const adapter = falAudio('fal-ai/diffrhythm')
30
+ * const result = await generateAudio({
31
+ * adapter,
32
+ * prompt: 'An upbeat electronic track with synths',
33
+ * duration: 10,
34
+ * })
35
+ * ```
36
+ */
37
+ var FalAudioAdapter = class extends BaseAudioAdapter {
38
+ name = "fal";
39
+ constructor(model, config) {
40
+ super(model, {});
41
+ configureFalClient(config);
42
+ }
43
+ async generateAudio(options) {
44
+ const { logger } = options;
45
+ logger.request(`activity=generateAudio provider=fal model=${this.model}`, {
46
+ provider: "fal",
47
+ model: this.model
48
+ });
49
+ try {
50
+ const input = this.buildInput(options);
51
+ const result = await fal.subscribe(this.model, { input });
52
+ return this.transformResponse(result);
53
+ } catch (error) {
54
+ logger.errors("fal.generateAudio fatal", {
55
+ error,
56
+ source: "fal.generateAudio"
57
+ });
58
+ throw error;
59
+ }
60
+ }
61
+ buildInput(options) {
62
+ return {
63
+ ...buildDurationFragment(this.model, options.duration),
64
+ ...options.modelOptions,
65
+ prompt: options.prompt
66
+ };
67
+ }
68
+ generateId() {
69
+ return generateId(this.name);
70
+ }
71
+ transformResponse(response) {
72
+ const data = response.data;
73
+ let audioUrl;
74
+ let contentType;
75
+ if (data["audio"] && typeof data["audio"] === "object" && "url" in data["audio"]) {
76
+ const audioObj = data["audio"];
77
+ audioUrl = audioObj.url;
78
+ contentType = audioObj.content_type;
79
+ } else if (typeof data.audio_url === "string") audioUrl = data.audio_url;
80
+ if (!audioUrl) throw new Error("Audio URL not found in fal audio generation response");
81
+ const usage = buildFalUsage(takeBillableUnits(response.requestId));
82
+ return {
83
+ id: response.requestId || this.generateId(),
84
+ model: this.model,
85
+ audio: {
86
+ url: audioUrl,
87
+ contentType: deriveAudioContentType(contentType, audioUrl)
88
+ },
89
+ ...usage ? { usage } : {}
90
+ };
91
+ }
92
+ };
76
93
  function falAudio(model, config) {
77
- return new FalAudioAdapter(model, config);
94
+ return new FalAudioAdapter(model, config);
78
95
  }
79
- export {
80
- FalAudioAdapter,
81
- falAudio
82
- };
83
- //# sourceMappingURL=audio.js.map
96
+ //#endregion
97
+ export { FalAudioAdapter, falAudio };
98
+
99
+ //# sourceMappingURL=audio.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"audio.js","sources":["../../../src/adapters/audio.ts"],"sourcesContent":["import { fal } from '@fal-ai/client'\nimport { BaseAudioAdapter } from '@tanstack/ai/adapters'\nimport {\n configureFalClient,\n deriveAudioContentType,\n generateId as utilGenerateId,\n} from '../utils/client'\nimport { buildFalUsage, takeBillableUnits } from '../utils/billing'\nimport type { OutputType, Result } from '@fal-ai/client'\nimport type {\n AudioGenerationOptions,\n AudioGenerationResult,\n} from '@tanstack/ai'\nimport type { FalClientConfig } from '../utils/client'\nimport type { FalModel, FalModelInput } from '../model-meta'\n\n/**\n * Provider options for audio generation, excluding fields TanStack AI handles.\n */\nexport type FalAudioProviderOptions<TModel extends string> = Omit<\n FalModelInput<TModel>,\n 'prompt'\n>\n\n/**\n * Fal audio models don't agree on the name for \"how long should this be.\"\n * Most accept seconds via `duration`, but a few use model-specific fields —\n * e.g. ElevenLabs wants milliseconds via `music_length_ms`, Stable Audio wants\n * `seconds_total`. Explicit per-model entries override the default; user\n * `modelOptions` still win over either.\n */\nconst DURATION_FRAGMENT_BUILDERS: Record<\n string,\n (seconds: number) => Record<string, unknown>\n> = {\n 'fal-ai/elevenlabs/music': (seconds) => ({ music_length_ms: seconds * 1000 }),\n 'fal-ai/stable-audio-25/text-to-audio': (seconds) => ({\n seconds_total: seconds,\n }),\n}\n\nfunction buildDurationFragment(\n model: string,\n duration: number | undefined,\n): Record<string, unknown> {\n if (duration == null) return {}\n const builder = DURATION_FRAGMENT_BUILDERS[model]\n return builder ? builder(duration) : { duration }\n}\n\n/**\n * fal.ai audio generation adapter.\n *\n * Supports fal.ai audio models like diffrhythm (music), sound effects, etc.\n *\n * @example\n * ```typescript\n * const adapter = falAudio('fal-ai/diffrhythm')\n * const result = await generateAudio({\n * adapter,\n * prompt: 'An upbeat electronic track with synths',\n * duration: 10,\n * })\n * ```\n */\nexport class FalAudioAdapter<TModel extends FalModel> extends BaseAudioAdapter<\n TModel,\n FalAudioProviderOptions<TModel>\n> {\n readonly name = 'fal' as const\n\n constructor(model: TModel, config?: FalClientConfig) {\n super(model, {})\n configureFalClient(config)\n }\n\n async generateAudio(\n options: AudioGenerationOptions<FalAudioProviderOptions<TModel>>,\n ): Promise<AudioGenerationResult> {\n const { logger } = options\n logger.request(`activity=generateAudio provider=fal model=${this.model}`, {\n provider: 'fal',\n model: this.model,\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.generateAudio fatal', {\n error,\n source: 'fal.generateAudio',\n })\n throw error\n }\n }\n\n private buildInput(\n options: AudioGenerationOptions<FalAudioProviderOptions<TModel>>,\n ): FalModelInput<TModel> {\n return {\n ...buildDurationFragment(this.model, options.duration),\n ...options.modelOptions,\n prompt: options.prompt,\n } as FalModelInput<TModel>\n }\n\n protected override generateId(): string {\n return utilGenerateId(this.name)\n }\n\n private transformResponse(\n response: Result<OutputType<TModel>>,\n ): AudioGenerationResult {\n const data = response.data as Record<string, unknown>\n\n // fal returns { audio: { url, content_type } } or { audio_url: string }\n let audioUrl: string | undefined\n let contentType: string | undefined\n\n if (\n data['audio'] &&\n typeof data['audio'] === 'object' &&\n 'url' in data['audio']\n ) {\n const audioObj = data['audio'] as { url: string; content_type?: string }\n audioUrl = audioObj.url\n contentType = audioObj.content_type\n } else if (typeof data.audio_url === 'string') {\n audioUrl = data.audio_url\n }\n\n if (!audioUrl) {\n throw new Error('Audio URL not found in fal audio generation response')\n }\n\n const usage = buildFalUsage(takeBillableUnits(response.requestId))\n\n return {\n id: response.requestId || this.generateId(),\n model: this.model,\n audio: {\n url: audioUrl,\n contentType: deriveAudioContentType(contentType, audioUrl),\n },\n ...(usage ? { usage } : {}),\n }\n }\n}\n\nexport function falAudio<TModel extends FalModel>(\n model: TModel,\n config?: FalClientConfig,\n): FalAudioAdapter<TModel> {\n return new FalAudioAdapter(model, config)\n}\n"],"names":["utilGenerateId"],"mappings":";;;;AA+BA,MAAM,6BAGF;AAAA,EACF,2BAA2B,CAAC,aAAa,EAAE,iBAAiB,UAAU,IAAA;AAAA,EACtE,wCAAwC,CAAC,aAAa;AAAA,IACpD,eAAe;AAAA,EAAA;AAEnB;AAEA,SAAS,sBACP,OACA,UACyB;AACzB,MAAI,YAAY,KAAM,QAAO,CAAA;AAC7B,QAAM,UAAU,2BAA2B,KAAK;AAChD,SAAO,UAAU,QAAQ,QAAQ,IAAI,EAAE,SAAA;AACzC;AAiBO,MAAM,wBAAiD,iBAG5D;AAAA,EACS,OAAO;AAAA,EAEhB,YAAY,OAAe,QAA0B;AACnD,UAAM,OAAO,EAAE;AACf,uBAAmB,MAAM;AAAA,EAC3B;AAAA,EAEA,MAAM,cACJ,SACgC;AAChC,UAAM,EAAE,WAAW;AACnB,WAAO,QAAQ,6CAA6C,KAAK,KAAK,IAAI;AAAA,MACxE,UAAU;AAAA,MACV,OAAO,KAAK;AAAA,IAAA,CACb;AACD,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,SACuB;AACvB,WAAO;AAAA,MACL,GAAG,sBAAsB,KAAK,OAAO,QAAQ,QAAQ;AAAA,MACrD,GAAG,QAAQ;AAAA,MACX,QAAQ,QAAQ;AAAA,IAAA;AAAA,EAEpB;AAAA,EAEmB,aAAqB;AACtC,WAAOA,WAAe,KAAK,IAAI;AAAA,EACjC;AAAA,EAEQ,kBACN,UACuB;AACvB,UAAM,OAAO,SAAS;AAGtB,QAAI;AACJ,QAAI;AAEJ,QACE,KAAK,OAAO,KACZ,OAAO,KAAK,OAAO,MAAM,YACzB,SAAS,KAAK,OAAO,GACrB;AACA,YAAM,WAAW,KAAK,OAAO;AAC7B,iBAAW,SAAS;AACpB,oBAAc,SAAS;AAAA,IACzB,WAAW,OAAO,KAAK,cAAc,UAAU;AAC7C,iBAAW,KAAK;AAAA,IAClB;AAEA,QAAI,CAAC,UAAU;AACb,YAAM,IAAI,MAAM,sDAAsD;AAAA,IACxE;AAEA,UAAM,QAAQ,cAAc,kBAAkB,SAAS,SAAS,CAAC;AAEjE,WAAO;AAAA,MACL,IAAI,SAAS,aAAa,KAAK,WAAA;AAAA,MAC/B,OAAO,KAAK;AAAA,MACZ,OAAO;AAAA,QACL,KAAK;AAAA,QACL,aAAa,uBAAuB,aAAa,QAAQ;AAAA,MAAA;AAAA,MAE3D,GAAI,QAAQ,EAAE,UAAU,CAAA;AAAA,IAAC;AAAA,EAE7B;AACF;AAEO,SAAS,SACd,OACA,QACyB;AACzB,SAAO,IAAI,gBAAgB,OAAO,MAAM;AAC1C;"}
1
+ {"version":3,"file":"audio.js","names":[],"sources":["../../../src/adapters/audio.ts"],"sourcesContent":["import { fal } from '@fal-ai/client'\nimport { BaseAudioAdapter } from '@tanstack/ai/adapters'\nimport {\n configureFalClient,\n deriveAudioContentType,\n generateId as utilGenerateId,\n} from '../utils/client'\nimport { buildFalUsage, takeBillableUnits } from '../utils/billing'\nimport type { OutputType, Result } from '@fal-ai/client'\nimport type {\n AudioGenerationOptions,\n AudioGenerationResult,\n} from '@tanstack/ai'\nimport type { FalClientConfig } from '../utils/client'\nimport type { FalModel, FalModelInput } from '../model-meta'\n\n/**\n * Provider options for audio generation, excluding fields TanStack AI handles.\n */\nexport type FalAudioProviderOptions<TModel extends string> = Omit<\n FalModelInput<TModel>,\n 'prompt'\n>\n\n/**\n * Fal audio models don't agree on the name for \"how long should this be.\"\n * Most accept seconds via `duration`, but a few use model-specific fields —\n * e.g. ElevenLabs wants milliseconds via `music_length_ms`, Stable Audio wants\n * `seconds_total`. Explicit per-model entries override the default; user\n * `modelOptions` still win over either.\n */\nconst DURATION_FRAGMENT_BUILDERS: Record<\n string,\n (seconds: number) => Record<string, unknown>\n> = {\n 'fal-ai/elevenlabs/music': (seconds) => ({ music_length_ms: seconds * 1000 }),\n 'fal-ai/stable-audio-25/text-to-audio': (seconds) => ({\n seconds_total: seconds,\n }),\n}\n\nfunction buildDurationFragment(\n model: string,\n duration: number | undefined,\n): Record<string, unknown> {\n if (duration == null) return {}\n const builder = DURATION_FRAGMENT_BUILDERS[model]\n return builder ? builder(duration) : { duration }\n}\n\n/**\n * fal.ai audio generation adapter.\n *\n * Supports fal.ai audio models like diffrhythm (music), sound effects, etc.\n *\n * @example\n * ```typescript\n * const adapter = falAudio('fal-ai/diffrhythm')\n * const result = await generateAudio({\n * adapter,\n * prompt: 'An upbeat electronic track with synths',\n * duration: 10,\n * })\n * ```\n */\nexport class FalAudioAdapter<TModel extends FalModel> extends BaseAudioAdapter<\n TModel,\n FalAudioProviderOptions<TModel>\n> {\n readonly name = 'fal' as const\n\n constructor(model: TModel, config?: FalClientConfig) {\n super(model, {})\n configureFalClient(config)\n }\n\n async generateAudio(\n options: AudioGenerationOptions<FalAudioProviderOptions<TModel>>,\n ): Promise<AudioGenerationResult> {\n const { logger } = options\n logger.request(`activity=generateAudio provider=fal model=${this.model}`, {\n provider: 'fal',\n model: this.model,\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.generateAudio fatal', {\n error,\n source: 'fal.generateAudio',\n })\n throw error\n }\n }\n\n private buildInput(\n options: AudioGenerationOptions<FalAudioProviderOptions<TModel>>,\n ): FalModelInput<TModel> {\n return {\n ...buildDurationFragment(this.model, options.duration),\n ...options.modelOptions,\n prompt: options.prompt,\n } as FalModelInput<TModel>\n }\n\n protected override generateId(): string {\n return utilGenerateId(this.name)\n }\n\n private transformResponse(\n response: Result<OutputType<TModel>>,\n ): AudioGenerationResult {\n const data = response.data as Record<string, unknown>\n\n // fal returns { audio: { url, content_type } } or { audio_url: string }\n let audioUrl: string | undefined\n let contentType: string | undefined\n\n if (\n data['audio'] &&\n typeof data['audio'] === 'object' &&\n 'url' in data['audio']\n ) {\n const audioObj = data['audio'] as { url: string; content_type?: string }\n audioUrl = audioObj.url\n contentType = audioObj.content_type\n } else if (typeof data.audio_url === 'string') {\n audioUrl = data.audio_url\n }\n\n if (!audioUrl) {\n throw new Error('Audio URL not found in fal audio generation response')\n }\n\n const usage = buildFalUsage(takeBillableUnits(response.requestId))\n\n return {\n id: response.requestId || this.generateId(),\n model: this.model,\n audio: {\n url: audioUrl,\n contentType: deriveAudioContentType(contentType, audioUrl),\n },\n ...(usage ? { usage } : {}),\n }\n }\n}\n\nexport function falAudio<TModel extends FalModel>(\n model: TModel,\n config?: FalClientConfig,\n): FalAudioAdapter<TModel> {\n return new FalAudioAdapter(model, config)\n}\n"],"mappings":";;;;;;;;;;;;AA+BA,IAAM,6BAGF;CACF,4BAA4B,aAAa,EAAE,iBAAiB,UAAU,IAAK;CAC3E,yCAAyC,aAAa,EACpD,eAAe,QACjB;AACF;AAEA,SAAS,sBACP,OACA,UACyB;CACzB,IAAI,YAAY,MAAM,OAAO,CAAC;CAC9B,MAAM,UAAU,2BAA2B;CAC3C,OAAO,UAAU,QAAQ,QAAQ,IAAI,EAAE,SAAS;AAClD;;;;;;;;;;;;;;;;AAiBA,IAAa,kBAAb,cAA8D,iBAG5D;CACA,OAAgB;CAEhB,YAAY,OAAe,QAA0B;EACnD,MAAM,OAAO,CAAC,CAAC;EACf,mBAAmB,MAAM;CAC3B;CAEA,MAAM,cACJ,SACgC;EAChC,MAAM,EAAE,WAAW;EACnB,OAAO,QAAQ,6CAA6C,KAAK,SAAS;GACxE,UAAU;GACV,OAAO,KAAK;EACd,CAAC;EACD,IAAI;GACF,MAAM,QAAQ,KAAK,WAAW,OAAO;GACrC,MAAM,SAAS,MAAM,IAAI,UAAU,KAAK,OAAO,EAAE,MAAM,CAAC;GACxD,OAAO,KAAK,kBAAkB,MAAM;EACtC,SAAS,OAAO;GACd,OAAO,OAAO,2BAA2B;IACvC;IACA,QAAQ;GACV,CAAC;GACD,MAAM;EACR;CACF;CAEA,WACE,SACuB;EACvB,OAAO;GACL,GAAG,sBAAsB,KAAK,OAAO,QAAQ,QAAQ;GACrD,GAAG,QAAQ;GACX,QAAQ,QAAQ;EAClB;CACF;CAEA,aAAwC;EACtC,OAAO,WAAe,KAAK,IAAI;CACjC;CAEA,kBACE,UACuB;EACvB,MAAM,OAAO,SAAS;EAGtB,IAAI;EACJ,IAAI;EAEJ,IACE,KAAK,YACL,OAAO,KAAK,aAAa,YACzB,SAAS,KAAK,UACd;GACA,MAAM,WAAW,KAAK;GACtB,WAAW,SAAS;GACpB,cAAc,SAAS;EACzB,OAAO,IAAI,OAAO,KAAK,cAAc,UACnC,WAAW,KAAK;EAGlB,IAAI,CAAC,UACH,MAAM,IAAI,MAAM,sDAAsD;EAGxE,MAAM,QAAQ,cAAc,kBAAkB,SAAS,SAAS,CAAC;EAEjE,OAAO;GACL,IAAI,SAAS,aAAa,KAAK,WAAW;GAC1C,OAAO,KAAK;GACZ,OAAO;IACL,KAAK;IACL,aAAa,uBAAuB,aAAa,QAAQ;GAC3D;GACA,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;EAC3B;CACF;AACF;AAEA,SAAgB,SACd,OACA,QACyB;CACzB,OAAO,IAAI,gBAAgB,OAAO,MAAM;AAC1C"}
@@ -1,115 +1,106 @@
1
- import { fal } from "@fal-ai/client";
2
- import { resolveMediaPrompt } from "@tanstack/ai";
3
- import { BaseImageAdapter } from "@tanstack/ai/adapters";
4
- import { configureFalClient, generateId } from "../utils/client.js";
5
1
  import { buildFalUsage, takeBillableUnits } from "../utils/billing.js";
2
+ import { configureFalClient, generateId } from "../utils/client.js";
6
3
  import { mapSizeToFalFormat } from "../image/image-provider-options.js";
7
4
  import { mapImageInputsToFalFields } from "../image/image-inputs.js";
8
- class FalImageAdapter extends BaseImageAdapter {
9
- kind = "image";
10
- name = "fal";
11
- constructor(model, config) {
12
- super(model, {});
13
- configureFalClient(config);
14
- }
15
- async generateImages(options) {
16
- const { logger } = options;
17
- logger.request(`activity=generateImage provider=fal model=${this.model}`, {
18
- provider: "fal",
19
- model: this.model
20
- });
21
- const resolved = resolveMediaPrompt(options.prompt);
22
- if (resolved.videos.length > 0) {
23
- throw new Error(
24
- `fal.generateImages does not support video prompt parts on model ${this.model}.`
25
- );
26
- }
27
- if (resolved.audios.length > 0) {
28
- throw new Error(
29
- `fal.generateImages does not support audio prompt parts on model ${this.model}.`
30
- );
31
- }
32
- try {
33
- const input = this.buildInput(options, resolved);
34
- const result = await fal.subscribe(this.model, { input });
35
- return this.transformResponse(result);
36
- } catch (error) {
37
- logger.errors("fal.generateImage fatal", {
38
- error,
39
- source: "fal.generateImage"
40
- });
41
- throw error;
42
- }
43
- }
44
- buildInput(options, resolved) {
45
- const sizeParams = mapSizeToFalFormat(options.size);
46
- const inputFields = mapImageInputsToFalFields(this.model, resolved.images);
47
- const input = {
48
- ...sizeParams,
49
- ...inputFields,
50
- ...options.modelOptions,
51
- // Media-only prompts (e.g. upscalers, background removal) omit the
52
- // prompt field entirely rather than sending an empty string.
53
- ...resolved.text ? { prompt: resolved.text } : {},
54
- num_images: options.numberOfImages
55
- };
56
- return input;
57
- }
58
- generateId() {
59
- return generateId(this.name);
60
- }
61
- transformResponse(response) {
62
- const images = [];
63
- const data = response.data;
64
- if ("images" in data && Array.isArray(data.images)) {
65
- for (const img of data.images) {
66
- images.push(this.parseImage(img));
67
- }
68
- } else if ("image" in data && data.image && typeof data.image === "object") {
69
- images.push(this.parseImage(data.image));
70
- }
71
- if (images.length === 0) {
72
- throw new Error(
73
- "Unexpected fal image response shape. Expected images[] or image{}. Got keys: " + Object.keys(data).join(",")
74
- );
75
- }
76
- const usage = buildFalUsage(takeBillableUnits(response.requestId));
77
- return {
78
- id: response.requestId || this.generateId(),
79
- model: this.model,
80
- images,
81
- ...usage ? { usage } : {}
82
- };
83
- }
84
- parseImage(img) {
85
- let url;
86
- if (typeof img === "string") {
87
- url = img;
88
- } else if (img && typeof img === "object" && "url" in img && typeof img.url === "string") {
89
- url = img.url;
90
- } else {
91
- throw new Error(
92
- `Invalid image payload from fal response: expected string or { url: string }, received ${img === null ? "null" : typeof img}`
93
- );
94
- }
95
- if (url.startsWith("data:")) {
96
- const base64Match = url.match(/^data:image\/[^;]+;base64,(.+)$/);
97
- if (base64Match && base64Match[1]) {
98
- return { b64Json: base64Match[1] };
99
- }
100
- }
101
- return { url };
102
- }
103
- }
5
+ import { fal } from "@fal-ai/client";
6
+ import { resolveMediaPrompt } from "@tanstack/ai";
7
+ import { BaseImageAdapter } from "@tanstack/ai/adapters";
8
+ //#region src/adapters/image.ts
9
+ /**
10
+ * fal.ai image generation adapter with full type inference.
11
+ *
12
+ * Uses fal.ai's comprehensive type system to provide autocomplete
13
+ *
14
+ * and type safety for all 600+ supported models.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * const adapter = falImage('fal-ai/flux/dev')
19
+ * const result = await adapter.generateImages({
20
+ * model: 'fal-ai/flux/dev',
21
+ * prompt: 'a cat',
22
+ * modelOptions: {
23
+ * num_inference_steps: 28, // Type-safe! Autocomplete works
24
+ * guidance_scale: 3.5,
25
+ * },
26
+ * })
27
+ * ```
28
+ */
29
+ var FalImageAdapter = class extends BaseImageAdapter {
30
+ kind = "image";
31
+ name = "fal";
32
+ constructor(model, config) {
33
+ super(model, {});
34
+ configureFalClient(config);
35
+ }
36
+ async generateImages(options) {
37
+ const { logger } = options;
38
+ logger.request(`activity=generateImage provider=fal model=${this.model}`, {
39
+ provider: "fal",
40
+ model: this.model
41
+ });
42
+ const resolved = resolveMediaPrompt(options.prompt);
43
+ if (resolved.videos.length > 0) throw new Error(`fal.generateImages does not support video prompt parts on model ${this.model}.`);
44
+ if (resolved.audios.length > 0) throw new Error(`fal.generateImages does not support audio prompt parts on model ${this.model}.`);
45
+ try {
46
+ const input = this.buildInput(options, resolved);
47
+ const result = await fal.subscribe(this.model, { input });
48
+ return this.transformResponse(result);
49
+ } catch (error) {
50
+ logger.errors("fal.generateImage fatal", {
51
+ error,
52
+ source: "fal.generateImage"
53
+ });
54
+ throw error;
55
+ }
56
+ }
57
+ buildInput(options, resolved) {
58
+ const sizeParams = mapSizeToFalFormat(options.size);
59
+ const inputFields = mapImageInputsToFalFields(this.model, resolved.images);
60
+ return {
61
+ ...sizeParams,
62
+ ...inputFields,
63
+ ...options.modelOptions,
64
+ ...resolved.text ? { prompt: resolved.text } : {},
65
+ num_images: options.numberOfImages
66
+ };
67
+ }
68
+ generateId() {
69
+ return generateId(this.name);
70
+ }
71
+ transformResponse(response) {
72
+ const images = [];
73
+ const data = response.data;
74
+ if ("images" in data && Array.isArray(data.images)) for (const img of data.images) images.push(this.parseImage(img));
75
+ else if ("image" in data && data.image && typeof data.image === "object") images.push(this.parseImage(data.image));
76
+ if (images.length === 0) throw new Error("Unexpected fal image response shape. Expected images[] or image{}. Got keys: " + Object.keys(data).join(","));
77
+ const usage = buildFalUsage(takeBillableUnits(response.requestId));
78
+ return {
79
+ id: response.requestId || this.generateId(),
80
+ model: this.model,
81
+ images,
82
+ ...usage ? { usage } : {}
83
+ };
84
+ }
85
+ parseImage(img) {
86
+ let url;
87
+ if (typeof img === "string") url = img;
88
+ else if (img && typeof img === "object" && "url" in img && typeof img.url === "string") url = img.url;
89
+ else throw new Error(`Invalid image payload from fal response: expected string or { url: string }, received ${img === null ? "null" : typeof img}`);
90
+ if (url.startsWith("data:")) {
91
+ const base64Match = url.match(/^data:image\/[^;]+;base64,(.+)$/);
92
+ if (base64Match && base64Match[1]) return { b64Json: base64Match[1] };
93
+ }
94
+ return { url };
95
+ }
96
+ };
104
97
  function createFalImage(model, config) {
105
- return new FalImageAdapter(model, config);
98
+ return new FalImageAdapter(model, config);
106
99
  }
107
100
  function falImage(model, config) {
108
- return createFalImage(model, config);
101
+ return createFalImage(model, config);
109
102
  }
110
- export {
111
- FalImageAdapter,
112
- createFalImage,
113
- falImage
114
- };
115
- //# sourceMappingURL=image.js.map
103
+ //#endregion
104
+ export { FalImageAdapter, createFalImage, falImage };
105
+
106
+ //# sourceMappingURL=image.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"image.js","sources":["../../../src/adapters/image.ts"],"sourcesContent":["import { fal } from '@fal-ai/client'\nimport { resolveMediaPrompt } from '@tanstack/ai'\nimport { BaseImageAdapter } from '@tanstack/ai/adapters'\nimport {\n configureFalClient,\n generateId as utilGenerateId,\n} from '../utils/client'\nimport { buildFalUsage, takeBillableUnits } from '../utils/billing'\nimport { mapSizeToFalFormat } from '../image/image-provider-options'\nimport { mapImageInputsToFalFields } from '../image/image-inputs'\nimport type { OutputType, Result } from '@fal-ai/client'\nimport type { FalClientConfig } from '../utils/client'\nimport type {\n GeneratedImage,\n ImageGenerationOptions,\n ImageGenerationResult,\n ResolvedMediaPrompt,\n} from '@tanstack/ai'\nimport type {\n FalImagePromptModalitiesFor,\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 Record<TModel, FalImagePromptModalitiesFor<TModel>>\n> {\n override 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 const resolved = resolveMediaPrompt(options.prompt)\n\n if (resolved.videos.length > 0) {\n throw new Error(\n `fal.generateImages does not support video prompt parts on model ${this.model}.`,\n )\n }\n if (resolved.audios.length > 0) {\n throw new Error(\n `fal.generateImages does not support audio prompt parts on model ${this.model}.`,\n )\n }\n\n try {\n const input = this.buildInput(options, resolved)\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 resolved: ResolvedMediaPrompt,\n ): FalModelInput<TModel> {\n const sizeParams = mapSizeToFalFormat(options.size)\n // Order matters: size and derived image-input fields first, then\n // modelOptions (so explicit user overrides win for mask_url /\n // control_image_url / reference_image_urls), then the call-controlled\n // prompt / num_images, which always take precedence.\n const inputFields = mapImageInputsToFalFields(this.model, resolved.images)\n const input = {\n ...sizeParams,\n ...inputFields,\n ...options.modelOptions,\n // Media-only prompts (e.g. upscalers, background removal) omit the\n // prompt field entirely rather than sending an empty string.\n ...(resolved.text ? { prompt: resolved.text } : {}),\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 const usage = buildFalUsage(takeBillableUnits(response.requestId))\n\n return {\n id: response.requestId || this.generateId(),\n model: this.model,\n images,\n ...(usage ? { usage } : {}),\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.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":"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"}
1
+ {"version":3,"file":"image.js","names":[],"sources":["../../../src/adapters/image.ts"],"sourcesContent":["import { fal } from '@fal-ai/client'\nimport { resolveMediaPrompt } from '@tanstack/ai'\nimport { BaseImageAdapter } from '@tanstack/ai/adapters'\nimport {\n configureFalClient,\n generateId as utilGenerateId,\n} from '../utils/client'\nimport { buildFalUsage, takeBillableUnits } from '../utils/billing'\nimport { mapSizeToFalFormat } from '../image/image-provider-options'\nimport { mapImageInputsToFalFields } from '../image/image-inputs'\nimport type { OutputType, Result } from '@fal-ai/client'\nimport type { FalClientConfig } from '../utils/client'\nimport type {\n GeneratedImage,\n ImageGenerationOptions,\n ImageGenerationResult,\n ResolvedMediaPrompt,\n} from '@tanstack/ai'\nimport type {\n FalImagePromptModalitiesFor,\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 Record<TModel, FalImagePromptModalitiesFor<TModel>>\n> {\n override 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 const resolved = resolveMediaPrompt(options.prompt)\n\n if (resolved.videos.length > 0) {\n throw new Error(\n `fal.generateImages does not support video prompt parts on model ${this.model}.`,\n )\n }\n if (resolved.audios.length > 0) {\n throw new Error(\n `fal.generateImages does not support audio prompt parts on model ${this.model}.`,\n )\n }\n\n try {\n const input = this.buildInput(options, resolved)\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 resolved: ResolvedMediaPrompt,\n ): FalModelInput<TModel> {\n const sizeParams = mapSizeToFalFormat(options.size)\n // Order matters: size and derived image-input fields first, then\n // modelOptions (so explicit user overrides win for mask_url /\n // control_image_url / reference_image_urls), then the call-controlled\n // prompt / num_images, which always take precedence.\n const inputFields = mapImageInputsToFalFields(this.model, resolved.images)\n const input = {\n ...sizeParams,\n ...inputFields,\n ...options.modelOptions,\n // Media-only prompts (e.g. upscalers, background removal) omit the\n // prompt field entirely rather than sending an empty string.\n ...(resolved.text ? { prompt: resolved.text } : {}),\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 const usage = buildFalUsage(takeBillableUnits(response.requestId))\n\n return {\n id: response.requestId || this.generateId(),\n model: this.model,\n images,\n ...(usage ? { usage } : {}),\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.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"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8CA,IAAa,kBAAb,cAA8D,iBAM5D;CACA,OAAyB;CACzB,OAAgB;CAEhB,YAAY,OAAe,QAA0B;EACnD,MAAM,OAAO,CAAC,CAAC;EACf,mBAAmB,MAAM;CAC3B;CAEA,MAAM,eACJ,SAIgC;EAChC,MAAM,EAAE,WAAW;EAEnB,OAAO,QAAQ,6CAA6C,KAAK,SAAS;GACxE,UAAU;GACV,OAAO,KAAK;EACd,CAAC;EAED,MAAM,WAAW,mBAAmB,QAAQ,MAAM;EAElD,IAAI,SAAS,OAAO,SAAS,GAC3B,MAAM,IAAI,MACR,mEAAmE,KAAK,MAAM,EAChF;EAEF,IAAI,SAAS,OAAO,SAAS,GAC3B,MAAM,IAAI,MACR,mEAAmE,KAAK,MAAM,EAChF;EAGF,IAAI;GACF,MAAM,QAAQ,KAAK,WAAW,SAAS,QAAQ;GAC/C,MAAM,SAAS,MAAM,IAAI,UAAU,KAAK,OAAO,EAAE,MAAM,CAAC;GACxD,OAAO,KAAK,kBAAkB,MAAM;EACtC,SAAS,OAAO;GACd,OAAO,OAAO,2BAA2B;IACvC;IACA,QAAQ;GACV,CAAC;GACD,MAAM;EACR;CACF;CAEA,WACE,SAIA,UACuB;EACvB,MAAM,aAAa,mBAAmB,QAAQ,IAAI;EAKlD,MAAM,cAAc,0BAA0B,KAAK,OAAO,SAAS,MAAM;EAUzE,OAAO;GARL,GAAG;GACH,GAAG;GACH,GAAG,QAAQ;GAGX,GAAI,SAAS,OAAO,EAAE,QAAQ,SAAS,KAAK,IAAI,CAAC;GACjD,YAAY,QAAQ;EAEf;CACT;CAEA,aAAwC;EACtC,OAAO,WAAe,KAAK,IAAI;CACjC;CAEA,kBACE,UACuB;EACvB,MAAM,SAAgC,CAAC;EACvC,MAAM,OAAO,SAAS;EAGtB,IAAI,YAAY,QAAQ,MAAM,QAAQ,KAAK,MAAM,GAC/C,KAAK,MAAM,OAAO,KAAK,QACrB,OAAO,KAAK,KAAK,WAAW,GAAG,CAAC;OAI/B,IAAI,WAAW,QAAQ,KAAK,SAAS,OAAO,KAAK,UAAU,UAC9D,OAAO,KAAK,KAAK,WAAW,KAAK,KAAK,CAAC;EAGzC,IAAI,OAAO,WAAW,GACpB,MAAM,IAAI,MACR,kFACE,OAAO,KAAK,IAAI,CAAC,CAAC,KAAK,GAAG,CAC9B;EAGF,MAAM,QAAQ,cAAc,kBAAkB,SAAS,SAAS,CAAC;EAEjE,OAAO;GACL,IAAI,SAAS,aAAa,KAAK,WAAW;GAC1C,OAAO,KAAK;GACZ;GACA,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC;EAC3B;CACF;CAEA,WAAmB,KAA8B;EAC/C,IAAI;EACJ,IAAI,OAAO,QAAQ,UACjB,MAAM;OACD,IACL,OACA,OAAO,QAAQ,YACf,SAAS,OACT,OAAO,IAAI,QAAQ,UAEnB,MAAO,IAAwB;OAE/B,MAAM,IAAI,MACR,yFACE,QAAQ,OAAO,SAAS,OAAO,KAEnC;EAGF,IAAI,IAAI,WAAW,OAAO,GAAG;GAC3B,MAAM,cAAc,IAAI,MAAM,iCAAiC;GAC/D,IAAI,eAAe,YAAY,IAC7B,OAAO,EAAE,SAAS,YAAY,GAAG;EAErC;EACA,OAAO,EAAE,IAAI;CACf;AACF;AAEA,SAAgB,eACd,OACA,QACyB;CACzB,OAAO,IAAI,gBAAgB,OAAO,MAAM;AAC1C;AAEA,SAAgB,SACd,OACA,QACyB;CACzB,OAAO,eAAe,OAAO,MAAM;AACrC"}