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