@howells/motif-sdk 0.4.0 → 0.5.1

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.js CHANGED
@@ -22,62 +22,80 @@ var ASPECT_RATIOS = [
22
22
  var RESOLUTIONS = ["0.5K", "1K", "2K", "4K"];
23
23
  function aspectToGptSize(aspect) {
24
24
  switch (aspect) {
25
- case "auto":
25
+ case "auto": {
26
26
  return "auto";
27
+ }
27
28
  case "9:16":
28
29
  case "1:8":
29
30
  case "1:4":
30
31
  case "2:3":
31
32
  case "4:5":
32
- case "3:4":
33
+ case "3:4": {
33
34
  return "1024x1536";
35
+ }
34
36
  case "16:9":
35
37
  case "8:1":
36
38
  case "4:1":
37
39
  case "3:2":
38
40
  case "5:4":
39
41
  case "4:3":
40
- case "21:9":
42
+ case "21:9": {
41
43
  return "1536x1024";
42
- default:
44
+ }
45
+ case "1:1": {
43
46
  return "1024x1024";
47
+ }
48
+ default: {
49
+ return "1024x1024";
50
+ }
44
51
  }
45
52
  }
46
53
  function aspectToFalImageSize(aspect) {
47
54
  switch (aspect) {
48
- case "auto":
55
+ case "auto": {
49
56
  return "auto";
57
+ }
50
58
  case "16:9":
51
59
  case "21:9":
52
60
  case "4:1":
53
- case "8:1":
61
+ case "8:1": {
54
62
  return "landscape_16_9";
63
+ }
55
64
  case "3:2":
56
- case "5:4":
65
+ case "5:4": {
57
66
  return "landscape_4_3";
58
- case "4:3":
67
+ }
68
+ case "4:3": {
59
69
  return "landscape_4_3";
70
+ }
60
71
  case "9:16":
61
72
  case "1:4":
62
- case "1:8":
73
+ case "1:8": {
63
74
  return "portrait_16_9";
75
+ }
64
76
  case "2:3":
65
- case "4:5":
77
+ case "4:5": {
66
78
  return "portrait_4_3";
67
- case "3:4":
79
+ }
80
+ case "3:4": {
68
81
  return "portrait_4_3";
69
- default:
82
+ }
83
+ case "1:1": {
84
+ return "square_hd";
85
+ }
86
+ default: {
70
87
  return "square_hd";
88
+ }
71
89
  }
72
90
  }
73
91
  var FORMAT_PRESETS = {
74
- cover: { label: "Cover", aspect: "2:3", resolution: "2K" },
75
- square: { label: "Square", aspect: "1:1" },
76
- landscape: { label: "Landscape", aspect: "16:9" },
77
- portrait: { label: "Portrait", aspect: "2:3" },
78
- story: { label: "Story", aspect: "9:16" },
79
- og: { label: "OG Image", aspect: "16:9" },
80
- wide: { label: "Wide", aspect: "21:9" }
92
+ cover: { aspect: "2:3", label: "Cover", resolution: "2K" },
93
+ landscape: { aspect: "16:9", label: "Landscape" },
94
+ og: { aspect: "16:9", label: "OG Image" },
95
+ portrait: { aspect: "2:3", label: "Portrait" },
96
+ square: { aspect: "1:1", label: "Square" },
97
+ story: { aspect: "9:16", label: "Story" },
98
+ wide: { aspect: "21:9", label: "Wide" }
81
99
  };
82
100
 
83
101
  // src/models.ts
@@ -87,305 +105,332 @@ var AA_IMAGE_SOURCES = [
87
105
  "https://artificialanalysis.ai/image/leaderboard/editing"
88
106
  ];
89
107
  var FAL_PRICING_CHECKED_AT = "2026-05-12";
108
+ var FAL_PRICING_CHECKED_JUL_2026 = "2026-07-11";
90
109
  var MODELS = {
91
110
  // ─── Generation Models ────────────────────────────────────────
92
111
  gpt2: {
93
- name: "GPT Image 2",
94
- endpoint: "openai/gpt-image-2",
95
- editEndpoint: "openai/gpt-image-2/image-to-image",
96
- type: "generation",
97
- pricing: "$0.211",
98
- pricePerImageUsd: 0.211,
99
- falPricing: {
100
- endpointId: "openai/gpt-image-2",
101
- unitPrice: 1,
102
- unit: "units",
103
- currency: "USD",
104
- estimatedCostPerImageUsd: 0.211,
105
- source: "fal-pricing-api",
106
- checkedAt: FAL_PRICING_CHECKED_AT
107
- },
108
112
  benchmark: {
109
- useCase: "Highest-ranked text-to-image quality and transparent PNGs",
110
- tiers: { quality: "frontier", speed: "very_slow", price: "ultra" },
113
+ artificialAnalysis: {
114
+ editing: { elo: 1249, pricePer1k: 211, rank: 3, winRate: 0.66 },
115
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
116
+ sourceUrls: AA_IMAGE_SOURCES,
117
+ textToImage: { elo: 1337, pricePer1k: 211, rank: 1, winRate: 0.8 }
118
+ },
111
119
  speed: {
112
120
  medianSeconds: 200.7,
113
121
  p95Seconds: 232.6,
114
122
  source: "artificial-analysis-models"
115
123
  },
116
- artificialAnalysis: {
117
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
118
- sourceUrls: AA_IMAGE_SOURCES,
119
- textToImage: { rank: 1, elo: 1337, winRate: 0.8, pricePer1k: 211 },
120
- editing: { rank: 3, elo: 1249, winRate: 0.66, pricePer1k: 211 }
121
- }
124
+ tiers: { price: "ultra", quality: "frontier", speed: "very_slow" },
125
+ useCase: "Highest-ranked text-to-image quality and transparent PNGs"
126
+ },
127
+ editEndpoint: "openai/gpt-image-2/image-to-image",
128
+ endpoint: "openai/gpt-image-2",
129
+ falPricing: {
130
+ checkedAt: FAL_PRICING_CHECKED_AT,
131
+ currency: "USD",
132
+ endpointId: "openai/gpt-image-2",
133
+ estimatedCostPerImageUsd: 0.211,
134
+ source: "fal-pricing-api",
135
+ unit: "units",
136
+ unitPrice: 1
122
137
  },
138
+ maxReferenceImages: 4,
139
+ name: "GPT Image 2",
140
+ pricePerImageUsd: 0.211,
141
+ pricing: "$0.211",
123
142
  sizeMode: "image_size_enum",
124
143
  supportsAspect: true,
125
- supportsResolution: false,
126
144
  supportsEdit: true,
145
+ supportsMaskImage: true,
127
146
  supportsNumImages: true,
128
147
  supportsOutputFormat: true,
129
148
  supportsQuality: true,
130
- supportsMaskImage: true,
149
+ supportsResolution: false,
131
150
  supportsSyncMode: true,
132
- maxReferenceImages: 4,
151
+ type: "generation",
133
152
  useQueue: true
134
153
  },
135
154
  gpt: {
136
- name: "GPT Image 1.5",
137
- endpoint: "fal-ai/gpt-image-1.5",
138
- editEndpoint: "fal-ai/gpt-image-1.5/edit",
139
- type: "generation",
140
- pricing: "$0.133",
141
- pricePerImageUsd: 0.133,
142
- falPricing: {
143
- endpointId: "fal-ai/gpt-image-1.5",
144
- unitPrice: 1,
145
- unit: "units",
146
- currency: "USD",
147
- estimatedCostPerImageUsd: 0.133,
148
- source: "fal-pricing-api",
149
- checkedAt: FAL_PRICING_CHECKED_AT
150
- },
151
155
  benchmark: {
152
- useCase: "Best OpenAI edit quality with transparent PNG support",
153
- tiers: { quality: "frontier", speed: "slow", price: "ultra" },
156
+ artificialAnalysis: {
157
+ editing: { elo: 1262, pricePer1k: 133, rank: 2, winRate: 0.69 },
158
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
159
+ sourceUrls: AA_IMAGE_SOURCES,
160
+ textToImage: { elo: 1268, pricePer1k: 133, rank: 2, winRate: 0.73 }
161
+ },
154
162
  speed: {
155
163
  medianSeconds: 34.6,
156
164
  p95Seconds: 38.3,
157
165
  source: "artificial-analysis-models"
158
166
  },
159
- artificialAnalysis: {
160
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
161
- sourceUrls: AA_IMAGE_SOURCES,
162
- textToImage: { rank: 2, elo: 1268, winRate: 0.73, pricePer1k: 133 },
163
- editing: { rank: 2, elo: 1262, winRate: 0.69, pricePer1k: 133 }
164
- }
167
+ tiers: { price: "ultra", quality: "frontier", speed: "slow" },
168
+ useCase: "Best OpenAI edit quality with transparent PNG support"
169
+ },
170
+ editEndpoint: "fal-ai/gpt-image-1.5/edit",
171
+ endpoint: "fal-ai/gpt-image-1.5",
172
+ falPricing: {
173
+ checkedAt: FAL_PRICING_CHECKED_AT,
174
+ currency: "USD",
175
+ endpointId: "fal-ai/gpt-image-1.5",
176
+ estimatedCostPerImageUsd: 0.133,
177
+ source: "fal-pricing-api",
178
+ unit: "units",
179
+ unitPrice: 1
165
180
  },
181
+ maxReferenceImages: 4,
182
+ name: "GPT Image 1.5",
183
+ pricePerImageUsd: 0.133,
184
+ pricing: "$0.133",
166
185
  sizeMode: "gpt_size",
167
186
  supportsAspect: false,
168
187
  supportsBackground: true,
169
- supportsResolution: false,
170
188
  supportsEdit: true,
189
+ supportsMaskImage: true,
171
190
  supportsNumImages: true,
172
191
  supportsOutputFormat: true,
173
192
  supportsQuality: true,
174
- supportsMaskImage: true,
193
+ supportsResolution: false,
175
194
  supportsSyncMode: true,
176
- maxReferenceImages: 4
195
+ type: "generation"
177
196
  },
178
197
  banana2: {
179
- name: "Nano Banana 2",
180
- endpoint: "fal-ai/nano-banana-2",
198
+ benchmark: {
199
+ artificialAnalysis: {
200
+ editing: { elo: 1231, pricePer1k: 67, rank: 5, winRate: 0.63 },
201
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
202
+ sourceUrls: AA_IMAGE_SOURCES,
203
+ textToImage: { elo: 1263, pricePer1k: 67, rank: 3, winRate: 0.71 }
204
+ },
205
+ tiers: { price: "premium", quality: "frontier", speed: "unknown" },
206
+ useCase: "Best balance of quality, edit support, web search, and cost"
207
+ },
181
208
  editEndpoint: "fal-ai/nano-banana-2/edit",
182
- type: "generation",
183
- pricing: "$0.08",
184
- pricePerImageUsd: 0.08,
209
+ endpoint: "fal-ai/nano-banana-2",
185
210
  falPricing: {
186
- endpointId: "fal-ai/nano-banana-2",
187
- unitPrice: 0.08,
188
- unit: "images",
211
+ checkedAt: FAL_PRICING_CHECKED_AT,
189
212
  currency: "USD",
213
+ endpointId: "fal-ai/nano-banana-2",
190
214
  estimatedCostPerImageUsd: 0.08,
191
215
  source: "fal-pricing-api",
192
- checkedAt: FAL_PRICING_CHECKED_AT
193
- },
194
- benchmark: {
195
- useCase: "Best balance of quality, edit support, web search, and cost",
196
- tiers: { quality: "frontier", speed: "unknown", price: "premium" },
197
- artificialAnalysis: {
198
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
199
- sourceUrls: AA_IMAGE_SOURCES,
200
- textToImage: { rank: 3, elo: 1263, winRate: 0.71, pricePer1k: 67 },
201
- editing: { rank: 5, elo: 1231, winRate: 0.63, pricePer1k: 67 }
202
- }
216
+ unit: "images",
217
+ unitPrice: 0.08
203
218
  },
219
+ maxReferenceImages: 4,
220
+ name: "Nano Banana 2",
221
+ pricePerImageUsd: 0.08,
222
+ pricing: "$0.08",
204
223
  sizeMode: "aspect_ratio",
205
224
  supportsAspect: true,
206
- supportsResolution: true,
207
225
  supportsEdit: true,
226
+ supportsGoogleSearch: true,
227
+ supportsLimitGenerations: true,
208
228
  supportsNumImages: true,
209
229
  supportsOutputFormat: true,
230
+ supportsResolution: true,
210
231
  supportsSafetyTolerance: true,
211
232
  supportsSeed: true,
212
- supportsWebSearch: true,
213
- supportsGoogleSearch: true,
214
- supportsLimitGenerations: true,
215
233
  supportsSyncMode: true,
216
234
  supportsThinkingLevel: true,
217
- maxReferenceImages: 4
235
+ supportsWebSearch: true,
236
+ type: "generation"
218
237
  },
219
238
  banana: {
220
- name: "Nano Banana Pro",
221
- endpoint: "fal-ai/nano-banana-pro",
222
- editEndpoint: "fal-ai/nano-banana-pro/edit",
223
- type: "generation",
224
- pricing: "$0.15",
225
- pricePerImageUsd: 0.15,
226
- falPricing: {
227
- endpointId: "fal-ai/nano-banana-pro",
228
- unitPrice: 0.15,
229
- unit: "images",
230
- currency: "USD",
231
- estimatedCostPerImageUsd: 0.15,
232
- source: "fal-pricing-api",
233
- checkedAt: FAL_PRICING_CHECKED_AT
234
- },
235
239
  benchmark: {
236
- useCase: "Premium Gemini image generation and multi-reference edits",
237
- tiers: { quality: "frontier", speed: "balanced", price: "ultra" },
240
+ artificialAnalysis: {
241
+ editing: { elo: 1241, pricePer1k: 134, rank: 4, winRate: 0.65 },
242
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
243
+ sourceUrls: AA_IMAGE_SOURCES,
244
+ textToImage: { elo: 1220, pricePer1k: 134, rank: 5, winRate: 0.66 }
245
+ },
238
246
  speed: {
239
247
  medianSeconds: 19.2,
240
248
  p95Seconds: 30.3,
241
249
  source: "artificial-analysis-models"
242
250
  },
243
- artificialAnalysis: {
244
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
245
- sourceUrls: AA_IMAGE_SOURCES,
246
- textToImage: { rank: 5, elo: 1220, winRate: 0.66, pricePer1k: 134 },
247
- editing: { rank: 4, elo: 1241, winRate: 0.65, pricePer1k: 134 }
248
- }
251
+ tiers: { price: "ultra", quality: "frontier", speed: "balanced" },
252
+ useCase: "Premium Gemini image generation and multi-reference edits"
253
+ },
254
+ editEndpoint: "fal-ai/nano-banana-pro/edit",
255
+ endpoint: "fal-ai/nano-banana-pro",
256
+ falPricing: {
257
+ checkedAt: FAL_PRICING_CHECKED_AT,
258
+ currency: "USD",
259
+ endpointId: "fal-ai/nano-banana-pro",
260
+ estimatedCostPerImageUsd: 0.15,
261
+ source: "fal-pricing-api",
262
+ unit: "images",
263
+ unitPrice: 0.15
249
264
  },
265
+ maxReferenceImages: 14,
266
+ name: "Nano Banana Pro",
267
+ pricePerImageUsd: 0.15,
268
+ pricing: "$0.15",
250
269
  sizeMode: "aspect_ratio",
251
270
  supportsAspect: true,
252
- supportsResolution: true,
253
271
  supportsEdit: true,
272
+ supportsGoogleSearch: true,
273
+ supportsLimitGenerations: true,
254
274
  supportsNumImages: true,
255
275
  supportsOutputFormat: true,
276
+ supportsResolution: true,
256
277
  supportsSafetyTolerance: true,
257
278
  supportsSeed: true,
258
- supportsWebSearch: true,
259
- supportsGoogleSearch: true,
260
- supportsLimitGenerations: true,
261
279
  supportsSyncMode: true,
262
- maxReferenceImages: 14
280
+ supportsWebSearch: true,
281
+ type: "generation"
263
282
  },
264
283
  gemini: {
265
- name: "Gemini 2.5 Flash",
266
- endpoint: "fal-ai/gemini-25-flash-image",
267
284
  editEndpoint: "fal-ai/gemini-25-flash-image/edit",
268
- type: "generation",
269
- pricing: "$0.04",
270
- pricePerImageUsd: 0.0398,
285
+ endpoint: "fal-ai/gemini-25-flash-image",
271
286
  falPricing: {
272
- endpointId: "fal-ai/gemini-25-flash-image",
273
- unitPrice: 0.0398,
274
- unit: "images",
287
+ checkedAt: FAL_PRICING_CHECKED_AT,
275
288
  currency: "USD",
289
+ endpointId: "fal-ai/gemini-25-flash-image",
276
290
  estimatedCostPerImageUsd: 0.0398,
277
291
  source: "fal-pricing-api",
278
- checkedAt: FAL_PRICING_CHECKED_AT
292
+ unit: "images",
293
+ unitPrice: 0.0398
279
294
  },
295
+ maxReferenceImages: 4,
296
+ name: "Gemini 2.5 Flash",
297
+ pricePerImageUsd: 0.0398,
298
+ pricing: "$0.04",
280
299
  sizeMode: "aspect_ratio",
281
300
  supportsAspect: true,
282
- supportsResolution: false,
283
301
  supportsEdit: true,
284
302
  supportsNumImages: true,
285
303
  supportsOutputFormat: true,
304
+ supportsResolution: false,
286
305
  supportsSafetyTolerance: true,
287
306
  supportsSeed: true,
288
307
  supportsSyncMode: true,
289
- maxReferenceImages: 4
308
+ type: "generation"
290
309
  },
291
310
  gemini3: {
292
- name: "Gemini 3 Pro",
293
- endpoint: "fal-ai/gemini-3-pro-image-preview",
294
311
  editEndpoint: "fal-ai/gemini-3-pro-image-preview/edit",
295
- type: "generation",
296
- pricing: "$0.15",
297
- pricePerImageUsd: 0.15,
312
+ endpoint: "fal-ai/gemini-3-pro-image-preview",
298
313
  falPricing: {
299
- endpointId: "fal-ai/gemini-3-pro-image-preview",
300
- unitPrice: 0.15,
301
- unit: "images",
314
+ checkedAt: FAL_PRICING_CHECKED_AT,
302
315
  currency: "USD",
316
+ endpointId: "fal-ai/gemini-3-pro-image-preview",
303
317
  estimatedCostPerImageUsd: 0.15,
304
318
  source: "fal-pricing-api",
305
- checkedAt: FAL_PRICING_CHECKED_AT
319
+ unit: "images",
320
+ unitPrice: 0.15
306
321
  },
322
+ maxReferenceImages: 4,
323
+ name: "Gemini 3 Pro",
324
+ pricePerImageUsd: 0.15,
325
+ pricing: "$0.15",
307
326
  sizeMode: "aspect_ratio",
308
327
  supportsAspect: true,
309
- supportsResolution: true,
310
328
  supportsEdit: true,
311
329
  supportsNumImages: true,
312
330
  supportsOutputFormat: true,
331
+ supportsResolution: true,
313
332
  supportsSafetyTolerance: true,
314
333
  supportsSeed: true,
315
- supportsWebSearch: true,
316
334
  supportsSyncMode: true,
317
- maxReferenceImages: 4
335
+ supportsWebSearch: true,
336
+ type: "generation"
318
337
  },
319
338
  seedream4: {
320
- name: "Seedream 4.0",
321
- endpoint: "fal-ai/bytedance/seedream/v4/text-to-image",
322
- editEndpoint: "fal-ai/bytedance/seedream/v4/edit",
323
- type: "generation",
324
- pricing: "$0.03",
325
- pricePerImageUsd: 0.03,
326
- falPricing: {
327
- endpointId: "fal-ai/bytedance/seedream/v4/text-to-image",
328
- unitPrice: 0.03,
329
- unit: "images",
330
- currency: "USD",
331
- estimatedCostPerImageUsd: 0.03,
332
- source: "fal-pricing-api",
333
- checkedAt: FAL_PRICING_CHECKED_AT
334
- },
335
339
  benchmark: {
336
- useCase: "High-ranked budget generation and large multi-reference edits",
337
- tiers: { quality: "best", speed: "balanced", price: "budget" },
340
+ artificialAnalysis: {
341
+ editing: { elo: 1184, pricePer1k: 30, rank: 16, winRate: 0.61 },
342
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
343
+ sourceUrls: AA_IMAGE_SOURCES,
344
+ textToImage: { elo: 1198, pricePer1k: 30, rank: 6, winRate: 0.59 }
345
+ },
338
346
  speed: {
339
347
  medianSeconds: 14.3,
340
348
  p95Seconds: 21,
341
349
  source: "artificial-analysis-models"
342
350
  },
343
- artificialAnalysis: {
344
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
345
- sourceUrls: AA_IMAGE_SOURCES,
346
- textToImage: { rank: 6, elo: 1198, winRate: 0.59, pricePer1k: 30 },
347
- editing: { rank: 16, elo: 1184, winRate: 0.61, pricePer1k: 30 }
348
- }
351
+ tiers: { price: "budget", quality: "best", speed: "balanced" },
352
+ useCase: "High-ranked budget generation and large multi-reference edits"
353
+ },
354
+ editEndpoint: "fal-ai/bytedance/seedream/v4/edit",
355
+ endpoint: "fal-ai/bytedance/seedream/v4/text-to-image",
356
+ falPricing: {
357
+ checkedAt: FAL_PRICING_CHECKED_AT,
358
+ currency: "USD",
359
+ endpointId: "fal-ai/bytedance/seedream/v4/text-to-image",
360
+ estimatedCostPerImageUsd: 0.03,
361
+ source: "fal-pricing-api",
362
+ unit: "images",
363
+ unitPrice: 0.03
349
364
  },
365
+ maxReferenceImages: 10,
366
+ name: "Seedream 4.0",
367
+ pricePerImageUsd: 0.03,
368
+ pricing: "$0.03",
350
369
  sizeMode: "image_size_enum",
351
370
  supportsAspect: true,
352
- supportsResolution: false,
353
371
  supportsEdit: true,
354
372
  supportsNumImages: true,
373
+ supportsResolution: false,
355
374
  supportsSeed: true,
356
375
  supportsSyncMode: true,
357
- maxReferenceImages: 10
376
+ type: "generation"
358
377
  },
359
378
  seedream45: {
360
- name: "Seedream 4.5",
361
- endpoint: "fal-ai/bytedance/seedream/v4.5/text-to-image",
362
- editEndpoint: "fal-ai/bytedance/seedream/v4.5/edit",
363
- type: "generation",
364
- pricing: "$0.04",
365
- pricePerImageUsd: 0.04,
366
- falPricing: {
367
- endpointId: "fal-ai/bytedance/seedream/v4.5/text-to-image",
368
- unitPrice: 0.04,
369
- unit: "images",
370
- currency: "USD",
371
- estimatedCostPerImageUsd: 0.04,
372
- source: "fal-pricing-api",
373
- checkedAt: FAL_PRICING_CHECKED_AT
374
- },
375
379
  benchmark: {
376
- useCase: "Cheap current Seedream model with good animation/art scores",
377
- tiers: { quality: "better", speed: "balanced", price: "standard" },
380
+ artificialAnalysis: {
381
+ editing: { elo: 1184, pricePer1k: 40, rank: 17, winRate: 0.58 },
382
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
383
+ sourceUrls: AA_IMAGE_SOURCES,
384
+ textToImage: { elo: 1167, pricePer1k: 40, rank: 17, winRate: 0.63 }
385
+ },
378
386
  speed: {
379
387
  medianSeconds: 18.2,
380
388
  p95Seconds: 43.3,
381
389
  source: "artificial-analysis-models"
382
390
  },
383
- artificialAnalysis: {
384
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
385
- sourceUrls: AA_IMAGE_SOURCES,
386
- textToImage: { rank: 17, elo: 1167, winRate: 0.63, pricePer1k: 40 },
387
- editing: { rank: 17, elo: 1184, winRate: 0.58, pricePer1k: 40 }
388
- }
391
+ tiers: { price: "standard", quality: "better", speed: "balanced" },
392
+ useCase: "Cheap current Seedream model with good animation/art scores"
393
+ },
394
+ editEndpoint: "fal-ai/bytedance/seedream/v4.5/edit",
395
+ endpoint: "fal-ai/bytedance/seedream/v4.5/text-to-image",
396
+ falPricing: {
397
+ checkedAt: FAL_PRICING_CHECKED_AT,
398
+ currency: "USD",
399
+ endpointId: "fal-ai/bytedance/seedream/v4.5/text-to-image",
400
+ estimatedCostPerImageUsd: 0.04,
401
+ source: "fal-pricing-api",
402
+ unit: "images",
403
+ unitPrice: 0.04
404
+ },
405
+ maxReferenceImages: 10,
406
+ name: "Seedream 4.5",
407
+ pricePerImageUsd: 0.04,
408
+ pricing: "$0.04",
409
+ sizeMode: "image_size_enum",
410
+ supportsAspect: true,
411
+ supportsEdit: true,
412
+ supportsNumImages: true,
413
+ supportsResolution: false,
414
+ supportsSeed: true,
415
+ supportsSyncMode: true,
416
+ type: "generation"
417
+ },
418
+ seedream5: {
419
+ name: "Seedream 5.0 Pro",
420
+ endpoint: "bytedance/seedream/v5/pro/text-to-image",
421
+ editEndpoint: "bytedance/seedream/v5/pro/edit",
422
+ type: "generation",
423
+ // fal tiers Seedream v5 Pro by output size: $0.0675 up to 1536², $0.135 up to 2048².
424
+ pricing: "$0.0675 (\u22641536\xB2) / $0.135 (\u22642048\xB2)",
425
+ pricePerImageUsd: 0.0675,
426
+ falPricing: {
427
+ checkedAt: FAL_PRICING_CHECKED_JUL_2026,
428
+ currency: "USD",
429
+ endpointId: "bytedance/seedream/v5/pro/text-to-image",
430
+ estimatedCostPerImageUsd: 0.0675,
431
+ source: "fal-pricing-api",
432
+ unit: "images",
433
+ unitPrice: 0.0675
389
434
  },
390
435
  sizeMode: "image_size_enum",
391
436
  supportsAspect: true,
@@ -396,417 +441,522 @@ var MODELS = {
396
441
  supportsSyncMode: true,
397
442
  maxReferenceImages: 10
398
443
  },
399
- "flux2-max": {
400
- name: "FLUX.2 Max",
401
- endpoint: "fal-ai/flux-2-max",
402
- editEndpoint: "fal-ai/flux-2-max/edit",
444
+ "seedream5-lite": {
445
+ name: "Seedream 5.0 Lite",
446
+ endpoint: "fal-ai/bytedance/seedream/v5/lite/text-to-image",
447
+ editEndpoint: "fal-ai/bytedance/seedream/v5/lite/edit",
403
448
  type: "generation",
404
- pricing: "$0.07/MP",
405
- pricePerImageUsd: 0.07,
449
+ // Flat price regardless of resolution; native up to Auto 3K (~9.4MP).
450
+ pricing: "$0.035",
451
+ pricePerImageUsd: 0.035,
406
452
  falPricing: {
407
- endpointId: "fal-ai/flux-2-max",
408
- unitPrice: 0.07,
409
- unit: "megapixels",
453
+ checkedAt: FAL_PRICING_CHECKED_JUL_2026,
410
454
  currency: "USD",
411
- estimatedCostPerImageUsd: 0.07,
455
+ endpointId: "fal-ai/bytedance/seedream/v5/lite/text-to-image",
456
+ estimatedCostPerImageUsd: 0.035,
412
457
  source: "fal-pricing-api",
413
- checkedAt: FAL_PRICING_CHECKED_AT
458
+ unit: "images",
459
+ unitPrice: 0.035
414
460
  },
461
+ sizeMode: "image_size_enum",
462
+ supportsAspect: true,
463
+ supportsResolution: false,
464
+ supportsEdit: true,
465
+ supportsNumImages: true,
466
+ supportsSeed: true,
467
+ supportsSyncMode: true,
468
+ maxReferenceImages: 10
469
+ },
470
+ "flux2-max": {
415
471
  benchmark: {
416
- useCase: "Best FLUX quality and high-end editing",
417
- tiers: { quality: "best", speed: "slow", price: "premium" },
472
+ artificialAnalysis: {
473
+ editing: { elo: 1206, pricePer1k: 140, rank: 10, winRate: 0.6 },
474
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
475
+ sourceUrls: AA_IMAGE_SOURCES,
476
+ textToImage: { elo: 1197, pricePer1k: 70, rank: 8, winRate: 0.67 }
477
+ },
418
478
  speed: {
419
479
  medianSeconds: 31.6,
420
480
  p95Seconds: 41,
421
481
  source: "artificial-analysis-models"
422
482
  },
423
- artificialAnalysis: {
424
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
425
- sourceUrls: AA_IMAGE_SOURCES,
426
- textToImage: { rank: 8, elo: 1197, winRate: 0.67, pricePer1k: 70 },
427
- editing: { rank: 10, elo: 1206, winRate: 0.6, pricePer1k: 140 }
428
- }
483
+ tiers: { price: "premium", quality: "best", speed: "slow" },
484
+ useCase: "Best FLUX quality and high-end editing"
429
485
  },
486
+ editEndpoint: "fal-ai/flux-2-max/edit",
487
+ endpoint: "fal-ai/flux-2-max",
488
+ falPricing: {
489
+ checkedAt: FAL_PRICING_CHECKED_AT,
490
+ currency: "USD",
491
+ endpointId: "fal-ai/flux-2-max",
492
+ estimatedCostPerImageUsd: 0.07,
493
+ source: "fal-pricing-api",
494
+ unit: "megapixels",
495
+ unitPrice: 0.07
496
+ },
497
+ maxReferenceImages: 10,
498
+ name: "FLUX.2 Max",
499
+ pricePerImageUsd: 0.07,
500
+ pricing: "$0.07/MP",
430
501
  sizeMode: "image_size_enum",
431
502
  supportsAspect: true,
432
- supportsResolution: false,
433
503
  supportsEdit: true,
434
504
  supportsNumImages: false,
435
505
  supportsOutputFormat: true,
436
- supportsSafetyTolerance: true,
506
+ supportsResolution: false,
437
507
  supportsSafetyChecker: true,
508
+ supportsSafetyTolerance: true,
438
509
  supportsSeed: true,
439
510
  supportsSyncMode: true,
440
- maxReferenceImages: 10
511
+ type: "generation"
441
512
  },
442
513
  "flux2-pro": {
443
- name: "FLUX.2 Pro",
444
- endpoint: "fal-ai/flux-2-pro",
445
- editEndpoint: "fal-ai/flux-2-pro/edit",
446
- type: "generation",
447
- pricing: "$0.03/MP",
448
- pricePerImageUsd: 0.03,
449
- falPricing: {
450
- endpointId: "fal-ai/flux-2-pro",
451
- unitPrice: 0.03,
452
- unit: "processed megapixels",
453
- currency: "USD",
454
- estimatedCostPerImageUsd: 0.03,
455
- source: "fal-pricing-api",
456
- checkedAt: FAL_PRICING_CHECKED_AT
457
- },
458
514
  benchmark: {
459
- useCase: "Production FLUX quality at a low per-megapixel price",
460
- tiers: { quality: "best", speed: "balanced", price: "budget" },
515
+ artificialAnalysis: {
516
+ editing: { elo: 1170, pricePer1k: 30, rank: 21 },
517
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
518
+ sourceUrls: AA_IMAGE_SOURCES,
519
+ textToImage: { elo: 1186, pricePer1k: 30, rank: 10, winRate: 0.62 }
520
+ },
461
521
  speed: {
462
522
  medianSeconds: 16.1,
463
523
  p95Seconds: 23.9,
464
524
  source: "artificial-analysis-models"
465
525
  },
466
- artificialAnalysis: {
467
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
468
- sourceUrls: AA_IMAGE_SOURCES,
469
- textToImage: { rank: 10, elo: 1186, winRate: 0.62, pricePer1k: 30 },
470
- editing: { rank: 21, elo: 1170, pricePer1k: 30 }
471
- }
526
+ tiers: { price: "budget", quality: "best", speed: "balanced" },
527
+ useCase: "Production FLUX quality at a low per-megapixel price"
528
+ },
529
+ editEndpoint: "fal-ai/flux-2-pro/edit",
530
+ endpoint: "fal-ai/flux-2-pro",
531
+ falPricing: {
532
+ checkedAt: FAL_PRICING_CHECKED_AT,
533
+ currency: "USD",
534
+ endpointId: "fal-ai/flux-2-pro",
535
+ estimatedCostPerImageUsd: 0.03,
536
+ source: "fal-pricing-api",
537
+ unit: "processed megapixels",
538
+ unitPrice: 0.03
472
539
  },
540
+ maxReferenceImages: 10,
541
+ name: "FLUX.2 Pro",
542
+ pricePerImageUsd: 0.03,
543
+ pricing: "$0.03/MP",
473
544
  sizeMode: "image_size_enum",
474
545
  supportsAspect: true,
475
- supportsResolution: false,
476
546
  supportsEdit: true,
477
547
  supportsNumImages: false,
478
548
  supportsOutputFormat: true,
479
- supportsSafetyTolerance: true,
549
+ supportsResolution: false,
480
550
  supportsSafetyChecker: true,
551
+ supportsSafetyTolerance: true,
481
552
  supportsSeed: true,
482
553
  supportsSyncMode: true,
483
- maxReferenceImages: 10
554
+ type: "generation"
484
555
  },
485
556
  "flux2-flex": {
486
- name: "FLUX.2 Flex",
487
- endpoint: "fal-ai/flux-2-flex",
488
- editEndpoint: "fal-ai/flux-2-flex/edit",
489
- type: "generation",
490
- pricing: "$0.05/MP",
491
- pricePerImageUsd: 0.05,
492
- falPricing: {
493
- endpointId: "fal-ai/flux-2-flex",
494
- unitPrice: 0.05,
495
- unit: "processed megapixels",
496
- currency: "USD",
497
- estimatedCostPerImageUsd: 0.05,
498
- source: "fal-pricing-api",
499
- checkedAt: FAL_PRICING_CHECKED_AT
500
- },
501
557
  benchmark: {
502
- useCase: "FLUX quality with guidance and step controls",
503
- tiers: { quality: "best", speed: "balanced", price: "standard" },
558
+ artificialAnalysis: {
559
+ editing: { elo: 1161, pricePer1k: 60, rank: 22 },
560
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
561
+ sourceUrls: AA_IMAGE_SOURCES,
562
+ textToImage: { elo: 1182, pricePer1k: 60, rank: 13, winRate: 0.61 }
563
+ },
504
564
  speed: {
505
565
  medianSeconds: 15.3,
506
566
  p95Seconds: 22.2,
507
567
  source: "artificial-analysis-models"
508
568
  },
509
- artificialAnalysis: {
510
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
511
- sourceUrls: AA_IMAGE_SOURCES,
512
- textToImage: { rank: 13, elo: 1182, winRate: 0.61, pricePer1k: 60 },
513
- editing: { rank: 22, elo: 1161, pricePer1k: 60 }
514
- }
569
+ tiers: { price: "standard", quality: "best", speed: "balanced" },
570
+ useCase: "FLUX quality with guidance and step controls"
571
+ },
572
+ editEndpoint: "fal-ai/flux-2-flex/edit",
573
+ endpoint: "fal-ai/flux-2-flex",
574
+ falPricing: {
575
+ checkedAt: FAL_PRICING_CHECKED_AT,
576
+ currency: "USD",
577
+ endpointId: "fal-ai/flux-2-flex",
578
+ estimatedCostPerImageUsd: 0.05,
579
+ source: "fal-pricing-api",
580
+ unit: "processed megapixels",
581
+ unitPrice: 0.05
515
582
  },
583
+ maxReferenceImages: 10,
584
+ name: "FLUX.2 Flex",
585
+ pricePerImageUsd: 0.05,
586
+ pricing: "$0.05/MP",
516
587
  sizeMode: "image_size_enum",
588
+ supportedOutputFormats: ["jpeg", "png"],
517
589
  supportsAspect: true,
518
- supportsResolution: false,
519
590
  supportsEdit: true,
591
+ supportsGuidanceScale: true,
592
+ supportsInferenceSteps: true,
520
593
  supportsNumImages: false,
521
594
  supportsOutputFormat: true,
522
- supportedOutputFormats: ["jpeg", "png"],
523
- supportsSafetyTolerance: true,
595
+ supportsResolution: false,
524
596
  supportsSafetyChecker: true,
597
+ supportsSafetyTolerance: true,
525
598
  supportsSeed: true,
526
- supportsGuidanceScale: true,
527
- supportsInferenceSteps: true,
528
599
  supportsSyncMode: true,
529
- maxReferenceImages: 10
600
+ type: "generation"
530
601
  },
531
602
  "flux2-dev": {
532
- name: "FLUX.2 Dev",
533
- endpoint: "fal-ai/flux-2",
534
- editEndpoint: "fal-ai/flux-2/edit",
535
- type: "generation",
536
- pricing: "$0.00167/sec",
537
- pricePerImageUsd: 0.012,
538
- falPricing: {
539
- endpointId: "fal-ai/flux-2",
540
- unitPrice: 167e-5,
541
- unit: "compute seconds",
542
- currency: "USD",
543
- estimatedCostPerImageUsd: 0.012,
544
- source: "fal-pricing-api",
545
- checkedAt: FAL_PRICING_CHECKED_AT
546
- },
547
603
  benchmark: {
548
- useCase: "Open FLUX.2 quality with low average cost",
549
- tiers: { quality: "better", speed: "fast", price: "variable" },
604
+ artificialAnalysis: {
605
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
606
+ sourceUrls: AA_IMAGE_SOURCES,
607
+ textToImage: { elo: 1160, pricePer1k: 12, rank: 19, winRate: 0.57 }
608
+ },
550
609
  speed: {
551
610
  medianSeconds: 4.6,
552
611
  p95Seconds: 69.6,
553
612
  source: "artificial-analysis-models"
554
613
  },
555
- artificialAnalysis: {
556
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
557
- sourceUrls: AA_IMAGE_SOURCES,
558
- textToImage: { rank: 19, elo: 1160, winRate: 0.57, pricePer1k: 12 }
559
- }
614
+ tiers: { price: "variable", quality: "better", speed: "fast" },
615
+ useCase: "Open FLUX.2 quality with low average cost"
616
+ },
617
+ editEndpoint: "fal-ai/flux-2/edit",
618
+ endpoint: "fal-ai/flux-2",
619
+ falPricing: {
620
+ checkedAt: FAL_PRICING_CHECKED_AT,
621
+ currency: "USD",
622
+ endpointId: "fal-ai/flux-2",
623
+ estimatedCostPerImageUsd: 0.012,
624
+ source: "fal-pricing-api",
625
+ unit: "compute seconds",
626
+ unitPrice: 167e-5
560
627
  },
628
+ maxReferenceImages: 10,
629
+ name: "FLUX.2 [dev]",
630
+ pricePerImageUsd: 0.012,
631
+ pricing: "$0.00167/sec",
561
632
  sizeMode: "image_size_enum",
562
633
  supportsAspect: true,
563
- supportsResolution: false,
564
634
  supportsEdit: true,
635
+ supportsGuidanceScale: true,
636
+ supportsInferenceSteps: true,
565
637
  supportsNumImages: true,
566
638
  supportsOutputFormat: true,
567
- supportsSafetyTolerance: true,
639
+ supportsResolution: false,
568
640
  supportsSafetyChecker: true,
641
+ supportsSafetyTolerance: true,
569
642
  supportsSeed: true,
643
+ supportsSyncMode: true,
644
+ type: "generation"
645
+ },
646
+ "flux2-turbo": {
647
+ endpoint: "fal-ai/flux-2/turbo",
648
+ falPricing: {
649
+ checkedAt: FAL_PRICING_CHECKED_JUL_2026,
650
+ currency: "USD",
651
+ endpointId: "fal-ai/flux-2/turbo",
652
+ estimatedCostPerImageUsd: 8e-3,
653
+ source: "fal-pricing-api",
654
+ unit: "megapixels",
655
+ unitPrice: 8e-3
656
+ },
657
+ name: "FLUX.2 Turbo",
658
+ pricePerImageUsd: 8e-3,
659
+ pricing: "$0.008/MP",
660
+ sizeMode: "image_size_enum",
661
+ supportsAspect: true,
662
+ supportsEdit: false,
570
663
  supportsGuidanceScale: true,
571
- supportsInferenceSteps: true,
664
+ supportsNumImages: true,
665
+ supportsOutputFormat: true,
666
+ supportsResolution: false,
667
+ supportsSafetyChecker: true,
668
+ supportsSeed: true,
572
669
  supportsSyncMode: true,
573
- maxReferenceImages: 10
670
+ type: "generation"
574
671
  },
575
672
  flux: {
576
- name: "FLUX Pro Ultra",
577
673
  endpoint: "fal-ai/flux-pro/v1.1-ultra",
578
- type: "generation",
579
- pricing: "$0.06",
580
- pricePerImageUsd: 0.06,
581
674
  falPricing: {
582
- endpointId: "fal-ai/flux-pro/v1.1-ultra",
583
- unitPrice: 0.06,
584
- unit: "images",
675
+ checkedAt: FAL_PRICING_CHECKED_AT,
585
676
  currency: "USD",
677
+ endpointId: "fal-ai/flux-pro/v1.1-ultra",
586
678
  estimatedCostPerImageUsd: 0.06,
587
679
  source: "fal-pricing-api",
588
- checkedAt: FAL_PRICING_CHECKED_AT
680
+ unit: "images",
681
+ unitPrice: 0.06
589
682
  },
683
+ maxReferenceImages: 1,
684
+ name: "FLUX Pro Ultra",
685
+ pricePerImageUsd: 0.06,
686
+ pricing: "$0.06",
590
687
  sizeMode: "aspect_ratio",
591
688
  supportsAspect: true,
592
- supportsResolution: false,
593
689
  supportsEdit: true,
690
+ supportsEnhancePrompt: true,
691
+ supportsImagePromptStrength: true,
594
692
  supportsNumImages: true,
595
693
  supportsOutputFormat: true,
596
694
  supportsRaw: true,
597
- supportsEnhancePrompt: true,
695
+ supportsResolution: false,
598
696
  supportsSafetyTolerance: true,
599
697
  supportsSeed: true,
600
- supportsImagePromptStrength: true,
601
698
  supportsSyncMode: true,
602
- maxReferenceImages: 1
699
+ type: "generation"
603
700
  },
604
701
  "flux-fast": {
605
- name: "FLUX Schnell",
606
702
  endpoint: "fal-ai/flux/schnell",
607
- type: "generation",
608
- pricing: "$0.003",
609
- pricePerImageUsd: 3e-3,
610
703
  falPricing: {
611
- endpointId: "fal-ai/flux/schnell",
612
- unitPrice: 3e-3,
613
- unit: "megapixels",
704
+ checkedAt: FAL_PRICING_CHECKED_AT,
614
705
  currency: "USD",
706
+ endpointId: "fal-ai/flux/schnell",
615
707
  estimatedCostPerImageUsd: 3e-3,
616
708
  source: "fal-pricing-api",
617
- checkedAt: FAL_PRICING_CHECKED_AT
709
+ unit: "megapixels",
710
+ unitPrice: 3e-3
618
711
  },
712
+ name: "FLUX Schnell",
713
+ pricePerImageUsd: 3e-3,
714
+ pricing: "$0.003",
619
715
  sizeMode: "image_size_enum",
620
716
  supportsAspect: true,
621
- supportsResolution: false,
622
717
  supportsEdit: false,
623
- supportsNumImages: true,
624
- supportsOutputFormat: true,
625
718
  supportsGuidanceScale: true,
626
719
  supportsInferenceSteps: true,
720
+ supportsNumImages: true,
721
+ supportsOutputFormat: true,
722
+ supportsResolution: false,
627
723
  supportsSeed: true,
628
- supportsSyncMode: true
724
+ supportsSyncMode: true,
725
+ type: "generation"
629
726
  },
630
727
  recraft: {
631
- name: "Recraft V3",
632
728
  endpoint: "fal-ai/recraft-v3",
633
- type: "generation",
634
- pricing: "$0.04",
635
- pricePerImageUsd: 0.04,
636
729
  falPricing: {
730
+ checkedAt: FAL_PRICING_CHECKED_AT,
731
+ currency: "USD",
637
732
  endpointId: "fal-ai/recraft-v3",
638
- unitPrice: 0.04,
733
+ estimatedCostPerImageUsd: 0.04,
734
+ source: "fal-pricing-api",
639
735
  unit: "images",
736
+ unitPrice: 0.04
737
+ },
738
+ name: "Recraft V3",
739
+ pricePerImageUsd: 0.04,
740
+ pricing: "$0.04",
741
+ sizeMode: "image_size_enum",
742
+ supportsAspect: true,
743
+ supportsEdit: false,
744
+ supportsNumImages: false,
745
+ supportsResolution: false,
746
+ supportsStyle: true,
747
+ supportsSyncMode: true,
748
+ type: "generation"
749
+ },
750
+ recraft4: {
751
+ endpoint: "fal-ai/recraft/v4/text-to-image",
752
+ falPricing: {
753
+ checkedAt: FAL_PRICING_CHECKED_JUL_2026,
640
754
  currency: "USD",
755
+ endpointId: "fal-ai/recraft/v4/text-to-image",
641
756
  estimatedCostPerImageUsd: 0.04,
642
757
  source: "fal-pricing-api",
643
- checkedAt: FAL_PRICING_CHECKED_AT
758
+ unit: "images",
759
+ unitPrice: 0.04
644
760
  },
761
+ name: "Recraft V4",
762
+ pricePerImageUsd: 0.04,
763
+ pricing: "$0.04",
645
764
  sizeMode: "image_size_enum",
646
765
  supportsAspect: true,
647
- supportsResolution: false,
648
766
  supportsEdit: false,
649
767
  supportsNumImages: false,
768
+ supportsResolution: false,
650
769
  supportsStyle: true,
651
- supportsSyncMode: true
770
+ supportsSyncMode: true,
771
+ type: "generation"
652
772
  },
653
773
  ideogram: {
654
- name: "Ideogram V3",
655
774
  endpoint: "fal-ai/ideogram/v3",
656
- type: "generation",
657
- pricing: "$0.03",
658
- pricePerImageUsd: 0.03,
659
775
  falPricing: {
660
- endpointId: "fal-ai/ideogram/v3",
661
- unitPrice: 0.03,
662
- unit: "images",
776
+ checkedAt: FAL_PRICING_CHECKED_AT,
663
777
  currency: "USD",
778
+ endpointId: "fal-ai/ideogram/v3",
664
779
  estimatedCostPerImageUsd: 0.03,
665
780
  source: "fal-pricing-api",
666
- checkedAt: FAL_PRICING_CHECKED_AT
781
+ unit: "images",
782
+ unitPrice: 0.03
667
783
  },
784
+ name: "Ideogram V3",
785
+ pricePerImageUsd: 0.03,
786
+ pricing: "$0.03",
668
787
  sizeMode: "image_size_enum",
669
788
  supportsAspect: true,
670
- supportsResolution: false,
671
789
  supportsEdit: false,
672
- supportsNumImages: true,
673
790
  supportsExpandPrompt: true,
674
791
  supportsNegativePrompt: true,
792
+ supportsNumImages: true,
675
793
  supportsRenderingSpeed: true,
794
+ supportsResolution: false,
676
795
  supportsSeed: true,
677
796
  supportsStyle: true,
678
- supportsSyncMode: true
797
+ supportsSyncMode: true,
798
+ type: "generation"
679
799
  },
680
- "grok-image": {
681
- name: "Grok Imagine Image",
682
- endpoint: "xai/grok-imagine-image",
683
- editEndpoint: "xai/grok-imagine-image/edit",
800
+ ideogram4: {
801
+ name: "Ideogram V4",
802
+ endpoint: "ideogram/v4",
684
803
  type: "generation",
685
- pricing: "$0.02",
686
- pricePerImageUsd: 0.02,
804
+ // fal bills v4 per output megapixel by rendering speed: TURBO $0.03, BALANCED
805
+ // $0.06, QUALITY $0.10 (plus a flat $0.03 when prompt expansion is used).
806
+ // Headline uses the TURBO 1MP rate, matching how ideogram v3 is modeled.
807
+ pricing: "$0.03",
808
+ pricePerImageUsd: 0.03,
687
809
  falPricing: {
688
- endpointId: "xai/grok-imagine-image",
689
- unitPrice: 0.02,
690
- unit: "images",
810
+ checkedAt: FAL_PRICING_CHECKED_JUL_2026,
691
811
  currency: "USD",
692
- estimatedCostPerImageUsd: 0.02,
812
+ endpointId: "ideogram/v4",
813
+ estimatedCostPerImageUsd: 0.03,
693
814
  source: "fal-pricing-api",
694
- checkedAt: FAL_PRICING_CHECKED_AT
815
+ unit: "megapixels",
816
+ unitPrice: 0.03
695
817
  },
818
+ sizeMode: "image_size_enum",
819
+ supportsAspect: true,
820
+ supportsResolution: false,
821
+ supportsEdit: false,
822
+ supportsNumImages: true,
823
+ supportsRenderingSpeed: true,
824
+ supportsSeed: true,
825
+ supportsOutputFormat: true,
826
+ supportedOutputFormats: ["jpeg", "png"],
827
+ supportsSafetyChecker: true,
828
+ supportsSyncMode: true
829
+ },
830
+ "grok-image": {
696
831
  benchmark: {
697
- useCase: "Very fast, cheap generation and edits",
698
- tiers: { quality: "best", speed: "fast", price: "budget" },
832
+ artificialAnalysis: {
833
+ editing: { elo: 1213, pricePer1k: 20, rank: 7, winRate: 0.6 },
834
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
835
+ sourceUrls: AA_IMAGE_SOURCES,
836
+ textToImage: { elo: 1182, pricePer1k: 20, rank: 12, winRate: 0.6 }
837
+ },
699
838
  speed: {
700
839
  medianSeconds: 5.1,
701
840
  p95Seconds: 6.5,
702
841
  source: "artificial-analysis-models"
703
842
  },
704
- artificialAnalysis: {
705
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
706
- sourceUrls: AA_IMAGE_SOURCES,
707
- textToImage: { rank: 12, elo: 1182, winRate: 0.6, pricePer1k: 20 },
708
- editing: { rank: 7, elo: 1213, winRate: 0.6, pricePer1k: 20 }
709
- }
843
+ tiers: { price: "budget", quality: "best", speed: "fast" },
844
+ useCase: "Very fast, cheap generation and edits"
845
+ },
846
+ editEndpoint: "xai/grok-imagine-image/edit",
847
+ endpoint: "xai/grok-imagine-image",
848
+ falPricing: {
849
+ checkedAt: FAL_PRICING_CHECKED_AT,
850
+ currency: "USD",
851
+ endpointId: "xai/grok-imagine-image",
852
+ estimatedCostPerImageUsd: 0.02,
853
+ source: "fal-pricing-api",
854
+ unit: "images",
855
+ unitPrice: 0.02
710
856
  },
857
+ maxReferenceImages: 4,
858
+ name: "Grok Imagine Image",
859
+ pricePerImageUsd: 0.02,
860
+ pricing: "$0.02",
711
861
  sizeMode: "aspect_ratio",
712
862
  supportsAspect: true,
713
- supportsResolution: true,
714
863
  supportsEdit: true,
715
864
  supportsNumImages: true,
716
865
  supportsOutputFormat: true,
866
+ supportsResolution: true,
717
867
  supportsSyncMode: true,
718
- maxReferenceImages: 4
868
+ type: "generation"
719
869
  },
720
870
  qwen: {
721
- name: "Qwen Image",
722
- endpoint: "fal-ai/qwen-image",
723
- type: "generation",
724
- pricing: "$0.02/MP",
725
- pricePerImageUsd: 0.02,
726
- falPricing: {
727
- endpointId: "fal-ai/qwen-image",
728
- unitPrice: 0.02,
729
- unit: "megapixels",
730
- currency: "USD",
731
- estimatedCostPerImageUsd: 0.02,
732
- source: "fal-pricing-api",
733
- checkedAt: FAL_PRICING_CHECKED_AT
734
- },
735
871
  benchmark: {
736
- useCase: "Low-cost open-weight text and design generation",
737
- tiers: { quality: "better", speed: "slow", price: "budget" },
872
+ artificialAnalysis: {
873
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
874
+ sourceUrls: AA_IMAGE_SOURCES,
875
+ textToImage: { elo: 1158, pricePer1k: 20, rank: 20, winRate: 0.6 }
876
+ },
738
877
  speed: {
739
878
  medianSeconds: 23.3,
740
879
  p95Seconds: 491,
741
880
  source: "artificial-analysis-models"
742
881
  },
743
- artificialAnalysis: {
744
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
745
- sourceUrls: AA_IMAGE_SOURCES,
746
- textToImage: { rank: 20, elo: 1158, winRate: 0.6, pricePer1k: 20 }
747
- }
882
+ tiers: { price: "budget", quality: "better", speed: "slow" },
883
+ useCase: "Low-cost open-weight text and design generation"
884
+ },
885
+ endpoint: "fal-ai/qwen-image",
886
+ falPricing: {
887
+ checkedAt: FAL_PRICING_CHECKED_AT,
888
+ currency: "USD",
889
+ endpointId: "fal-ai/qwen-image",
890
+ estimatedCostPerImageUsd: 0.02,
891
+ source: "fal-pricing-api",
892
+ unit: "megapixels",
893
+ unitPrice: 0.02
748
894
  },
895
+ name: "Qwen Image",
896
+ pricePerImageUsd: 0.02,
897
+ pricing: "$0.02/MP",
749
898
  sizeMode: "image_size_enum",
750
899
  supportsAspect: true,
751
- supportsResolution: false,
752
900
  supportsEdit: false,
753
901
  supportsNumImages: true,
754
902
  supportsOutputFormat: true,
903
+ supportsResolution: false,
755
904
  supportsSeed: true,
756
- supportsSyncMode: true
905
+ supportsSyncMode: true,
906
+ type: "generation"
757
907
  },
758
908
  // ─── Video Models ─────────────────────────────────────────────
759
909
  kling: {
760
- name: "Kling v3 Pro",
761
910
  endpoint: "fal-ai/kling-video/v3/pro/image-to-video",
762
- type: "video",
911
+ name: "Kling v3 Pro",
763
912
  pricing: "$0.11/sec",
764
913
  sizeMode: "none",
765
914
  supportsAspect: false,
766
- supportsResolution: false,
767
915
  supportsEdit: false,
768
- supportsNumImages: false
916
+ supportsNumImages: false,
917
+ supportsResolution: false,
918
+ type: "video"
769
919
  },
770
920
  // ─── Utility Models ───────────────────────────────────────────
771
921
  clarity: {
772
- name: "Clarity Upscaler",
773
922
  endpoint: "fal-ai/clarity-upscaler",
774
- type: "utility",
775
- pricing: "$0.02",
923
+ name: "Clarity Upscaler",
924
+ pricing: "$0.03/MP",
776
925
  supportsAspect: false,
777
- supportsResolution: false,
778
926
  supportsEdit: false,
779
- supportsNumImages: false
927
+ supportsNumImages: false,
928
+ supportsResolution: false,
929
+ type: "utility"
780
930
  },
781
931
  crystal: {
782
- name: "Crystal Upscaler",
783
932
  endpoint: "clarityai/crystal-upscaler",
784
- type: "utility",
933
+ name: "Crystal Upscaler",
785
934
  pricing: "$0.02",
786
935
  supportsAspect: false,
787
- supportsResolution: false,
788
936
  supportsEdit: false,
789
- supportsNumImages: false
937
+ supportsNumImages: false,
938
+ supportsResolution: false,
939
+ type: "utility"
790
940
  },
791
941
  rmbg: {
792
- name: "BiRefNet (Background Removal)",
793
942
  endpoint: "fal-ai/birefnet",
794
- type: "utility",
943
+ name: "BiRefNet (Background Removal)",
795
944
  pricing: "$0.02",
796
945
  supportsAspect: false,
797
- supportsResolution: false,
798
946
  supportsEdit: false,
799
- supportsNumImages: false
947
+ supportsNumImages: false,
948
+ supportsResolution: false,
949
+ type: "utility"
800
950
  },
801
951
  bria: {
802
- name: "Bria RMBG 2.0",
803
952
  endpoint: "fal-ai/bria/background/remove",
804
- type: "utility",
953
+ name: "Bria RMBG 2.0",
805
954
  pricing: "$0.02",
806
955
  supportsAspect: false,
807
- supportsResolution: false,
808
956
  supportsEdit: false,
809
- supportsNumImages: false
957
+ supportsNumImages: false,
958
+ supportsResolution: false,
959
+ type: "utility"
810
960
  }
811
961
  };
812
962
  var GENERATION_MODELS = [
@@ -818,19 +968,24 @@ var GENERATION_MODELS = [
818
968
  "gemini3",
819
969
  "seedream4",
820
970
  "seedream45",
971
+ "seedream5",
972
+ "seedream5-lite",
821
973
  "flux2-max",
822
974
  "flux2-pro",
823
975
  "flux2-flex",
824
976
  "flux2-dev",
977
+ "flux2-turbo",
825
978
  "flux",
826
979
  "flux-fast",
827
980
  "recraft",
981
+ "recraft4",
828
982
  "ideogram",
983
+ "ideogram4",
829
984
  "grok-image",
830
985
  "qwen"
831
986
  ];
832
987
  var EDIT_CAPABLE_MODELS = GENERATION_MODELS.filter(
833
- (id) => MODELS[id]?.supportsEdit
988
+ (id) => MODELS[id]?.supportsEdit === true
834
989
  );
835
990
  var UTILITY_MODELS = ["clarity", "crystal", "rmbg", "bria"];
836
991
  var VIDEO_MODELS = ["kling"];
@@ -886,6 +1041,9 @@ function estimateCost(model, resolution, numImages = 1) {
886
1041
  if ((model === "banana" || model === "gemini3") && resolution === "4K") {
887
1042
  return configuredPrice * 2 * numImages;
888
1043
  }
1044
+ if (model === "seedream5" && (resolution === "2K" || resolution === "4K")) {
1045
+ return configuredPrice * 2 * numImages;
1046
+ }
889
1047
  if (model === "banana2") {
890
1048
  const multiplier = resolution === "4K" ? 2 : resolution === "2K" ? 1.5 : resolution === "0.5K" ? 0.75 : 1;
891
1049
  return configuredPrice * multiplier * numImages;
@@ -893,29 +1051,40 @@ function estimateCost(model, resolution, numImages = 1) {
893
1051
  return configuredPrice * numImages;
894
1052
  }
895
1053
  switch (model) {
896
- case "gpt2":
1054
+ case "gpt2": {
897
1055
  return 0.211 * numImages;
898
- case "gpt":
1056
+ }
1057
+ case "gpt": {
899
1058
  return 0.133 * numImages;
1059
+ }
900
1060
  case "banana":
901
- case "gemini3":
1061
+ case "gemini3": {
902
1062
  return (resolution === "4K" ? 0.3 : 0.15) * numImages;
903
- case "gemini":
1063
+ }
1064
+ case "gemini": {
904
1065
  return 0.039 * numImages;
1066
+ }
905
1067
  case "flux":
906
- case "ideogram":
1068
+ case "ideogram": {
907
1069
  return 0.06 * numImages;
908
- case "recraft":
1070
+ }
1071
+ case "recraft": {
909
1072
  return 0.04 * numImages;
910
- case "flux-fast":
1073
+ }
1074
+ case "flux-fast": {
911
1075
  return 3e-3 * numImages;
912
- case "clarity":
1076
+ }
1077
+ case "clarity": {
1078
+ return 0.03 * numImages;
1079
+ }
913
1080
  case "crystal":
914
1081
  case "rmbg":
915
- case "bria":
1082
+ case "bria": {
916
1083
  return 0.02 * numImages;
917
- default:
1084
+ }
1085
+ default: {
918
1086
  return 0.1 * numImages;
1087
+ }
919
1088
  }
920
1089
  }
921
1090
  function estimateVideoCost(durationSeconds = 5, generateAudio = true) {
@@ -924,7 +1093,7 @@ function estimateVideoCost(durationSeconds = 5, generateAudio = true) {
924
1093
  }
925
1094
 
926
1095
  // src/creative.ts
927
- var CONTROL_CHAR_REGEX = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g;
1096
+ var CONTROL_CHAR_REGEX = /[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g;
928
1097
  var CreativeOptionError = class extends Error {
929
1098
  availableIds;
930
1099
  code = "INVALID_OPTION";
@@ -951,28 +1120,20 @@ var CREATIVE_FIELDS = [
951
1120
  "motion"
952
1121
  ];
953
1122
  var CREATIVE_TAXONOMY = {
954
- recipe: [
955
- {
956
- clause: "cinematic scene",
957
- description: "Frames the prompt as a cinematic still or scene.",
958
- id: "cinematic",
959
- label: "Cinematic"
960
- }
961
- ],
962
- shot: [
1123
+ camera: [
963
1124
  {
964
- clause: "close-up composition with controlled depth of field",
965
- description: "Tight framing that emphasizes subject detail.",
966
- id: "close-up",
967
- label: "Close-up"
1125
+ clause: "macro product photography with crisp surface detail",
1126
+ description: "Product-oriented macro camera language for close detail.",
1127
+ id: "macro-product",
1128
+ label: "Macro product"
968
1129
  }
969
1130
  ],
970
- lighting: [
1131
+ color: [
971
1132
  {
972
- clause: "rim lighting with defined edge highlights",
973
- description: "Back or side light that separates the subject edge.",
974
- id: "rim",
975
- label: "Rim"
1133
+ clause: "monochrome palette with tonal contrast",
1134
+ description: "Black-and-white or single-channel tonal treatment.",
1135
+ id: "monochrome",
1136
+ label: "Monochrome"
976
1137
  }
977
1138
  ],
978
1139
  genre: [
@@ -983,20 +1144,12 @@ var CREATIVE_TAXONOMY = {
983
1144
  label: "Film noir"
984
1145
  }
985
1146
  ],
986
- camera: [
987
- {
988
- clause: "macro product photography with crisp surface detail",
989
- description: "Product-oriented macro camera language for close detail.",
990
- id: "macro-product",
991
- label: "Macro product"
992
- }
993
- ],
994
- color: [
1147
+ lighting: [
995
1148
  {
996
- clause: "monochrome palette with tonal contrast",
997
- description: "Black-and-white or single-channel tonal treatment.",
998
- id: "monochrome",
999
- label: "Monochrome"
1149
+ clause: "rim lighting with defined edge highlights",
1150
+ description: "Back or side light that separates the subject edge.",
1151
+ id: "rim",
1152
+ label: "Rim"
1000
1153
  }
1001
1154
  ],
1002
1155
  material: [
@@ -1014,10 +1167,26 @@ var CREATIVE_TAXONOMY = {
1014
1167
  id: "still",
1015
1168
  label: "Still"
1016
1169
  }
1170
+ ],
1171
+ recipe: [
1172
+ {
1173
+ clause: "cinematic scene",
1174
+ description: "Frames the prompt as a cinematic still or scene.",
1175
+ id: "cinematic",
1176
+ label: "Cinematic"
1177
+ }
1178
+ ],
1179
+ shot: [
1180
+ {
1181
+ clause: "close-up composition with controlled depth of field",
1182
+ description: "Tight framing that emphasizes subject detail.",
1183
+ id: "close-up",
1184
+ label: "Close-up"
1185
+ }
1017
1186
  ]
1018
1187
  };
1019
1188
  function sanitizePrompt(prompt) {
1020
- return prompt.replace(CONTROL_CHAR_REGEX, "").replace(/\r\n/g, "\n").trim();
1189
+ return prompt.replace(CONTROL_CHAR_REGEX, "").replaceAll("\r\n", "\n").trim();
1021
1190
  }
1022
1191
  function enrichPrompt(options) {
1023
1192
  const basePrompt = sanitizePrompt(options.prompt);
@@ -1025,7 +1194,7 @@ function enrichPrompt(options) {
1025
1194
  const selected = {};
1026
1195
  for (const field of CREATIVE_FIELDS) {
1027
1196
  const optionId = options.creative?.[field];
1028
- if (!optionId) {
1197
+ if (optionId === void 0 || optionId === "") {
1029
1198
  continue;
1030
1199
  }
1031
1200
  const option = CREATIVE_TAXONOMY[field].find(
@@ -1100,11 +1269,11 @@ function normalizeImageSize(imageSize, sizeMode) {
1100
1269
  if (FAL_IMAGE_SIZE_PRESETS.includes(imageSize)) {
1101
1270
  return imageSize;
1102
1271
  }
1103
- const match = imageSize.match(/^(\d+)x(\d+)$/);
1272
+ const match = /^(\d+)x(\d+)$/.exec(imageSize);
1104
1273
  if (match) {
1105
1274
  const width = Number(match[1]);
1106
1275
  const height = Number(match[2]);
1107
- return { width, height };
1276
+ return { height, width };
1108
1277
  }
1109
1278
  throw new Error(
1110
1279
  `fal image_size must be one of ${FAL_IMAGE_SIZE_PRESETS.join(", ")} or WIDTHxHEIGHT`
@@ -1114,12 +1283,12 @@ function unsupported(config, option) {
1114
1283
  throw new Error(`${config.name} does not support ${option}`);
1115
1284
  }
1116
1285
  function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
1117
- if (options.editImageUrls?.length && config.maxReferenceImages !== void 0 && options.editImageUrls.length > config.maxReferenceImages) {
1286
+ if (options.editImageUrls !== void 0 && options.editImageUrls.length > 0 && config.maxReferenceImages !== void 0 && options.editImageUrls.length > config.maxReferenceImages) {
1118
1287
  throw new Error(
1119
1288
  `${config.name} supports at most ${config.maxReferenceImages} reference images`
1120
1289
  );
1121
1290
  }
1122
- if (options.editImageUrls?.length && !config.supportsEdit) {
1291
+ if (options.editImageUrls !== void 0 && options.editImageUrls.length > 0 && !config.supportsEdit) {
1123
1292
  unsupported(config, "image editing");
1124
1293
  }
1125
1294
  if (options.aspect !== void 0 && sizeMode === "none") {
@@ -1131,10 +1300,10 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
1131
1300
  if (options.numImages !== void 0 && options.numImages !== 1 && !config.supportsNumImages) {
1132
1301
  unsupported(config, "numImages");
1133
1302
  }
1134
- if (options.background !== void 0 && !config.supportsBackground) {
1303
+ if (options.background !== void 0 && config.supportsBackground !== true) {
1135
1304
  unsupported(config, "background");
1136
1305
  }
1137
- if (options.transparent && !(config.supportsBackground || config.supportsOutputFormat)) {
1306
+ if (options.transparent === true && !(config.supportsBackground === true || config.supportsOutputFormat === true)) {
1138
1307
  unsupported(config, "transparent output");
1139
1308
  }
1140
1309
  if (options.inputFidelity !== void 0) {
@@ -1148,70 +1317,70 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
1148
1317
  if (options.imageSize !== void 0 && sizeMode !== "gpt_size" && sizeMode !== "image_size_enum") {
1149
1318
  unsupported(config, "imageSize");
1150
1319
  }
1151
- if (options.imagePromptStrength !== void 0 && !config.supportsImagePromptStrength) {
1320
+ if (options.imagePromptStrength !== void 0 && config.supportsImagePromptStrength !== true) {
1152
1321
  unsupported(config, "imagePromptStrength");
1153
1322
  }
1154
1323
  if (options.imagePromptStrength !== void 0 && !hasEditImages) {
1155
1324
  throw new Error("imagePromptStrength requires editImageUrls");
1156
1325
  }
1157
- if (options.maskImageUrl !== void 0 && !config.supportsMaskImage) {
1326
+ if (options.maskImageUrl !== void 0 && config.supportsMaskImage !== true) {
1158
1327
  unsupported(config, "maskImageUrl");
1159
1328
  }
1160
1329
  if (options.maskImageUrl !== void 0 && !hasEditImages) {
1161
1330
  throw new Error("maskImageUrl requires editImageUrls");
1162
1331
  }
1163
- if (options.seed !== void 0 && !config.supportsSeed) {
1332
+ if (options.seed !== void 0 && config.supportsSeed !== true) {
1164
1333
  unsupported(config, "seed");
1165
1334
  }
1166
- if (options.outputFormat !== void 0 && !config.supportsOutputFormat) {
1335
+ if (options.outputFormat !== void 0 && config.supportsOutputFormat !== true) {
1167
1336
  unsupported(config, "outputFormat");
1168
1337
  }
1169
- if (options.quality !== void 0 && !config.supportsQuality) {
1338
+ if (options.quality !== void 0 && config.supportsQuality !== true) {
1170
1339
  unsupported(config, "quality");
1171
1340
  }
1172
- if (options.negativePrompt !== void 0 && !config.supportsNegativePrompt) {
1341
+ if (options.negativePrompt !== void 0 && config.supportsNegativePrompt !== true) {
1173
1342
  unsupported(config, "negativePrompt");
1174
1343
  }
1175
- if (options.style !== void 0 && !config.supportsStyle) {
1344
+ if (options.style !== void 0 && config.supportsStyle !== true) {
1176
1345
  unsupported(config, "style");
1177
1346
  }
1178
- if (options.syncMode !== void 0 && !config.supportsSyncMode) {
1347
+ if (options.syncMode !== void 0 && config.supportsSyncMode !== true) {
1179
1348
  unsupported(config, "syncMode");
1180
1349
  }
1181
- if (options.renderingSpeed !== void 0 && !config.supportsRenderingSpeed) {
1350
+ if (options.renderingSpeed !== void 0 && config.supportsRenderingSpeed !== true) {
1182
1351
  unsupported(config, "renderingSpeed");
1183
1352
  }
1184
- if (options.guidanceScale !== void 0 && !config.supportsGuidanceScale) {
1353
+ if (options.guidanceScale !== void 0 && config.supportsGuidanceScale !== true) {
1185
1354
  unsupported(config, "guidanceScale");
1186
1355
  }
1187
- if (options.numInferenceSteps !== void 0 && !config.supportsInferenceSteps) {
1356
+ if (options.numInferenceSteps !== void 0 && config.supportsInferenceSteps !== true) {
1188
1357
  unsupported(config, "numInferenceSteps");
1189
1358
  }
1190
- if (options.raw !== void 0 && !config.supportsRaw) {
1359
+ if (options.raw !== void 0 && config.supportsRaw !== true) {
1191
1360
  unsupported(config, "raw");
1192
1361
  }
1193
- if (options.enhancePrompt !== void 0 && !config.supportsEnhancePrompt) {
1362
+ if (options.enhancePrompt !== void 0 && config.supportsEnhancePrompt !== true) {
1194
1363
  unsupported(config, "enhancePrompt");
1195
1364
  }
1196
- if (options.safetyTolerance !== void 0 && !config.supportsSafetyTolerance) {
1365
+ if (options.safetyTolerance !== void 0 && config.supportsSafetyTolerance !== true) {
1197
1366
  unsupported(config, "safetyTolerance");
1198
1367
  }
1199
- if (options.enableWebSearch !== void 0 && !config.supportsWebSearch) {
1368
+ if (options.enableWebSearch !== void 0 && config.supportsWebSearch !== true) {
1200
1369
  unsupported(config, "enableWebSearch");
1201
1370
  }
1202
- if (options.enableGoogleSearch !== void 0 && !config.supportsGoogleSearch) {
1371
+ if (options.enableGoogleSearch !== void 0 && config.supportsGoogleSearch !== true) {
1203
1372
  unsupported(config, "enableGoogleSearch");
1204
1373
  }
1205
- if (options.enableSafetyChecker !== void 0 && !config.supportsSafetyChecker) {
1374
+ if (options.enableSafetyChecker !== void 0 && config.supportsSafetyChecker !== true) {
1206
1375
  unsupported(config, "enableSafetyChecker");
1207
1376
  }
1208
- if (options.limitGenerations !== void 0 && !config.supportsLimitGenerations) {
1377
+ if (options.limitGenerations !== void 0 && config.supportsLimitGenerations !== true) {
1209
1378
  unsupported(config, "limitGenerations");
1210
1379
  }
1211
- if (options.thinkingLevel !== void 0 && !config.supportsThinkingLevel) {
1380
+ if (options.thinkingLevel !== void 0 && config.supportsThinkingLevel !== true) {
1212
1381
  unsupported(config, "thinkingLevel");
1213
1382
  }
1214
- if (options.expandPrompt !== void 0 && !config.supportsExpandPrompt) {
1383
+ if (options.expandPrompt !== void 0 && config.supportsExpandPrompt !== true) {
1215
1384
  unsupported(config, "expandPrompt");
1216
1385
  }
1217
1386
  }
@@ -1253,8 +1422,8 @@ function buildGenerateBody(options) {
1253
1422
  if (!config) {
1254
1423
  throw new Error(`Unknown model: ${model}`);
1255
1424
  }
1256
- let endpoint = config.endpoint;
1257
- const enrichedPrompt = creative ? enrichPrompt({ prompt, creative }).prompt : prompt;
1425
+ let { endpoint } = config;
1426
+ const enrichedPrompt = creative ? enrichPrompt({ creative, prompt }).prompt : prompt;
1258
1427
  const body = { prompt: enrichedPrompt };
1259
1428
  const hasEditImages = Boolean(editImageUrls?.length);
1260
1429
  const editImages = editImageUrls ?? [];
@@ -1263,13 +1432,15 @@ function buildGenerateBody(options) {
1263
1432
  validateGenerateOptions(options, config, sizeMode, hasEditImages);
1264
1433
  const normalizedImageSize = normalizeImageSize(imageSize, sizeMode);
1265
1434
  switch (sizeMode) {
1266
- case "gpt_size":
1435
+ case "gpt_size": {
1267
1436
  body.image_size = normalizedImageSize ?? (hasEditImages && !explicitAspect ? "auto" : aspectToGptSize(aspect));
1268
1437
  break;
1269
- case "image_size_enum":
1438
+ }
1439
+ case "image_size_enum": {
1270
1440
  body.image_size = normalizedImageSize ?? aspectToFalImageSize(aspect);
1271
1441
  break;
1272
- case "aspect_ratio":
1442
+ }
1443
+ case "aspect_ratio": {
1273
1444
  if (config.supportsAspect) {
1274
1445
  body.aspect_ratio = aspect;
1275
1446
  }
@@ -1277,26 +1448,30 @@ function buildGenerateBody(options) {
1277
1448
  body.resolution = model === "grok-image" ? resolution.toLowerCase() : resolution;
1278
1449
  }
1279
1450
  break;
1451
+ }
1452
+ case "none": {
1453
+ break;
1454
+ }
1280
1455
  }
1281
- if (config.supportsQuality) {
1456
+ if (config.supportsQuality === true) {
1282
1457
  body.quality = quality ?? "high";
1283
1458
  }
1284
- if (config.supportsBackground && background) {
1459
+ if (config.supportsBackground === true && background) {
1285
1460
  body.background = background;
1286
1461
  }
1287
- if (config.supportsBackground && transparent) {
1462
+ if (config.supportsBackground === true && transparent === true) {
1288
1463
  body.background = "transparent";
1289
1464
  }
1290
- if (transparent && config.supportsOutputFormat) {
1465
+ if (transparent === true && config.supportsOutputFormat === true) {
1291
1466
  body.output_format = "png";
1292
1467
  }
1293
- if (syncMode !== void 0 && config.supportsSyncMode) {
1468
+ if (syncMode !== void 0 && config.supportsSyncMode === true) {
1294
1469
  body.sync_mode = syncMode;
1295
1470
  }
1296
- if (seed !== void 0 && config.supportsSeed) {
1471
+ if (seed !== void 0 && config.supportsSeed === true) {
1297
1472
  body.seed = seed;
1298
1473
  }
1299
- if (outputFormat && config.supportsOutputFormat && !transparent) {
1474
+ if (outputFormat && config.supportsOutputFormat === true && transparent !== true) {
1300
1475
  if (config.supportedOutputFormats && !config.supportedOutputFormats.includes(outputFormat)) {
1301
1476
  throw new Error(
1302
1477
  `${config.name} supports output formats: ${config.supportedOutputFormats.join(", ")}`
@@ -1304,62 +1479,78 @@ function buildGenerateBody(options) {
1304
1479
  }
1305
1480
  body.output_format = outputFormat;
1306
1481
  }
1307
- if (config.supportsRaw && raw !== void 0) {
1482
+ if (config.supportsRaw === true && raw !== void 0) {
1308
1483
  body.raw = raw;
1309
1484
  }
1310
- if (config.supportsEnhancePrompt && enhancePrompt !== void 0) {
1485
+ if (config.supportsEnhancePrompt === true && enhancePrompt !== void 0) {
1311
1486
  body.enhance_prompt = enhancePrompt;
1312
1487
  }
1313
- if (config.supportsImagePromptStrength && imagePromptStrength !== void 0) {
1488
+ if (config.supportsImagePromptStrength === true && imagePromptStrength !== void 0) {
1314
1489
  body.image_prompt_strength = imagePromptStrength;
1315
1490
  }
1316
1491
  if (model === "flux") {
1317
- if (safetyTolerance) body.safety_tolerance = safetyTolerance;
1492
+ if (safetyTolerance !== void 0 && safetyTolerance !== "") {
1493
+ body.safety_tolerance = safetyTolerance;
1494
+ }
1318
1495
  }
1319
- if (config.supportsGuidanceScale && guidanceScale !== void 0) {
1496
+ if (config.supportsGuidanceScale === true && guidanceScale !== void 0) {
1320
1497
  body.guidance_scale = guidanceScale;
1321
1498
  }
1322
- if (config.supportsInferenceSteps && numInferenceSteps !== void 0) {
1499
+ if (config.supportsInferenceSteps === true && numInferenceSteps !== void 0) {
1323
1500
  body.num_inference_steps = numInferenceSteps;
1324
1501
  }
1325
- if (config.supportsSafetyTolerance) {
1326
- if (safetyTolerance) body.safety_tolerance = safetyTolerance;
1502
+ if (config.supportsSafetyTolerance === true) {
1503
+ if (safetyTolerance !== void 0 && safetyTolerance !== "") {
1504
+ body.safety_tolerance = safetyTolerance;
1505
+ }
1327
1506
  }
1328
- if (config.supportsWebSearch) {
1329
- if (enableWebSearch !== void 0) body.enable_web_search = enableWebSearch;
1507
+ if (config.supportsWebSearch === true) {
1508
+ if (enableWebSearch !== void 0) {
1509
+ body.enable_web_search = enableWebSearch;
1510
+ }
1330
1511
  }
1331
- if (config.supportsGoogleSearch) {
1512
+ if (config.supportsGoogleSearch === true) {
1332
1513
  if (enableGoogleSearch !== void 0) {
1333
1514
  body.enable_google_search = enableGoogleSearch;
1334
1515
  }
1335
1516
  }
1336
- if (config.supportsSafetyChecker) {
1517
+ if (config.supportsSafetyChecker === true) {
1337
1518
  if (enableSafetyChecker !== void 0) {
1338
1519
  body.enable_safety_checker = enableSafetyChecker;
1339
1520
  }
1340
1521
  }
1341
- if (config.supportsLimitGenerations) {
1522
+ if (config.supportsLimitGenerations === true) {
1342
1523
  if (limitGenerations !== void 0) {
1343
1524
  body.limit_generations = limitGenerations;
1344
1525
  }
1345
1526
  }
1346
- if (config.supportsThinkingLevel) {
1347
- if (thinkingLevel) body.thinking_level = thinkingLevel;
1527
+ if (config.supportsThinkingLevel === true) {
1528
+ if (thinkingLevel) {
1529
+ body.thinking_level = thinkingLevel;
1530
+ }
1348
1531
  }
1349
- if (config.supportsStyle && style) {
1532
+ if (config.supportsStyle === true && style !== void 0 && style !== "") {
1350
1533
  body.style = style;
1351
1534
  }
1352
- if (config.supportsNegativePrompt) {
1353
- if (negativePrompt) body.negative_prompt = negativePrompt;
1535
+ if (config.supportsNegativePrompt === true) {
1536
+ if (negativePrompt !== void 0 && negativePrompt !== "") {
1537
+ body.negative_prompt = negativePrompt;
1538
+ }
1354
1539
  }
1355
1540
  if (model === "ideogram") {
1356
- if (style) body.style = style;
1541
+ if (style !== void 0 && style !== "") {
1542
+ body.style = style;
1543
+ }
1357
1544
  }
1358
- if (config.supportsRenderingSpeed) {
1359
- if (renderingSpeed) body.rendering_speed = renderingSpeed;
1545
+ if (config.supportsRenderingSpeed === true) {
1546
+ if (renderingSpeed) {
1547
+ body.rendering_speed = renderingSpeed;
1548
+ }
1360
1549
  }
1361
- if (config.supportsExpandPrompt) {
1362
- if (expandPrompt !== void 0) body.expand_prompt = expandPrompt;
1550
+ if (config.supportsExpandPrompt === true) {
1551
+ if (expandPrompt !== void 0) {
1552
+ body.expand_prompt = expandPrompt;
1553
+ }
1363
1554
  }
1364
1555
  if (model === "flux" && hasEditImages) {
1365
1556
  body.image_url = editImages[0];
@@ -1386,959 +1577,1045 @@ function buildGenerateBody(options) {
1386
1577
  body.input_fidelity = inputFidelity;
1387
1578
  }
1388
1579
  }
1389
- if (maskImageUrl && config.supportsMaskImage) {
1580
+ if (maskImageUrl !== void 0 && maskImageUrl !== "" && config.supportsMaskImage === true) {
1390
1581
  body.mask_image_url = maskImageUrl;
1391
1582
  }
1392
- return { endpoint, body };
1583
+ return { body, endpoint };
1393
1584
  }
1394
1585
 
1395
1586
  // src/leaderboards.ts
1396
1587
  var IMAGE_TEXT_TO_IMAGE_TOP_20 = {
1397
1588
  capturedAt: "2026-05-12",
1398
- source: "https://artificialanalysis.ai/image/leaderboard/text-to-image",
1399
1589
  entries: [
1400
1590
  {
1401
- rank: 1,
1591
+ apiPricing: "$211/1k images",
1402
1592
  creator: "OpenAI",
1403
- model: "GPT Image 2 (high)",
1404
1593
  elo: 1337,
1405
- apiPricing: "$211/1k images",
1406
1594
  isCurrent: true,
1595
+ model: "GPT Image 2 (high)",
1596
+ rank: 1,
1407
1597
  routableModelId: "gpt2"
1408
1598
  },
1409
1599
  {
1410
- rank: 2,
1600
+ apiPricing: "$133/1k images",
1411
1601
  creator: "OpenAI",
1412
- model: "GPT Image 1.5 (high)",
1413
1602
  elo: 1268,
1414
- apiPricing: "$133/1k images",
1415
1603
  isCurrent: true,
1604
+ model: "GPT Image 1.5 (high)",
1605
+ rank: 2,
1416
1606
  routableModelId: "gpt"
1417
1607
  },
1418
1608
  {
1419
- rank: 3,
1609
+ apiPricing: "$67/1k images",
1420
1610
  creator: "Google",
1421
- model: "Nano Banana 2 (Gemini 3.1 Flash Image Preview)",
1422
1611
  elo: 1263,
1423
- apiPricing: "$67/1k images",
1424
1612
  isCurrent: true,
1425
- routableModelId: "banana2"
1613
+ model: "Nano Banana 2 (Gemini 3.1 Flash Image Preview)",
1614
+ rank: 3,
1615
+ routableModelId: "banana2"
1426
1616
  },
1427
1617
  {
1428
- rank: 4,
1618
+ apiPricing: "$150/1k images",
1429
1619
  creator: "Sourceful",
1430
- model: "Riverflow 2.0",
1431
1620
  elo: 1256,
1432
- apiPricing: "$150/1k images",
1433
- isCurrent: true
1621
+ isCurrent: true,
1622
+ model: "Riverflow 2.0",
1623
+ rank: 4
1434
1624
  },
1435
1625
  {
1436
- rank: 5,
1626
+ apiPricing: "$134/1k images",
1437
1627
  creator: "Google",
1438
- model: "Nano Banana Pro (Gemini 3 Pro Image)",
1439
1628
  elo: 1220,
1440
- apiPricing: "$134/1k images",
1441
1629
  isCurrent: true,
1630
+ model: "Nano Banana Pro (Gemini 3 Pro Image)",
1631
+ rank: 5,
1442
1632
  routableModelId: "banana"
1443
1633
  },
1444
1634
  {
1445
- rank: 6,
1635
+ apiPricing: "$30/1k images",
1446
1636
  creator: "ByteDance Seed",
1447
- model: "Seedream 4.0",
1448
1637
  elo: 1198,
1449
- apiPricing: "$30/1k images",
1450
1638
  isCurrent: true,
1639
+ model: "Seedream 4.0",
1640
+ rank: 6,
1451
1641
  routableModelId: "seedream4"
1452
1642
  },
1453
1643
  {
1454
- rank: 7,
1644
+ apiPricing: "$50/1k images",
1455
1645
  creator: "Microsoft",
1456
- model: "MAI-Image-2",
1457
1646
  elo: 1198,
1458
- apiPricing: "$50/1k images",
1459
- isCurrent: true
1647
+ isCurrent: true,
1648
+ model: "MAI-Image-2",
1649
+ rank: 7
1460
1650
  },
1461
1651
  {
1462
- rank: 8,
1652
+ apiPricing: "$70/1k images",
1463
1653
  creator: "Black Forest Labs",
1464
- model: "FLUX.2 [max]",
1465
1654
  elo: 1197,
1466
- apiPricing: "$70/1k images",
1467
1655
  isCurrent: true,
1656
+ model: "FLUX.2 [max]",
1657
+ rank: 8,
1468
1658
  routableModelId: "flux2-max"
1469
1659
  },
1470
1660
  {
1471
- rank: 9,
1661
+ apiPricing: "Unavailable",
1472
1662
  creator: "Peanut",
1473
- model: "Peanut (Open Weights Coming Soon)",
1474
1663
  elo: 1187,
1475
- apiPricing: "Unavailable",
1476
- isCurrent: true
1664
+ isCurrent: true,
1665
+ model: "Peanut (Open Weights Coming Soon)",
1666
+ rank: 9
1477
1667
  },
1478
1668
  {
1479
- rank: 10,
1669
+ apiPricing: "$30/1k images",
1480
1670
  creator: "Black Forest Labs",
1481
- model: "FLUX.2 [pro]",
1482
1671
  elo: 1186,
1483
- apiPricing: "$30/1k images",
1484
1672
  isCurrent: true,
1673
+ model: "FLUX.2 [pro]",
1674
+ rank: 10,
1485
1675
  routableModelId: "flux2-pro"
1486
1676
  },
1487
1677
  {
1488
- rank: 11,
1678
+ apiPricing: "$60/1k images",
1489
1679
  creator: "Google",
1490
- model: "Imagen 4 Ultra Preview 0606",
1491
1680
  elo: 1184,
1492
- apiPricing: "$60/1k images",
1493
- isCurrent: false
1681
+ isCurrent: false,
1682
+ model: "Imagen 4 Ultra Preview 0606",
1683
+ rank: 11
1494
1684
  },
1495
1685
  {
1496
- rank: 12,
1686
+ apiPricing: "$20/1k images",
1497
1687
  creator: "xAI",
1498
- model: "grok-imagine-image",
1499
1688
  elo: 1182,
1500
- apiPricing: "$20/1k images",
1501
1689
  isCurrent: true,
1690
+ model: "grok-imagine-image",
1691
+ rank: 12,
1502
1692
  routableModelId: "grok-image"
1503
1693
  },
1504
1694
  {
1505
- rank: 13,
1695
+ apiPricing: "$60/1k images",
1506
1696
  creator: "Black Forest Labs",
1507
- model: "FLUX.2 [flex]",
1508
1697
  elo: 1182,
1509
- apiPricing: "$60/1k images",
1510
1698
  isCurrent: true,
1699
+ model: "FLUX.2 [flex]",
1700
+ rank: 13,
1511
1701
  routableModelId: "flux2-flex"
1512
1702
  },
1513
1703
  {
1514
- rank: 14,
1704
+ apiPricing: "$30/1k images",
1515
1705
  creator: "ImagineArt",
1516
- model: "ImagineArt 2.0",
1517
1706
  elo: 1181,
1518
- apiPricing: "$30/1k images",
1519
- isCurrent: true
1707
+ isCurrent: true,
1708
+ model: "ImagineArt 2.0",
1709
+ rank: 14
1520
1710
  },
1521
1711
  {
1522
- rank: 15,
1712
+ apiPricing: "$60/1k images",
1523
1713
  creator: "Google",
1524
- model: "Imagen 4 Ultra",
1525
1714
  elo: 1171,
1526
- apiPricing: "$60/1k images",
1527
- isCurrent: true
1715
+ isCurrent: true,
1716
+ model: "Imagen 4 Ultra",
1717
+ rank: 15
1528
1718
  },
1529
1719
  {
1530
- rank: 16,
1720
+ apiPricing: "$40/1k images",
1531
1721
  creator: "Google",
1532
- model: "Imagen 4 Preview 0606",
1533
1722
  elo: 1169,
1534
- apiPricing: "$40/1k images",
1535
- isCurrent: false
1723
+ isCurrent: false,
1724
+ model: "Imagen 4 Preview 0606",
1725
+ rank: 16
1536
1726
  },
1537
1727
  {
1538
- rank: 17,
1728
+ apiPricing: "$40/1k images",
1539
1729
  creator: "ByteDance Seed",
1540
- model: "Seedream 4.5",
1541
1730
  elo: 1167,
1542
- apiPricing: "$40/1k images",
1543
1731
  isCurrent: true,
1732
+ model: "Seedream 4.5",
1733
+ rank: 17,
1544
1734
  routableModelId: "seedream45"
1545
1735
  },
1546
1736
  {
1547
- rank: 18,
1737
+ apiPricing: "$8/1k images",
1548
1738
  creator: "Fal",
1549
- model: "FLUX.2 [dev] Turbo",
1550
1739
  elo: 1161,
1551
- apiPricing: "$8/1k images",
1552
- isCurrent: true
1740
+ isCurrent: true,
1741
+ model: "FLUX.2 [dev] Turbo",
1742
+ rank: 18
1553
1743
  },
1554
1744
  {
1555
- rank: 19,
1745
+ apiPricing: "$12/1k images",
1556
1746
  creator: "Black Forest Labs",
1557
- model: "FLUX.2 [dev]",
1558
1747
  elo: 1160,
1559
- apiPricing: "$12/1k images",
1560
1748
  isCurrent: true,
1749
+ model: "FLUX.2 [dev]",
1750
+ rank: 19,
1561
1751
  routableModelId: "flux2-dev"
1562
1752
  },
1563
1753
  {
1564
- rank: 20,
1754
+ apiPricing: "$20/1k images",
1565
1755
  creator: "Alibaba",
1566
- model: "Qwen Image Max 2512",
1567
1756
  elo: 1158,
1568
- apiPricing: "$20/1k images",
1569
1757
  isCurrent: true,
1758
+ model: "Qwen Image Max 2512",
1759
+ rank: 20,
1570
1760
  routableModelId: "qwen"
1571
1761
  }
1572
- ]
1762
+ ],
1763
+ source: "https://artificialanalysis.ai/image/leaderboard/text-to-image"
1573
1764
  };
1574
1765
  var IMAGE_EDITING_TOP_20 = {
1575
1766
  capturedAt: "2026-05-12",
1576
- source: "https://artificialanalysis.ai/image/leaderboard/editing",
1577
1767
  entries: [
1578
1768
  {
1579
- rank: 1,
1769
+ apiPricing: "$150/1k images",
1580
1770
  creator: "Sourceful",
1581
- model: "Riverflow 2.0",
1582
1771
  elo: 1286,
1583
- apiPricing: "$150/1k images",
1584
- isCurrent: true
1772
+ isCurrent: true,
1773
+ model: "Riverflow 2.0",
1774
+ rank: 1
1585
1775
  },
1586
1776
  {
1587
- rank: 2,
1777
+ apiPricing: "$133/1k images",
1588
1778
  creator: "OpenAI",
1589
- model: "GPT Image 1.5 (high)",
1590
1779
  elo: 1262,
1591
- apiPricing: "$133/1k images",
1592
1780
  isCurrent: true,
1781
+ model: "GPT Image 1.5 (high)",
1782
+ rank: 2,
1593
1783
  routableModelId: "gpt"
1594
1784
  },
1595
1785
  {
1596
- rank: 3,
1786
+ apiPricing: "$211/1k images",
1597
1787
  creator: "OpenAI",
1598
- model: "GPT Image 2 (high)",
1599
1788
  elo: 1249,
1600
- apiPricing: "$211/1k images",
1601
1789
  isCurrent: true,
1790
+ model: "GPT Image 2 (high)",
1791
+ rank: 3,
1602
1792
  routableModelId: "gpt2"
1603
1793
  },
1604
1794
  {
1605
- rank: 4,
1795
+ apiPricing: "$134/1k images",
1606
1796
  creator: "Google",
1607
- model: "Nano Banana Pro (Gemini 3 Pro Image)",
1608
1797
  elo: 1241,
1609
- apiPricing: "$134/1k images",
1610
1798
  isCurrent: true,
1799
+ model: "Nano Banana Pro (Gemini 3 Pro Image)",
1800
+ rank: 4,
1611
1801
  routableModelId: "banana"
1612
1802
  },
1613
1803
  {
1614
- rank: 5,
1804
+ apiPricing: "$67/1k images",
1615
1805
  creator: "Google",
1616
- model: "Nano Banana 2 (Gemini 3.1 Flash Image Preview)",
1617
1806
  elo: 1231,
1618
- apiPricing: "$67/1k images",
1619
1807
  isCurrent: true,
1808
+ model: "Nano Banana 2 (Gemini 3.1 Flash Image Preview)",
1809
+ rank: 5,
1620
1810
  routableModelId: "banana2"
1621
1811
  },
1622
1812
  {
1623
- rank: 6,
1813
+ apiPricing: "$90/1k images",
1624
1814
  creator: "Tencent",
1625
- model: "HunyuanImage 3.0 Instruct (Fal)",
1626
1815
  elo: 1222,
1627
- apiPricing: "$90/1k images",
1628
- isCurrent: true
1816
+ isCurrent: true,
1817
+ model: "HunyuanImage 3.0 Instruct (Fal)",
1818
+ rank: 6
1629
1819
  },
1630
1820
  {
1631
- rank: 7,
1821
+ apiPricing: "$20/1k images",
1632
1822
  creator: "xAI",
1633
- model: "grok-imagine-image",
1634
1823
  elo: 1213,
1635
- apiPricing: "$20/1k images",
1636
1824
  isCurrent: true,
1825
+ model: "grok-imagine-image",
1826
+ rank: 7,
1637
1827
  routableModelId: "grok-image"
1638
1828
  },
1639
1829
  {
1640
- rank: 8,
1830
+ apiPricing: "$70/1k images",
1641
1831
  creator: "xAI",
1642
- model: "grok-imagine-image-pro",
1643
1832
  elo: 1212,
1644
- apiPricing: "$70/1k images",
1645
- isCurrent: true
1833
+ isCurrent: true,
1834
+ model: "grok-imagine-image-pro",
1835
+ rank: 8
1646
1836
  },
1647
1837
  {
1648
- rank: 9,
1838
+ apiPricing: "Unavailable",
1649
1839
  creator: "KlingAI",
1650
- model: "Kling Image 3.0 Omni",
1651
1840
  elo: 1207,
1652
- apiPricing: "Unavailable",
1653
- isCurrent: true
1841
+ isCurrent: true,
1842
+ model: "Kling Image 3.0 Omni",
1843
+ rank: 9
1654
1844
  },
1655
1845
  {
1656
- rank: 10,
1846
+ apiPricing: "$140/1k images",
1657
1847
  creator: "Black Forest Labs",
1658
- model: "FLUX.2 [max]",
1659
1848
  elo: 1206,
1660
- apiPricing: "$140/1k images",
1661
1849
  isCurrent: true,
1850
+ model: "FLUX.2 [max]",
1851
+ rank: 10,
1662
1852
  routableModelId: "flux2-max"
1663
1853
  },
1664
1854
  {
1665
- rank: 11,
1855
+ apiPricing: "Unavailable",
1666
1856
  creator: "Alibaba",
1667
- model: "Wan 2.7 Pro",
1668
1857
  elo: 1200,
1669
- apiPricing: "Unavailable",
1670
- isCurrent: true
1858
+ isCurrent: true,
1859
+ model: "Wan 2.7 Pro",
1860
+ rank: 11
1671
1861
  },
1672
1862
  {
1673
- rank: 12,
1863
+ apiPricing: "$28/1k images",
1674
1864
  creator: "KlingAI",
1675
- model: "Kling Image 3.0",
1676
1865
  elo: 1196,
1677
- apiPricing: "$28/1k images",
1678
- isCurrent: true
1866
+ isCurrent: true,
1867
+ model: "Kling Image 3.0",
1868
+ rank: 12
1679
1869
  },
1680
1870
  {
1681
- rank: 13,
1871
+ apiPricing: "$28/1k images",
1682
1872
  creator: "KlingAI",
1683
- model: "Kling Image O1",
1684
1873
  elo: 1193,
1685
- apiPricing: "$28/1k images",
1686
- isCurrent: true
1874
+ isCurrent: true,
1875
+ model: "Kling Image O1",
1876
+ rank: 13
1687
1877
  },
1688
1878
  {
1689
- rank: 14,
1879
+ apiPricing: "$30/1k images",
1690
1880
  creator: "Alibaba",
1691
- model: "Wan 2.6 Image",
1692
1881
  elo: 1188,
1693
- apiPricing: "$30/1k images",
1694
- isCurrent: true
1882
+ isCurrent: true,
1883
+ model: "Wan 2.6 Image",
1884
+ rank: 14
1695
1885
  },
1696
1886
  {
1697
- rank: 15,
1887
+ apiPricing: "$66/1k images",
1698
1888
  creator: "Sourceful",
1699
- model: "Riverflow 1",
1700
1889
  elo: 1184,
1701
- apiPricing: "$66/1k images",
1702
- isCurrent: true
1890
+ isCurrent: true,
1891
+ model: "Riverflow 1",
1892
+ rank: 15
1703
1893
  },
1704
1894
  {
1705
- rank: 16,
1895
+ apiPricing: "$30/1k images",
1706
1896
  creator: "ByteDance Seed",
1707
- model: "Seedream 4.0",
1708
1897
  elo: 1184,
1709
- apiPricing: "$30/1k images",
1710
1898
  isCurrent: true,
1899
+ model: "Seedream 4.0",
1900
+ rank: 16,
1711
1901
  routableModelId: "seedream4"
1712
1902
  },
1713
1903
  {
1714
- rank: 17,
1904
+ apiPricing: "$40/1k images",
1715
1905
  creator: "ByteDance Seed",
1716
- model: "Seedream 4.5",
1717
1906
  elo: 1184,
1718
- apiPricing: "$40/1k images",
1719
1907
  isCurrent: true,
1908
+ model: "Seedream 4.5",
1909
+ rank: 17,
1720
1910
  routableModelId: "seedream45"
1721
1911
  },
1722
1912
  {
1723
- rank: 18,
1913
+ apiPricing: "$26/1k images",
1724
1914
  creator: "Alibaba",
1725
- model: "Wan 2.7",
1726
1915
  elo: 1181,
1727
- apiPricing: "$26/1k images",
1728
- isCurrent: true
1916
+ isCurrent: true,
1917
+ model: "Wan 2.7",
1918
+ rank: 18
1729
1919
  },
1730
1920
  {
1731
- rank: 19,
1921
+ apiPricing: "$39/1k images",
1732
1922
  creator: "Google",
1733
- model: "Nano Banana (Gemini 2.5 Flash Image)",
1734
1923
  elo: 1173,
1735
- apiPricing: "$39/1k images",
1736
1924
  isCurrent: true,
1925
+ model: "Nano Banana (Gemini 2.5 Flash Image)",
1926
+ rank: 19,
1737
1927
  routableModelId: "gemini"
1738
1928
  },
1739
1929
  {
1740
- rank: 20,
1930
+ apiPricing: "$40/1k images",
1741
1931
  creator: "Reve",
1742
- model: "Reve V1 (December)",
1743
1932
  elo: 1172,
1744
- apiPricing: "$40/1k images",
1745
- isCurrent: true
1933
+ isCurrent: true,
1934
+ model: "Reve V1 (December)",
1935
+ rank: 20
1746
1936
  }
1747
- ]
1937
+ ],
1938
+ source: "https://artificialanalysis.ai/image/leaderboard/editing"
1748
1939
  };
1749
1940
  var VIDEO_TEXT_TO_VIDEO_TOP_15 = {
1750
1941
  capturedAt: "2026-05-12",
1751
- source: "https://artificialanalysis.ai/video/leaderboard/text-to-video",
1752
1942
  entries: [
1753
1943
  {
1754
- rank: 1,
1944
+ apiPricing: "$14.40/min",
1755
1945
  creator: "Alibaba-ATH",
1756
- model: "HappyHorse-1.0",
1757
1946
  elo: 1354,
1758
- apiPricing: "$14.40/min"
1947
+ model: "HappyHorse-1.0",
1948
+ rank: 1
1759
1949
  },
1760
1950
  {
1761
- rank: 2,
1951
+ apiPricing: "No API available",
1762
1952
  creator: "ByteDance Seed",
1763
- model: "Dreamina Seedance 2.0 720p",
1764
1953
  elo: 1273,
1765
- apiPricing: "No API available"
1954
+ model: "Dreamina Seedance 2.0 720p",
1955
+ rank: 2
1766
1956
  },
1767
1957
  {
1768
- rank: 3,
1958
+ apiPricing: "$13.44/min",
1769
1959
  creator: "KlingAI",
1770
- model: "Kling 3.0 1080p (Pro)",
1771
1960
  elo: 1249,
1772
- apiPricing: "$13.44/min"
1961
+ model: "Kling 3.0 1080p (Pro)",
1962
+ rank: 3
1773
1963
  },
1774
1964
  {
1775
- rank: 4,
1965
+ apiPricing: "$13.44/min",
1776
1966
  creator: "KlingAI",
1777
- model: "Kling 3.0 Omni 1080p (Pro)",
1778
1967
  elo: 1233,
1779
- apiPricing: "$13.44/min"
1968
+ model: "Kling 3.0 Omni 1080p (Pro)",
1969
+ rank: 4
1780
1970
  },
1781
1971
  {
1782
- rank: 5,
1972
+ apiPricing: "$4.20/min",
1783
1973
  creator: "xAI",
1784
- model: "grok-imagine-video",
1785
1974
  elo: 1233,
1786
- apiPricing: "$4.20/min"
1975
+ model: "grok-imagine-video",
1976
+ rank: 5
1787
1977
  },
1788
1978
  {
1789
- rank: 6,
1979
+ apiPricing: "$9.60/min",
1790
1980
  creator: "Vidu",
1791
- model: "Vidu Q3 Pro",
1792
1981
  elo: 1225,
1793
- apiPricing: "$9.60/min"
1982
+ model: "Vidu Q3 Pro",
1983
+ rank: 6
1794
1984
  },
1795
1985
  {
1796
- rank: 7,
1986
+ apiPricing: "$3.00/min",
1797
1987
  creator: "Video Rebirth",
1798
- model: "Bach-1.0 Preview",
1799
1988
  elo: 1224,
1800
- apiPricing: "$3.00/min"
1989
+ model: "Bach-1.0 Preview",
1990
+ rank: 7
1801
1991
  },
1802
1992
  {
1803
- rank: 8,
1993
+ apiPricing: "$10.08/min",
1804
1994
  creator: "KlingAI",
1805
- model: "Kling 3.0 Omni 720p (Standard)",
1806
1995
  elo: 1224,
1807
- apiPricing: "$10.08/min"
1996
+ model: "Kling 3.0 Omni 720p (Standard)",
1997
+ rank: 8
1808
1998
  },
1809
1999
  {
1810
- rank: 9,
2000
+ apiPricing: "$5.40/min",
1811
2001
  creator: "PixVerse",
1812
- model: "PixVerse V6",
1813
2002
  elo: 1222,
1814
- apiPricing: "$5.40/min"
2003
+ model: "PixVerse V6",
2004
+ rank: 9
1815
2005
  },
1816
2006
  {
1817
- rank: 10,
2007
+ apiPricing: "$9.00/min",
1818
2008
  creator: "PixVerse",
1819
- model: "PixVerse V5.6",
1820
2009
  elo: 1221,
1821
- apiPricing: "$9.00/min"
2010
+ model: "PixVerse V5.6",
2011
+ rank: 10
1822
2012
  },
1823
2013
  {
1824
- rank: 11,
2014
+ apiPricing: "No API available",
1825
2015
  creator: "Runway",
1826
- model: "Runway Gen-4.5",
1827
2016
  elo: 1220,
1828
- apiPricing: "No API available"
2017
+ model: "Runway Gen-4.5",
2018
+ rank: 11
1829
2019
  },
1830
2020
  {
1831
- rank: 12,
2021
+ apiPricing: "$12.00/min",
1832
2022
  creator: "Google",
1833
- model: "Veo 3",
1834
2023
  elo: 1218,
1835
- apiPricing: "$12.00/min"
2024
+ model: "Veo 3",
2025
+ rank: 12
1836
2026
  },
1837
2027
  {
1838
- rank: 13,
2028
+ apiPricing: "$10.08/min",
1839
2029
  creator: "KlingAI",
1840
- model: "Kling 3.0 720p (Standard)",
1841
2030
  elo: 1215,
1842
- apiPricing: "$10.08/min"
2031
+ model: "Kling 3.0 720p (Standard)",
2032
+ rank: 13
1843
2033
  },
1844
2034
  {
1845
- rank: 14,
2035
+ apiPricing: "$3.00/min",
1846
2036
  creator: "Google",
1847
- model: "Veo 3.1 Lite",
1848
2037
  elo: 1214,
1849
- apiPricing: "$3.00/min"
2038
+ model: "Veo 3.1 Lite",
2039
+ rank: 14
1850
2040
  },
1851
2041
  {
1852
- rank: 15,
2042
+ apiPricing: "$10.08/min",
1853
2043
  creator: "KlingAI",
1854
- model: "Kling O1 Pro (January)",
1855
2044
  elo: 1209,
1856
- apiPricing: "$10.08/min"
2045
+ model: "Kling O1 Pro (January)",
2046
+ rank: 15
1857
2047
  }
1858
- ]
2048
+ ],
2049
+ source: "https://artificialanalysis.ai/video/leaderboard/text-to-video"
1859
2050
  };
1860
2051
  var VIDEO_IMAGE_TO_VIDEO_TOP_15 = {
1861
2052
  capturedAt: "2026-05-12",
1862
- source: "https://artificialanalysis.ai/video/leaderboard/image-to-video",
1863
2053
  entries: [
1864
2054
  {
1865
- rank: 1,
2055
+ apiPricing: "Coming soon",
1866
2056
  creator: "Alibaba-ATH",
1867
- model: "HappyHorse-1.0",
1868
2057
  elo: 1395,
1869
- apiPricing: "Coming soon"
2058
+ model: "HappyHorse-1.0",
2059
+ rank: 1
1870
2060
  },
1871
2061
  {
1872
- rank: 2,
2062
+ apiPricing: "No API available",
1873
2063
  creator: "ByteDance Seed",
1874
- model: "Dreamina Seedance 2.0 720p",
1875
2064
  elo: 1348,
1876
- apiPricing: "No API available"
2065
+ model: "Dreamina Seedance 2.0 720p",
2066
+ rank: 2
1877
2067
  },
1878
2068
  {
1879
- rank: 3,
2069
+ apiPricing: "$4.20/min",
1880
2070
  creator: "xAI",
1881
- model: "grok-imagine-video",
1882
2071
  elo: 1326,
1883
- apiPricing: "$4.20/min"
2072
+ model: "grok-imagine-video",
2073
+ rank: 3
1884
2074
  },
1885
2075
  {
1886
- rank: 4,
2076
+ apiPricing: "$5.40/min",
1887
2077
  creator: "PixVerse",
1888
- model: "PixVerse V6",
1889
2078
  elo: 1322,
1890
- apiPricing: "$5.40/min"
2079
+ model: "PixVerse V6",
2080
+ rank: 4
1891
2081
  },
1892
2082
  {
1893
- rank: 5,
2083
+ apiPricing: "$9.60/min",
1894
2084
  creator: "Vidu",
1895
- model: "Vidu Q3 Pro",
1896
2085
  elo: 1287,
1897
- apiPricing: "$9.60/min"
2086
+ model: "Vidu Q3 Pro",
2087
+ rank: 5
1898
2088
  },
1899
2089
  {
1900
- rank: 6,
2090
+ apiPricing: "$4.20/min",
1901
2091
  creator: "KlingAI",
1902
- model: "Kling 2.5 Turbo 1080p",
1903
2092
  elo: 1283,
1904
- apiPricing: "$4.20/min"
2093
+ model: "Kling 2.5 Turbo 1080p",
2094
+ rank: 6
1905
2095
  },
1906
2096
  {
1907
- rank: 7,
2097
+ apiPricing: "$13.44/min",
1908
2098
  creator: "KlingAI",
1909
- model: "Kling 3.0 1080p (Pro)",
1910
2099
  elo: 1280,
1911
- apiPricing: "$13.44/min"
2100
+ model: "Kling 3.0 1080p (Pro)",
2101
+ rank: 7
1912
2102
  },
1913
2103
  {
1914
- rank: 8,
2104
+ apiPricing: "$9.00/min",
1915
2105
  creator: "PixVerse",
1916
- model: "PixVerse V5.6",
1917
2106
  elo: 1279,
1918
- apiPricing: "$9.00/min"
2107
+ model: "PixVerse V5.6",
2108
+ rank: 8
1919
2109
  },
1920
2110
  {
1921
- rank: 9,
2111
+ apiPricing: "$13.44/min",
1922
2112
  creator: "KlingAI",
1923
- model: "Kling 3.0 Omni 1080p (Pro)",
1924
2113
  elo: 1277,
1925
- apiPricing: "$13.44/min"
2114
+ model: "Kling 3.0 Omni 1080p (Pro)",
2115
+ rank: 9
1926
2116
  },
1927
2117
  {
1928
- rank: 10,
2118
+ apiPricing: "Coming soon",
1929
2119
  creator: "KlingAI",
1930
- model: "Kling 2.6 Standard (January)",
1931
2120
  elo: 1271,
1932
- apiPricing: "Coming soon"
2121
+ model: "Kling 2.6 Standard (January)",
2122
+ rank: 10
1933
2123
  },
1934
2124
  {
1935
- rank: 11,
2125
+ apiPricing: "$6.40/min",
1936
2126
  creator: "PixVerse",
1937
- model: "PixVerse V5.5",
1938
2127
  elo: 1271,
1939
- apiPricing: "$6.40/min"
2128
+ model: "PixVerse V5.5",
2129
+ rank: 11
1940
2130
  },
1941
2131
  {
1942
- rank: 12,
2132
+ apiPricing: "$6.00/min",
1943
2133
  creator: "Google",
1944
- model: "Veo 3.1 Fast",
1945
2134
  elo: 1268,
1946
- apiPricing: "$6.00/min"
2135
+ model: "Veo 3.1 Fast",
2136
+ rank: 12
1947
2137
  },
1948
2138
  {
1949
- rank: 13,
2139
+ apiPricing: "No API available",
1950
2140
  creator: "Runway",
1951
- model: "Runway Gen-4.5",
1952
2141
  elo: 1263,
1953
- apiPricing: "No API available"
2142
+ model: "Runway Gen-4.5",
2143
+ rank: 13
1954
2144
  },
1955
2145
  {
1956
- rank: 14,
2146
+ apiPricing: "$10.08/min",
1957
2147
  creator: "KlingAI",
1958
- model: "Kling 3.0 Omni 720p (Standard)",
1959
2148
  elo: 1263,
1960
- apiPricing: "$10.08/min"
2149
+ model: "Kling 3.0 Omni 720p (Standard)",
2150
+ rank: 14
1961
2151
  },
1962
2152
  {
1963
- rank: 15,
2153
+ apiPricing: "$10.08/min",
1964
2154
  creator: "KlingAI",
1965
- model: "Kling 3.0 720p (Standard)",
1966
2155
  elo: 1263,
1967
- apiPricing: "$10.08/min"
2156
+ model: "Kling 3.0 720p (Standard)",
2157
+ rank: 15
1968
2158
  }
1969
- ]
2159
+ ],
2160
+ source: "https://artificialanalysis.ai/video/leaderboard/image-to-video"
1970
2161
  };
1971
2162
 
1972
2163
  // src/server.ts
1973
2164
  import { err, ok } from "neverthrow";
1974
2165
 
2166
+ // src/fal-parse.ts
2167
+ function isRecord(value) {
2168
+ return typeof value === "object" && value !== null;
2169
+ }
2170
+ function asString(value) {
2171
+ return typeof value === "string" ? value : void 0;
2172
+ }
2173
+ function asNumber(value) {
2174
+ return typeof value === "number" ? value : void 0;
2175
+ }
2176
+ function toMotifImage(value) {
2177
+ if (isRecord(value)) {
2178
+ return {
2179
+ content_type: asString(value.content_type),
2180
+ height: asNumber(value.height),
2181
+ url: asString(value.url) ?? "",
2182
+ width: asNumber(value.width)
2183
+ };
2184
+ }
2185
+ return { url: "" };
2186
+ }
2187
+ function parseImages(value) {
2188
+ if (!Array.isArray(value)) {
2189
+ return [];
2190
+ }
2191
+ return value.map((entry) => toMotifImage(entry));
2192
+ }
2193
+ function parseLogs(value) {
2194
+ if (!Array.isArray(value)) {
2195
+ return void 0;
2196
+ }
2197
+ const entries = [];
2198
+ for (const entry of value) {
2199
+ if (isRecord(entry)) {
2200
+ entries.push({
2201
+ message: asString(entry.message) ?? "",
2202
+ timestamp: asString(entry.timestamp) ?? ""
2203
+ });
2204
+ }
2205
+ }
2206
+ return entries;
2207
+ }
2208
+ function endpointFromQueueUrl(url, fallback) {
2209
+ if (url === void 0 || url === "") {
2210
+ return fallback;
2211
+ }
2212
+ try {
2213
+ const parsed = new URL(url);
2214
+ const match = /^\/(.+)\/requests\//.exec(parsed.pathname);
2215
+ return match?.[1] ?? fallback;
2216
+ } catch {
2217
+ return fallback;
2218
+ }
2219
+ }
2220
+ function parseQueueSubmission(data) {
2221
+ if (!isRecord(data)) {
2222
+ return { requestId: "" };
2223
+ }
2224
+ return {
2225
+ requestId: asString(data.request_id) ?? "",
2226
+ responseUrl: asString(data.response_url)
2227
+ };
2228
+ }
2229
+ function toHeaderRecord(headers) {
2230
+ if (headers === void 0) {
2231
+ return {};
2232
+ }
2233
+ if (headers instanceof Headers) {
2234
+ const record = {};
2235
+ for (const [key, value] of headers.entries()) {
2236
+ record[key] = value;
2237
+ }
2238
+ return record;
2239
+ }
2240
+ if (Array.isArray(headers)) {
2241
+ const record = {};
2242
+ for (const [key, value] of headers) {
2243
+ record[key] = value;
2244
+ }
2245
+ return record;
2246
+ }
2247
+ return headers;
2248
+ }
2249
+
1975
2250
  // src/tools.ts
1976
2251
  var FAL_EXPLORE_CHECKED_AT = "2026-05-12";
1977
2252
  var FAL_TOOLS = {
1978
- nsfw: {
1979
- name: "NSFW Checker",
1980
- endpoint: "fal-ai/x-ailab/nsfw",
1981
- task: "vision moderation",
1982
- category: "moderation",
1983
- description: "Predict whether one or more images contain NSFW concepts.",
1984
- inputKind: "images",
1985
- inputField: "image_urls",
1986
- outputKeys: ["has_nsfw_concepts"],
1987
- pricing: "$0.001/image",
1988
- sourceUrl: "https://fal.ai/models/fal-ai/x-ailab/nsfw"
1989
- },
1990
- "topaz-image": {
1991
- name: "Topaz Image Upscale",
1992
- endpoint: "fal-ai/topaz/upscale/image",
1993
- task: "image enhancement",
1994
- category: "upscale",
1995
- description: "Professional Topaz image enhancement and upscaling.",
1996
- inputKind: "image",
1997
- inputField: "image_url",
1998
- outputKeys: ["image"],
1999
- pricing: "$0.08+ by output megapixels",
2000
- sourceUrl: "https://fal.ai/models/fal-ai/topaz/upscale/image",
2001
- defaultOptions: {
2002
- model: "Standard V2",
2003
- output_format: "jpeg",
2004
- upscale_factor: 2
2005
- }
2006
- },
2007
- "topaz-video": {
2008
- name: "Topaz Video Upscale",
2009
- endpoint: "fal-ai/topaz/upscale/video",
2010
- task: "video enhancement",
2011
- category: "upscale",
2012
- description: "Professional Topaz video enhancement and upscaling.",
2013
- inputKind: "video",
2014
- inputField: "video_url",
2015
- outputKeys: ["video"],
2016
- pricing: "$0.01-$0.08/sec by output resolution",
2017
- sourceUrl: "https://fal.ai/models/fal-ai/topaz/upscale/video",
2018
- defaultOptions: {
2019
- model: "Proteus",
2020
- upscale_factor: 2
2021
- }
2022
- },
2023
- "bria-rmbg": {
2024
- name: "Bria RMBG 2.0",
2025
- endpoint: "fal-ai/bria/background/remove",
2026
- task: "image background removal",
2027
- category: "background",
2028
- description: "Commercial-safe background removal for images.",
2029
- inputKind: "image",
2030
- inputField: "image_url",
2031
- outputKeys: ["image"],
2032
- pricing: "$0.02/image",
2033
- sourceUrl: "https://fal.ai/models/fal-ai/bria/background/remove"
2034
- },
2035
2253
  birefnet: {
2036
- name: "BirefNet Background Removal",
2037
- endpoint: "fal-ai/birefnet/v2",
2038
- task: "image background removal",
2039
2254
  category: "background",
2040
- description: "High-resolution dichotomous image segmentation and background removal.",
2041
- inputKind: "image",
2042
- inputField: "image_url",
2043
- outputKeys: ["image", "mask_image"],
2044
- pricing: "$0/compute-second listed by fal",
2045
- sourceUrl: "https://fal.ai/models/fal-ai/birefnet/v2",
2046
2255
  defaultOptions: {
2047
2256
  model: "General Use (Light)",
2048
2257
  operating_resolution: "1024x1024",
2049
2258
  output_format: "png",
2050
2259
  refine_foreground: true
2051
- }
2260
+ },
2261
+ description: "High-resolution dichotomous image segmentation and background removal.",
2262
+ endpoint: "fal-ai/birefnet/v2",
2263
+ inputField: "image_url",
2264
+ inputKind: "image",
2265
+ name: "BirefNet Background Removal",
2266
+ outputKeys: ["image", "mask_image"],
2267
+ pricing: "$0/compute-second listed by fal",
2268
+ sourceUrl: "https://fal.ai/models/fal-ai/birefnet/v2",
2269
+ task: "image background removal"
2052
2270
  },
2053
- rembg: {
2054
- name: "Remove Background",
2055
- endpoint: "fal-ai/imageutils/rembg",
2056
- task: "image background removal",
2271
+ "bria-rmbg": {
2057
2272
  category: "background",
2058
- description: "Generic image background removal utility.",
2059
- inputKind: "image",
2273
+ description: "Commercial-safe background removal for images.",
2274
+ endpoint: "fal-ai/bria/background/remove",
2060
2275
  inputField: "image_url",
2276
+ inputKind: "image",
2277
+ name: "Bria RMBG 2.0",
2061
2278
  outputKeys: ["image"],
2062
- pricing: "$0/compute-second listed by fal",
2063
- sourceUrl: "https://fal.ai/models/fal-ai/imageutils/rembg",
2064
- defaultOptions: {
2065
- crop_to_bbox: false
2066
- }
2279
+ pricing: "$0.02/image",
2280
+ sourceUrl: "https://fal.ai/models/fal-ai/bria/background/remove",
2281
+ task: "image background removal"
2067
2282
  },
2068
2283
  "bria-video-rmbg": {
2069
- name: "Bria Video Background Removal",
2070
- endpoint: "bria/video/background-removal",
2071
- task: "video background removal",
2072
2284
  category: "background",
2285
+ defaultOptions: {
2286
+ background_color: "Black",
2287
+ output_container_and_codec: "webm_vp9",
2288
+ preserve_audio: true
2289
+ },
2073
2290
  description: "Remove video backgrounds with configurable output container.",
2074
- inputKind: "video",
2291
+ endpoint: "bria/video/background-removal",
2075
2292
  inputField: "video_url",
2293
+ inputKind: "video",
2294
+ name: "Bria Video Background Removal",
2076
2295
  outputKeys: ["video"],
2077
2296
  pricing: "$0.14/sec",
2078
2297
  sourceUrl: "https://fal.ai/models/bria/video/background-removal",
2079
- defaultOptions: {
2080
- background_color: "Black",
2081
- output_container_and_codec: "webm_vp9",
2082
- preserve_audio: true
2083
- }
2298
+ task: "video background removal"
2299
+ },
2300
+ "depth-anything": {
2301
+ category: "preprocess",
2302
+ description: "Generate Depth Anything v2 depth maps from input images.",
2303
+ endpoint: "fal-ai/image-preprocessors/depth-anything/v2",
2304
+ inputField: "image_url",
2305
+ inputKind: "image",
2306
+ name: "Depth Anything v2 Preprocessor",
2307
+ outputKeys: ["image"],
2308
+ pricing: "$0/compute-second listed by fal",
2309
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/depth-anything/v2",
2310
+ task: "depth preprocessing"
2084
2311
  },
2085
2312
  lineart: {
2086
- name: "Line Art Preprocessor",
2087
- endpoint: "fal-ai/image-preprocessors/lineart",
2088
- task: "image preprocessing",
2089
2313
  category: "preprocess",
2314
+ defaultOptions: {
2315
+ coarse: false
2316
+ },
2090
2317
  description: "Generate line art/control-style edges from an input image.",
2091
- inputKind: "image",
2318
+ endpoint: "fal-ai/image-preprocessors/lineart",
2092
2319
  inputField: "image_url",
2320
+ inputKind: "image",
2321
+ name: "Line Art Preprocessor",
2093
2322
  outputKeys: ["image"],
2094
2323
  pricing: "$0/compute-second listed by fal",
2095
2324
  sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/lineart",
2096
- defaultOptions: {
2097
- coarse: false
2098
- }
2325
+ task: "image preprocessing"
2099
2326
  },
2100
- "sam-preprocessor": {
2101
- name: "SAM Preprocessor",
2102
- endpoint: "fal-ai/image-preprocessors/sam",
2103
- task: "segmentation preprocessing",
2104
- category: "preprocess",
2105
- description: "Generate a SAM segmentation map for ControlNet-style workflows.",
2106
- inputKind: "image",
2327
+ "marigold-depth": {
2328
+ category: "depth",
2329
+ defaultOptions: {
2330
+ ensemble_size: 10,
2331
+ num_inference_steps: 10
2332
+ },
2333
+ description: "Create depth maps using Marigold depth estimation.",
2334
+ endpoint: "fal-ai/imageutils/marigold-depth",
2107
2335
  inputField: "image_url",
2336
+ inputKind: "image",
2337
+ name: "Marigold Depth Estimation",
2108
2338
  outputKeys: ["image"],
2109
2339
  pricing: "$0/compute-second listed by fal",
2110
- sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/sam"
2340
+ sourceUrl: "https://fal.ai/models/fal-ai/imageutils/marigold-depth",
2341
+ task: "depth map"
2111
2342
  },
2112
2343
  "midas-depth": {
2113
- name: "MiDaS Depth Estimation",
2114
- endpoint: "fal-ai/imageutils/depth",
2115
- task: "depth map",
2116
2344
  category: "depth",
2345
+ defaultOptions: {
2346
+ a: Math.PI * 2,
2347
+ bg_th: 0.1
2348
+ },
2117
2349
  description: "Create MiDaS depth maps from input images.",
2118
- inputKind: "image",
2350
+ endpoint: "fal-ai/imageutils/depth",
2119
2351
  inputField: "image_url",
2352
+ inputKind: "image",
2353
+ name: "MiDaS Depth Estimation",
2120
2354
  outputKeys: ["image"],
2121
2355
  pricing: "$0/compute-second listed by fal",
2122
2356
  sourceUrl: "https://fal.ai/models/fal-ai/imageutils/depth",
2123
- defaultOptions: {
2124
- a: Math.PI * 2,
2125
- bg_th: 0.1
2126
- }
2357
+ task: "depth map"
2127
2358
  },
2128
2359
  "midas-preprocessor": {
2129
- name: "MiDaS Preprocessor",
2130
- endpoint: "fal-ai/image-preprocessors/midas",
2131
- task: "depth and normal preprocessing",
2132
2360
  category: "preprocess",
2133
2361
  description: "Generate MiDaS depth and normal maps for image workflows.",
2134
- inputKind: "image",
2362
+ endpoint: "fal-ai/image-preprocessors/midas",
2135
2363
  inputField: "image_url",
2364
+ inputKind: "image",
2365
+ name: "MiDaS Preprocessor",
2136
2366
  outputKeys: ["depth_map", "normal_map"],
2137
2367
  pricing: "$0/compute-second listed by fal",
2138
- sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/midas"
2368
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/midas",
2369
+ task: "depth and normal preprocessing"
2139
2370
  },
2140
- "marigold-depth": {
2141
- name: "Marigold Depth Estimation",
2142
- endpoint: "fal-ai/imageutils/marigold-depth",
2143
- task: "depth map",
2144
- category: "depth",
2145
- description: "Create depth maps using Marigold depth estimation.",
2146
- inputKind: "image",
2371
+ nsfw: {
2372
+ category: "moderation",
2373
+ description: "Predict whether one or more images contain NSFW concepts.",
2374
+ endpoint: "fal-ai/x-ailab/nsfw",
2375
+ inputField: "image_urls",
2376
+ inputKind: "images",
2377
+ name: "NSFW Checker",
2378
+ outputKeys: ["has_nsfw_concepts"],
2379
+ pricing: "$0.001/image",
2380
+ sourceUrl: "https://fal.ai/models/fal-ai/x-ailab/nsfw",
2381
+ task: "vision moderation"
2382
+ },
2383
+ rembg: {
2384
+ category: "background",
2385
+ defaultOptions: {
2386
+ crop_to_bbox: false
2387
+ },
2388
+ description: "Generic image background removal utility.",
2389
+ endpoint: "fal-ai/imageutils/rembg",
2147
2390
  inputField: "image_url",
2391
+ inputKind: "image",
2392
+ name: "Remove Background",
2148
2393
  outputKeys: ["image"],
2149
2394
  pricing: "$0/compute-second listed by fal",
2150
- sourceUrl: "https://fal.ai/models/fal-ai/imageutils/marigold-depth",
2151
- defaultOptions: {
2152
- ensemble_size: 10,
2153
- num_inference_steps: 10
2154
- }
2395
+ sourceUrl: "https://fal.ai/models/fal-ai/imageutils/rembg",
2396
+ task: "image background removal"
2155
2397
  },
2156
- "depth-anything": {
2157
- name: "Depth Anything v2 Preprocessor",
2158
- endpoint: "fal-ai/image-preprocessors/depth-anything/v2",
2159
- task: "depth preprocessing",
2398
+ "sam-preprocessor": {
2160
2399
  category: "preprocess",
2161
- description: "Generate Depth Anything v2 depth maps from input images.",
2162
- inputKind: "image",
2400
+ description: "Generate a SAM segmentation map for ControlNet-style workflows.",
2401
+ endpoint: "fal-ai/image-preprocessors/sam",
2163
2402
  inputField: "image_url",
2403
+ inputKind: "image",
2404
+ name: "SAM Preprocessor",
2164
2405
  outputKeys: ["image"],
2165
2406
  pricing: "$0/compute-second listed by fal",
2166
- sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/depth-anything/v2"
2407
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/sam",
2408
+ task: "segmentation preprocessing"
2409
+ },
2410
+ "sam2-auto": {
2411
+ category: "segmentation",
2412
+ defaultOptions: {
2413
+ min_mask_region_area: 100,
2414
+ output_format: "png",
2415
+ points_per_side: 32,
2416
+ pred_iou_thresh: 0.88,
2417
+ stability_score_thresh: 0.95
2418
+ },
2419
+ description: "Automatically segment an image into combined and individual masks.",
2420
+ endpoint: "fal-ai/sam2/auto-segment",
2421
+ inputField: "image_url",
2422
+ inputKind: "image",
2423
+ name: "SAM 2 Auto Segment",
2424
+ outputKeys: ["combined_mask", "individual_masks"],
2425
+ pricing: "$0/compute-second listed by fal",
2426
+ sourceUrl: "https://fal.ai/models/fal-ai/sam2/auto-segment",
2427
+ task: "automatic image segmentation"
2428
+ },
2429
+ "sam3-1-image": {
2430
+ category: "segmentation",
2431
+ defaultOptions: {
2432
+ apply_mask: true,
2433
+ max_masks: 3,
2434
+ output_format: "png"
2435
+ },
2436
+ description: "Segment image objects with text, point, or box prompts. SAM 3.1 adds Object Multiplex for faster multi-object tracking.",
2437
+ endpoint: "fal-ai/sam-3-1/image",
2438
+ inputField: "image_url",
2439
+ inputKind: "image",
2440
+ name: "SAM 3.1 Image",
2441
+ outputKeys: ["image", "masks", "metadata", "scores", "boxes"],
2442
+ pricing: "$0.005/request",
2443
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3-1/image",
2444
+ task: "promptable image segmentation"
2445
+ },
2446
+ "sam3-1-video": {
2447
+ category: "segmentation",
2448
+ defaultOptions: {
2449
+ apply_mask: true,
2450
+ prompt: "person"
2451
+ },
2452
+ description: "SAM 3.1 video segmentation with Object Multiplex tracking for multiple objects.",
2453
+ endpoint: "fal-ai/sam-3-1/video",
2454
+ inputField: "video_url",
2455
+ inputKind: "video",
2456
+ name: "SAM 3.1 Video",
2457
+ outputKeys: ["video", "boundingbox_frames_zip"],
2458
+ pricing: "fal pricing varies by frame count",
2459
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3-1/video",
2460
+ task: "multi-object video segmentation"
2167
2461
  },
2168
- "sam2-auto": {
2169
- name: "SAM 2 Auto Segment",
2170
- endpoint: "fal-ai/sam2/auto-segment",
2171
- task: "automatic image segmentation",
2172
- category: "segmentation",
2173
- description: "Automatically segment an image into combined and individual masks.",
2462
+ "sam3-3d-align": {
2463
+ category: "3d",
2464
+ description: "Align SAM 3D objects and bodies into a shared scene.",
2465
+ endpoint: "fal-ai/sam-3/3d-align",
2466
+ inputField: "image_url",
2174
2467
  inputKind: "image",
2468
+ name: "SAM 3D Align",
2469
+ outputKeys: ["scene_glb", "metadata", "artifacts_zip"],
2470
+ pricing: "fal pricing varies by scene",
2471
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-align",
2472
+ task: "3D scene alignment"
2473
+ },
2474
+ "sam3-3d-body": {
2475
+ category: "3d",
2476
+ defaultOptions: {
2477
+ export_meshes: true,
2478
+ include_3d_keypoints: true,
2479
+ include_mhr_params: true
2480
+ },
2481
+ description: "Reconstruct human body meshes and keypoints from a single image.",
2482
+ endpoint: "fal-ai/sam-3/3d-body",
2175
2483
  inputField: "image_url",
2176
- outputKeys: ["combined_mask", "individual_masks"],
2177
- pricing: "$0/compute-second listed by fal",
2178
- sourceUrl: "https://fal.ai/models/fal-ai/sam2/auto-segment",
2484
+ inputKind: "image",
2485
+ name: "SAM 3D Body",
2486
+ outputKeys: ["model_glb", "visualization", "meshes", "metadata"],
2487
+ pricing: "$0.015/inference",
2488
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-body",
2489
+ task: "single-image 3D body reconstruction"
2490
+ },
2491
+ "sam3-3d-objects": {
2492
+ category: "3d",
2179
2493
  defaultOptions: {
2180
- min_mask_region_area: 100,
2181
- output_format: "png",
2182
- points_per_side: 32,
2183
- pred_iou_thresh: 0.88,
2184
- stability_score_thresh: 0.95
2185
- }
2494
+ prompt: "car"
2495
+ },
2496
+ description: "Reconstruct one or more 3D objects from an image and prompts.",
2497
+ endpoint: "fal-ai/sam-3/3d-objects",
2498
+ inputField: "image_url",
2499
+ inputKind: "image",
2500
+ name: "SAM 3D Objects",
2501
+ outputKeys: [
2502
+ "gaussian_splat",
2503
+ "model_glb",
2504
+ "metadata",
2505
+ "individual_splats",
2506
+ "individual_glbs",
2507
+ "artifacts_zip"
2508
+ ],
2509
+ pricing: "$0.02/generation",
2510
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-objects",
2511
+ task: "single-image 3D object reconstruction"
2186
2512
  },
2187
2513
  "sam3-image": {
2188
- name: "SAM 3 Image",
2189
- endpoint: "fal-ai/sam-3/image",
2190
- task: "promptable image segmentation",
2191
2514
  category: "segmentation",
2192
- description: "Segment image objects with text, point, or box prompts.",
2193
- inputKind: "image",
2194
- inputField: "image_url",
2195
- outputKeys: ["image", "masks", "metadata", "scores", "boxes"],
2196
- pricing: "$0.005/request",
2197
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3/image",
2198
2515
  defaultOptions: {
2199
2516
  apply_mask: true,
2200
2517
  max_masks: 3,
2201
2518
  output_format: "png"
2202
- }
2203
- },
2204
- "sam3-1-image": {
2205
- name: "SAM 3.1 Image",
2206
- endpoint: "fal-ai/sam-3-1/image",
2207
- task: "promptable image segmentation",
2208
- category: "segmentation",
2209
- description: "Segment image objects with text, point, or box prompts. SAM 3.1 adds Object Multiplex for faster multi-object tracking.",
2210
- inputKind: "image",
2519
+ },
2520
+ description: "Segment image objects with text, point, or box prompts.",
2521
+ endpoint: "fal-ai/sam-3/image",
2211
2522
  inputField: "image_url",
2523
+ inputKind: "image",
2524
+ name: "SAM 3 Image",
2212
2525
  outputKeys: ["image", "masks", "metadata", "scores", "boxes"],
2213
2526
  pricing: "$0.005/request",
2214
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3-1/image",
2215
- defaultOptions: {
2216
- apply_mask: true,
2217
- max_masks: 3,
2218
- output_format: "png"
2219
- }
2527
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3/image",
2528
+ task: "promptable image segmentation"
2220
2529
  },
2221
2530
  "sam3-image-rle": {
2222
- name: "SAM 3 Image RLE",
2223
- endpoint: "fal-ai/sam-3/image-rle",
2224
- task: "promptable image segmentation to RLE",
2225
2531
  category: "segmentation",
2532
+ defaultOptions: {
2533
+ apply_mask: true,
2534
+ max_masks: 3
2535
+ },
2226
2536
  description: "Segment image objects and return run-length encoded masks.",
2227
- inputKind: "image",
2537
+ endpoint: "fal-ai/sam-3/image-rle",
2228
2538
  inputField: "image_url",
2539
+ inputKind: "image",
2540
+ name: "SAM 3 Image RLE",
2229
2541
  outputKeys: ["rle_masks", "metadata", "scores", "boxes"],
2230
2542
  pricing: "$0.005/request",
2231
2543
  sourceUrl: "https://fal.ai/models/fal-ai/sam-3/image-rle",
2232
- defaultOptions: {
2233
- apply_mask: true,
2234
- max_masks: 3
2235
- }
2544
+ task: "promptable image segmentation to RLE"
2236
2545
  },
2237
2546
  "sam3-video": {
2238
- name: "SAM 3 Video",
2239
- endpoint: "fal-ai/sam-3/video",
2240
- task: "promptable video segmentation",
2241
2547
  category: "segmentation",
2242
- description: "Segment and track prompted objects across video frames.",
2243
- inputKind: "video",
2244
- inputField: "video_url",
2245
- outputKeys: ["video", "boundingbox_frames_zip"],
2246
- pricing: "$0.005/16 frames",
2247
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3/video",
2248
2548
  defaultOptions: {
2249
2549
  apply_mask: true,
2250
2550
  detection_threshold: 0.5,
2251
2551
  prompt: "person",
2252
2552
  video_output_type: "X264 (.mp4)"
2253
- }
2553
+ },
2554
+ description: "Segment and track prompted objects across video frames.",
2555
+ endpoint: "fal-ai/sam-3/video",
2556
+ inputField: "video_url",
2557
+ inputKind: "video",
2558
+ name: "SAM 3 Video",
2559
+ outputKeys: ["video", "boundingbox_frames_zip"],
2560
+ pricing: "$0.005/16 frames",
2561
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3/video",
2562
+ task: "promptable video segmentation"
2254
2563
  },
2255
2564
  "sam3-video-rle": {
2256
- name: "SAM 3 Video RLE",
2257
- endpoint: "fal-ai/sam-3/video-rle",
2258
- task: "promptable video segmentation to RLE",
2259
2565
  category: "segmentation",
2566
+ defaultOptions: {
2567
+ apply_mask: true,
2568
+ detection_threshold: 0.5,
2569
+ prompt: "person"
2570
+ },
2260
2571
  description: "Track prompted video objects and return RLE mask data.",
2261
- inputKind: "video",
2572
+ endpoint: "fal-ai/sam-3/video-rle",
2262
2573
  inputField: "video_url",
2574
+ inputKind: "video",
2575
+ name: "SAM 3 Video RLE",
2263
2576
  outputKeys: ["rle_masks", "metadata"],
2264
2577
  pricing: "$0.005/16 frames",
2265
2578
  sourceUrl: "https://fal.ai/models/fal-ai/sam-3/video-rle",
2266
- defaultOptions: {
2267
- apply_mask: true,
2268
- detection_threshold: 0.5,
2269
- prompt: "person"
2270
- }
2579
+ task: "promptable video segmentation to RLE"
2271
2580
  },
2272
- "sam3-1-video": {
2273
- name: "SAM 3.1 Video",
2274
- endpoint: "fal-ai/sam-3-1/video",
2275
- task: "multi-object video segmentation",
2276
- category: "segmentation",
2277
- description: "SAM 3.1 video segmentation with Object Multiplex tracking for multiple objects.",
2278
- inputKind: "video",
2279
- inputField: "video_url",
2280
- outputKeys: ["video", "boundingbox_frames_zip"],
2281
- pricing: "fal pricing varies by frame count",
2282
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3-1/video",
2581
+ "topaz-image": {
2582
+ category: "upscale",
2283
2583
  defaultOptions: {
2284
- apply_mask: true,
2285
- prompt: "person"
2286
- }
2287
- },
2288
- "sam3-3d-objects": {
2289
- name: "SAM 3D Objects",
2290
- endpoint: "fal-ai/sam-3/3d-objects",
2291
- task: "single-image 3D object reconstruction",
2292
- category: "3d",
2293
- description: "Reconstruct one or more 3D objects from an image and prompts.",
2294
- inputKind: "image",
2584
+ model: "Standard V2",
2585
+ output_format: "jpeg",
2586
+ upscale_factor: 2
2587
+ },
2588
+ description: "Professional Topaz image enhancement and upscaling.",
2589
+ endpoint: "fal-ai/topaz/upscale/image",
2295
2590
  inputField: "image_url",
2296
- outputKeys: [
2297
- "gaussian_splat",
2298
- "model_glb",
2299
- "metadata",
2300
- "individual_splats",
2301
- "individual_glbs",
2302
- "artifacts_zip"
2303
- ],
2304
- pricing: "$0.02/generation",
2305
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-objects",
2306
- defaultOptions: {
2307
- prompt: "car"
2308
- }
2309
- },
2310
- "sam3-3d-body": {
2311
- name: "SAM 3D Body",
2312
- endpoint: "fal-ai/sam-3/3d-body",
2313
- task: "single-image 3D body reconstruction",
2314
- category: "3d",
2315
- description: "Reconstruct human body meshes and keypoints from a single image.",
2316
2591
  inputKind: "image",
2317
- inputField: "image_url",
2318
- outputKeys: ["model_glb", "visualization", "meshes", "metadata"],
2319
- pricing: "$0.015/inference",
2320
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-body",
2321
- defaultOptions: {
2322
- export_meshes: true,
2323
- include_3d_keypoints: true,
2324
- include_mhr_params: true
2325
- }
2592
+ name: "Topaz Image Upscale",
2593
+ outputKeys: ["image"],
2594
+ pricing: "$0.08+ by output megapixels",
2595
+ sourceUrl: "https://fal.ai/models/fal-ai/topaz/upscale/image",
2596
+ task: "image enhancement"
2326
2597
  },
2327
- "sam3-3d-align": {
2328
- name: "SAM 3D Align",
2329
- endpoint: "fal-ai/sam-3/3d-align",
2330
- task: "3D scene alignment",
2331
- category: "3d",
2332
- description: "Align SAM 3D objects and bodies into a shared scene.",
2333
- inputKind: "image",
2334
- inputField: "image_url",
2335
- outputKeys: ["scene_glb", "metadata", "artifacts_zip"],
2336
- pricing: "fal pricing varies by scene",
2337
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-align"
2598
+ "topaz-video": {
2599
+ category: "upscale",
2600
+ defaultOptions: {
2601
+ model: "Proteus",
2602
+ upscale_factor: 2
2603
+ },
2604
+ description: "Professional Topaz video enhancement and upscaling.",
2605
+ endpoint: "fal-ai/topaz/upscale/video",
2606
+ inputField: "video_url",
2607
+ inputKind: "video",
2608
+ name: "Topaz Video Upscale",
2609
+ outputKeys: ["video"],
2610
+ pricing: "$0.01-$0.08/sec by output resolution",
2611
+ sourceUrl: "https://fal.ai/models/fal-ai/topaz/upscale/video",
2612
+ task: "video enhancement"
2338
2613
  }
2339
2614
  };
2340
2615
  var FAL_TOOLS_CHECKED_AT = FAL_EXPLORE_CHECKED_AT;
2341
- var FAL_TOOL_IDS = Object.keys(FAL_TOOLS);
2616
+ var FAL_TOOL_IDS = Object.keys(
2617
+ FAL_TOOLS
2618
+ );
2342
2619
  function isFalToolId(tool) {
2343
2620
  return Object.hasOwn(FAL_TOOLS, tool);
2344
2621
  }
@@ -2347,7 +2624,7 @@ function buildFalToolRequest(options) {
2347
2624
  throw new Error(`Unknown fal tool: ${options.tool}`);
2348
2625
  }
2349
2626
  const tool = FAL_TOOLS[options.tool];
2350
- const values = options.inputs ?? (options.input ? [options.input] : []);
2627
+ const values = options.inputs ?? (options.input !== void 0 && options.input !== "" ? [options.input] : []);
2351
2628
  if (values.length === 0) {
2352
2629
  throw new Error(`${options.tool} requires input media`);
2353
2630
  }
@@ -2358,7 +2635,7 @@ function buildFalToolRequest(options) {
2358
2635
  ...options.options,
2359
2636
  [tool.inputField]: mediaValue
2360
2637
  };
2361
- return { endpoint: tool.endpoint, body, tool };
2638
+ return { body, endpoint: tool.endpoint, tool };
2362
2639
  }
2363
2640
 
2364
2641
  // src/server.ts
@@ -2366,16 +2643,6 @@ var FAL_BASE_URL = "https://fal.run";
2366
2643
  var FAL_QUEUE_URL = "https://queue.fal.run";
2367
2644
  var FAL_API_URL = "https://api.fal.ai";
2368
2645
  var FAL_REST_URL = "https://rest.alpha.fal.ai";
2369
- function endpointFromQueueUrl(url, fallback) {
2370
- if (!url) return fallback;
2371
- try {
2372
- const parsed = new URL(url);
2373
- const match = parsed.pathname.match(/^\/(.+)\/requests\//);
2374
- return match?.[1] ?? fallback;
2375
- } catch {
2376
- return fallback;
2377
- }
2378
- }
2379
2646
  var MotifServer = class {
2380
2647
  apiKey;
2381
2648
  timeout;
@@ -2398,8 +2665,8 @@ var MotifServer = class {
2398
2665
  /** Generate images synchronously (blocks until fal.ai returns). */
2399
2666
  async generate(options) {
2400
2667
  const config = MODELS[options.model];
2401
- if (config?.useQueue) {
2402
- return this.generateQueued(options);
2668
+ if (config?.useQueue === true) {
2669
+ return await this.generateQueued(options);
2403
2670
  }
2404
2671
  let built;
2405
2672
  try {
@@ -2409,9 +2676,9 @@ var MotifServer = class {
2409
2676
  }
2410
2677
  const { endpoint, body } = built;
2411
2678
  const response = await this.request(`${FAL_BASE_URL}/${endpoint}`, {
2412
- method: "POST",
2413
2679
  body: JSON.stringify(body),
2414
- headers: this.ephemeralHeaders(options)
2680
+ headers: this.ephemeralHeaders(options),
2681
+ method: "POST"
2415
2682
  });
2416
2683
  if (response.isErr()) {
2417
2684
  return err(response.error);
@@ -2435,14 +2702,16 @@ var MotifServer = class {
2435
2702
  return err(status.error);
2436
2703
  }
2437
2704
  if (status.value.status === "completed") {
2438
- return this.getJobResult(job.value.endpoint, job.value.requestId);
2705
+ return await this.getJobResult(job.value.endpoint, job.value.requestId);
2439
2706
  }
2440
2707
  if (status.value.status === "failed") {
2441
2708
  return err(
2442
2709
  new MotifError(status.value.error ?? "Queued generation failed", 0)
2443
2710
  );
2444
2711
  }
2445
- await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
2712
+ await new Promise((resolve) => {
2713
+ setTimeout(resolve, pollIntervalMs);
2714
+ });
2446
2715
  }
2447
2716
  return err(new MotifError("Queued generation timed out", 0));
2448
2717
  }
@@ -2457,22 +2726,23 @@ var MotifServer = class {
2457
2726
  }
2458
2727
  const { endpoint, body } = built;
2459
2728
  const response = await this.request(`${FAL_QUEUE_URL}/${endpoint}`, {
2460
- method: "POST",
2461
2729
  body: JSON.stringify(body),
2462
- headers: this.ephemeralHeaders(options)
2730
+ headers: this.ephemeralHeaders(options),
2731
+ method: "POST"
2463
2732
  });
2464
2733
  if (response.isErr()) {
2465
2734
  return err(response.error);
2466
2735
  }
2467
2736
  const data = await response.value.json();
2737
+ const submission = parseQueueSubmission(data);
2468
2738
  return ok({
2469
- requestId: data.request_id,
2470
- endpoint: endpointFromQueueUrl(data.response_url, endpoint),
2739
+ endpoint: endpointFromQueueUrl(submission.responseUrl, endpoint),
2471
2740
  estimatedCost: estimateCost(
2472
2741
  options.model,
2473
2742
  options.resolution,
2474
2743
  options.numImages
2475
- )
2744
+ ),
2745
+ requestId: submission.requestId
2476
2746
  });
2477
2747
  }
2478
2748
  /** Check the status of a queued generation. */
@@ -2483,23 +2753,25 @@ var MotifServer = class {
2483
2753
  return err(response.error);
2484
2754
  }
2485
2755
  const data = await response.value.json();
2756
+ const record = isRecord(data) ? data : {};
2757
+ const rawStatus = asString(record.status);
2486
2758
  let status;
2487
- if (data.status === "IN_QUEUE") {
2759
+ if (rawStatus === "IN_QUEUE") {
2488
2760
  status = "queued";
2489
- } else if (data.status === "IN_PROGRESS") {
2761
+ } else if (rawStatus === "IN_PROGRESS") {
2490
2762
  status = "processing";
2491
- } else if (data.status === "COMPLETED") {
2763
+ } else if (rawStatus === "COMPLETED") {
2492
2764
  status = "completed";
2493
- } else if (data.status === "FAILED" || data.status === "ERROR" || data.status === "CANCELED") {
2765
+ } else if (rawStatus === "FAILED" || rawStatus === "ERROR" || rawStatus === "CANCELED") {
2494
2766
  status = "failed";
2495
2767
  } else {
2496
- return err(new MotifError(`Unknown job status: ${data.status}`, 0));
2768
+ return err(new MotifError(`Unknown job status: ${rawStatus}`, 0));
2497
2769
  }
2498
2770
  return ok({
2499
- status,
2500
- error: data.error ?? data.detail,
2501
- queuePosition: data.queue_position,
2502
- logs: data.logs
2771
+ error: asString(record.error) ?? asString(record.detail),
2772
+ logs: parseLogs(record.logs),
2773
+ queuePosition: asNumber(record.queue_position),
2774
+ status
2503
2775
  });
2504
2776
  }
2505
2777
  /** Fetch the completed result from the queue. */
@@ -2532,21 +2804,38 @@ var MotifServer = class {
2532
2804
  }
2533
2805
  const body = { image_url: imageUrl };
2534
2806
  if (model === "crystal") {
2535
- if (scaleFactor !== void 0) body.scale_factor = scaleFactor;
2536
- if (creativity !== void 0) body.creativity = creativity;
2807
+ if (scaleFactor !== void 0) {
2808
+ body.scale_factor = scaleFactor;
2809
+ }
2810
+ if (creativity !== void 0) {
2811
+ body.creativity = creativity;
2812
+ }
2537
2813
  } else {
2538
- if (scaleFactor !== void 0) body.upscale_factor = scaleFactor;
2539
- if (creativity !== void 0) body.creativity = creativity;
2540
- if (resemblance !== void 0) body.resemblance = resemblance;
2541
- if (upscalePrompt) body.prompt = upscalePrompt;
2542
- if (negativePrompt) body.negative_prompt = negativePrompt;
2543
- if (numInferenceSteps !== void 0)
2814
+ if (scaleFactor !== void 0) {
2815
+ body.upscale_factor = scaleFactor;
2816
+ }
2817
+ if (creativity !== void 0) {
2818
+ body.creativity = creativity;
2819
+ }
2820
+ if (resemblance !== void 0) {
2821
+ body.resemblance = resemblance;
2822
+ }
2823
+ if (upscalePrompt !== void 0 && upscalePrompt !== "") {
2824
+ body.prompt = upscalePrompt;
2825
+ }
2826
+ if (negativePrompt !== void 0 && negativePrompt !== "") {
2827
+ body.negative_prompt = negativePrompt;
2828
+ }
2829
+ if (numInferenceSteps !== void 0) {
2544
2830
  body.num_inference_steps = numInferenceSteps;
2545
- if (guidanceScale !== void 0) body.guidance_scale = guidanceScale;
2831
+ }
2832
+ if (guidanceScale !== void 0) {
2833
+ body.guidance_scale = guidanceScale;
2834
+ }
2546
2835
  }
2547
2836
  const response = await this.request(`${FAL_BASE_URL}/${config.endpoint}`, {
2548
- method: "POST",
2549
- body: JSON.stringify(body)
2837
+ body: JSON.stringify(body),
2838
+ method: "POST"
2550
2839
  });
2551
2840
  if (response.isErr()) {
2552
2841
  return err(response.error);
@@ -2573,17 +2862,25 @@ var MotifServer = class {
2573
2862
  }
2574
2863
  const rbBody = { image_url: imageUrl };
2575
2864
  if (model === "rmbg") {
2576
- if (variant) rbBody.model = variant;
2577
- if (operatingResolution)
2865
+ if (variant) {
2866
+ rbBody.model = variant;
2867
+ }
2868
+ if (operatingResolution) {
2578
2869
  rbBody.operating_resolution = operatingResolution;
2579
- if (outputFormat) rbBody.output_format = outputFormat;
2580
- if (refineForeground !== void 0)
2870
+ }
2871
+ if (outputFormat) {
2872
+ rbBody.output_format = outputFormat;
2873
+ }
2874
+ if (refineForeground !== void 0) {
2581
2875
  rbBody.refine_foreground = refineForeground;
2582
- if (outputMask !== void 0) rbBody.output_mask = outputMask;
2876
+ }
2877
+ if (outputMask !== void 0) {
2878
+ rbBody.output_mask = outputMask;
2879
+ }
2583
2880
  }
2584
2881
  const response = await this.request(`${FAL_BASE_URL}/${config.endpoint}`, {
2585
- method: "POST",
2586
- body: JSON.stringify(rbBody)
2882
+ body: JSON.stringify(rbBody),
2883
+ method: "POST"
2587
2884
  });
2588
2885
  if (response.isErr()) {
2589
2886
  return err(response.error);
@@ -2612,28 +2909,33 @@ var MotifServer = class {
2612
2909
  return err(new MotifError("Kling video model not found", 0));
2613
2910
  }
2614
2911
  const body = {
2615
- start_image_url: imageUrl,
2616
- prompt,
2617
2912
  duration: String(duration),
2618
- generate_audio: generateAudio
2913
+ generate_audio: generateAudio,
2914
+ prompt,
2915
+ start_image_url: imageUrl
2619
2916
  };
2620
- if (endImageUrl) {
2917
+ if (endImageUrl !== void 0 && endImageUrl !== "") {
2621
2918
  body.end_image_url = endImageUrl;
2622
2919
  }
2623
- if (negativePrompt) body.negative_prompt = negativePrompt;
2624
- if (cfgScale !== void 0) body.cfg_scale = cfgScale;
2920
+ if (negativePrompt !== void 0 && negativePrompt !== "") {
2921
+ body.negative_prompt = negativePrompt;
2922
+ }
2923
+ if (cfgScale !== void 0) {
2924
+ body.cfg_scale = cfgScale;
2925
+ }
2625
2926
  const response = await this.request(`${FAL_QUEUE_URL}/${config.endpoint}`, {
2626
- method: "POST",
2627
- body: JSON.stringify(body)
2927
+ body: JSON.stringify(body),
2928
+ method: "POST"
2628
2929
  });
2629
2930
  if (response.isErr()) {
2630
2931
  return err(response.error);
2631
2932
  }
2632
2933
  const data = await response.value.json();
2934
+ const submission = parseQueueSubmission(data);
2633
2935
  return ok({
2634
- requestId: data.request_id,
2635
- endpoint: endpointFromQueueUrl(data.response_url, config.endpoint),
2636
- estimatedCost: estimateVideoCost(duration, generateAudio)
2936
+ endpoint: endpointFromQueueUrl(submission.responseUrl, config.endpoint),
2937
+ estimatedCost: estimateVideoCost(duration, generateAudio),
2938
+ requestId: submission.requestId
2637
2939
  });
2638
2940
  }
2639
2941
  /** Fetch the completed video result from the queue. */
@@ -2644,14 +2946,15 @@ var MotifServer = class {
2644
2946
  return err(response.error);
2645
2947
  }
2646
2948
  const data = await response.value.json();
2647
- if (!data.video) {
2949
+ const video = isRecord(data) && isRecord(data.video) ? data.video : void 0;
2950
+ if (video === void 0) {
2648
2951
  return err(new MotifError("No video in response", 0));
2649
2952
  }
2650
2953
  return ok({
2651
- url: data.video.url,
2652
- contentType: data.video.content_type,
2653
- fileName: data.video.file_name,
2654
- fileSize: data.video.file_size
2954
+ contentType: asString(video.content_type) ?? "",
2955
+ fileName: asString(video.file_name) ?? "",
2956
+ fileSize: asNumber(video.file_size) ?? 0,
2957
+ url: asString(video.url) ?? ""
2655
2958
  });
2656
2959
  }
2657
2960
  /** ─── File Upload ─────────────────────────────────────────── */
@@ -2663,26 +2966,33 @@ var MotifServer = class {
2663
2966
  const initiateResponse = await this.request(
2664
2967
  `${FAL_REST_URL}/storage/upload/initiate?storage_type=fal-cdn-v3`,
2665
2968
  {
2666
- method: "POST",
2667
2969
  body: JSON.stringify({
2668
2970
  content_type: options.contentType,
2669
2971
  file_name: options.fileName
2670
- })
2972
+ }),
2973
+ method: "POST"
2671
2974
  }
2672
2975
  );
2673
2976
  if (initiateResponse.isErr()) {
2674
2977
  return err(initiateResponse.error);
2675
2978
  }
2676
- const { file_url, upload_url } = await initiateResponse.value.json();
2979
+ const initiateData = await initiateResponse.value.json();
2980
+ const fileUrl = isRecord(initiateData) ? asString(initiateData.file_url) ?? "" : "";
2981
+ const uploadUrl = isRecord(initiateData) ? asString(initiateData.upload_url) : void 0;
2982
+ if (uploadUrl === void 0 || uploadUrl === "") {
2983
+ return err(
2984
+ new MotifError("Upload initiate response missing upload_url", 0)
2985
+ );
2986
+ }
2677
2987
  let putResponse;
2678
2988
  try {
2679
2989
  const body = Buffer.from(
2680
2990
  file instanceof Uint8Array ? file : new Uint8Array(file)
2681
2991
  );
2682
- putResponse = await fetch(upload_url, {
2683
- method: "PUT",
2992
+ putResponse = await fetch(uploadUrl, {
2993
+ body,
2684
2994
  headers: { "Content-Type": options.contentType },
2685
- body
2995
+ method: "PUT"
2686
2996
  });
2687
2997
  } catch (error) {
2688
2998
  return err(
@@ -2700,7 +3010,7 @@ var MotifServer = class {
2700
3010
  )
2701
3011
  );
2702
3012
  }
2703
- return ok(file_url);
3013
+ return ok(fileUrl);
2704
3014
  }
2705
3015
  /** ─── Utilities ───────────────────────────────────────────── */
2706
3016
  /** Run a registered fal utility/tool endpoint. */
@@ -2717,13 +3027,14 @@ var MotifServer = class {
2717
3027
  );
2718
3028
  }
2719
3029
  const response = await this.request(`${FAL_BASE_URL}/${request.endpoint}`, {
2720
- method: "POST",
2721
- body: JSON.stringify(request.body)
3030
+ body: JSON.stringify(request.body),
3031
+ method: "POST"
2722
3032
  });
2723
3033
  if (response.isErr()) {
2724
3034
  return err(response.error);
2725
3035
  }
2726
- return ok(await response.value.json());
3036
+ const data = await response.value.json();
3037
+ return ok(isRecord(data) ? data : {});
2727
3038
  }
2728
3039
  /**
2729
3040
  * Delete fal's stored IO payloads for a completed request.
@@ -2740,7 +3051,7 @@ var MotifServer = class {
2740
3051
  if (response.isErr()) {
2741
3052
  return err(response.error);
2742
3053
  }
2743
- return ok(void 0);
3054
+ return ok();
2744
3055
  }
2745
3056
  /** Estimate cost for a generation (no API call). */
2746
3057
  estimateCost(model, resolution, numImages) {
@@ -2772,21 +3083,25 @@ var MotifServer = class {
2772
3083
  let lastError = null;
2773
3084
  for (let attempt = 0; attempt <= this.retries; attempt++) {
2774
3085
  const controller = new AbortController();
2775
- const timeoutId = setTimeout(() => controller.abort(), this.timeout);
3086
+ const timeoutId = setTimeout(() => {
3087
+ controller.abort();
3088
+ }, this.timeout);
2776
3089
  try {
2777
3090
  const response = await fetch(url, {
2778
3091
  ...options,
2779
- signal: controller.signal,
2780
3092
  headers: {
2781
3093
  Authorization: `Key ${this.apiKey}`,
2782
3094
  "Content-Type": "application/json",
2783
- ...options.headers
2784
- }
3095
+ ...toHeaderRecord(options.headers)
3096
+ },
3097
+ signal: controller.signal
2785
3098
  });
2786
3099
  clearTimeout(timeoutId);
2787
3100
  if ((response.status === 429 || response.status >= 500) && attempt < this.retries) {
2788
3101
  const delay = 1e3 * 2 ** attempt;
2789
- await new Promise((r) => setTimeout(r, delay));
3102
+ await new Promise((r) => {
3103
+ setTimeout(r, delay);
3104
+ });
2790
3105
  continue;
2791
3106
  }
2792
3107
  if (!response.ok) {
@@ -2810,38 +3125,45 @@ var MotifServer = class {
2810
3125
  );
2811
3126
  if (attempt < this.retries) {
2812
3127
  const delay = 1e3 * 2 ** attempt;
2813
- await new Promise((r) => setTimeout(r, delay));
3128
+ await new Promise((r) => {
3129
+ setTimeout(r, delay);
3130
+ });
2814
3131
  }
2815
3132
  }
2816
3133
  }
2817
3134
  return err(lastError ?? new MotifError("Request failed after retries", 0));
2818
3135
  }
2819
3136
  ephemeralHeaders(options) {
2820
- return options.ephemeral ? { "X-Fal-Store-IO": "0" } : {};
3137
+ return options.ephemeral === true ? { "X-Fal-Store-IO": "0" } : {};
2821
3138
  }
2822
3139
  /**
2823
3140
  * Normalize fal.ai responses.
2824
3141
  * Some APIs return `{ image: {...} }` instead of `{ images: [...] }`.
2825
3142
  */
2826
3143
  normalizeResponse(data, fallbackRequestId) {
2827
- const obj = data;
2828
- const requestId = obj.request_id ?? obj.requestId ?? fallbackRequestId;
2829
- if ("detail" in obj) {
3144
+ if (!isRecord(data)) {
2830
3145
  return err(
2831
- new MotifError(obj.detail, 0, "FAL_ERROR")
3146
+ new MotifError("Unexpected fal response shape", 0, "FAL_ERROR")
2832
3147
  );
2833
3148
  }
3149
+ const obj = data;
3150
+ const requestId = asString(obj.request_id) ?? asString(obj.requestId) ?? fallbackRequestId;
3151
+ if ("detail" in obj) {
3152
+ return err(new MotifError(asString(obj.detail) ?? "", 0, "FAL_ERROR"));
3153
+ }
2834
3154
  if ("image" in obj && !("images" in obj)) {
2835
3155
  return ok({
2836
- images: [obj.image],
2837
- seed: obj.seed,
2838
- prompt: obj.prompt,
2839
- requestId
3156
+ images: [toMotifImage(obj.image)],
3157
+ prompt: asString(obj.prompt),
3158
+ requestId,
3159
+ seed: asNumber(obj.seed)
2840
3160
  });
2841
3161
  }
2842
3162
  return ok({
2843
- ...obj,
2844
- requestId
3163
+ images: parseImages(obj.images),
3164
+ prompt: asString(obj.prompt),
3165
+ requestId,
3166
+ seed: asNumber(obj.seed)
2845
3167
  });
2846
3168
  }
2847
3169
  };