@howells/motif-sdk 0.5.0 → 0.6.0

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