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