@howells/motif-sdk 6.0.0 → 7.0.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
@@ -313,7 +313,7 @@ const CREATIVE_TAXONOMY = {
313
313
  description: "Natural, unposed documentary portraits of people. Pair with a mood for the light.",
314
314
  id: "portrait",
315
315
  label: "Documentary portrait",
316
- model: "seedream45"
316
+ model: "seedream5"
317
317
  },
318
318
  {
319
319
  acceptsMood: false,
@@ -700,7 +700,6 @@ const AA_IMAGE_LEADERBOARD_SNAPSHOT = "2026-10-07";
700
700
  const AA_IMAGE_SOURCES = ["https://artificialanalysis.ai/image/leaderboard/text-to-image", "https://artificialanalysis.ai/image/leaderboard/editing"];
701
701
  const FAL_PRICING_CHECKED_AT = "2026-05-12";
702
702
  const FAL_PRICING_CHECKED_JUL_2026 = "2026-07-11";
703
- const FAL_PRICING_CHECKED_AUG_2026 = "2026-08-23";
704
703
  const FAL_PRICING_CHECKED_SEP_2026 = "2026-09-16";
705
704
  const FAL_PRICING_CHECKED_OCT_2026 = "2026-10-29";
706
705
  const MODELS = {
@@ -999,7 +998,7 @@ const MODELS = {
999
998
  quality: "frontier",
1000
999
  speed: "unknown"
1001
1000
  },
1002
- useCase: "Previous-generation Gemini workhorse; retains 0.5K output"
1001
+ useCase: "Gemini workhorse with 0.5K output"
1003
1002
  },
1004
1003
  editEndpoint: "fal-ai/nano-banana-2/edit",
1005
1004
  endpoint: "fal-ai/nano-banana-2",
@@ -1104,211 +1103,6 @@ const MODELS = {
1104
1103
  supportsWebSearch: true,
1105
1104
  type: "generation"
1106
1105
  },
1107
- gemini: {
1108
- benchmark: { artificialAnalysis: {
1109
- editing: {
1110
- elo: 987,
1111
- rank: 50
1112
- },
1113
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
1114
- sourceUrls: AA_IMAGE_SOURCES,
1115
- textToImage: {
1116
- elo: 985,
1117
- rank: 60
1118
- }
1119
- } },
1120
- editEndpoint: "fal-ai/gemini-25-flash-image/edit",
1121
- endpoint: "fal-ai/gemini-25-flash-image",
1122
- falPricing: {
1123
- checkedAt: FAL_PRICING_CHECKED_AT,
1124
- currency: "USD",
1125
- endpointId: "fal-ai/gemini-25-flash-image",
1126
- estimatedCostPerImageUsd: .0398,
1127
- source: "fal-pricing-api",
1128
- unit: "images",
1129
- unitPrice: .0398
1130
- },
1131
- maxReferenceImages: 4,
1132
- name: "Gemini 2.5 Flash",
1133
- pricePerImageUsd: .0398,
1134
- pricing: "$0.04",
1135
- sizeMode: "aspect_ratio",
1136
- supportedAspects: [
1137
- "21:9",
1138
- "16:9",
1139
- "3:2",
1140
- "4:3",
1141
- "5:4",
1142
- "1:1",
1143
- "4:5",
1144
- "3:4",
1145
- "2:3",
1146
- "9:16"
1147
- ],
1148
- supportsAspect: true,
1149
- supportsEdit: true,
1150
- supportsNumImages: true,
1151
- supportsOutputFormat: true,
1152
- supportsResolution: false,
1153
- supportsSafetyTolerance: true,
1154
- supportsSeed: true,
1155
- supportsSyncMode: true,
1156
- type: "generation"
1157
- },
1158
- gemini3: {
1159
- editEndpoint: "fal-ai/gemini-3-pro-image-preview/edit",
1160
- endpoint: "fal-ai/gemini-3-pro-image-preview",
1161
- falPricing: {
1162
- checkedAt: FAL_PRICING_CHECKED_AT,
1163
- currency: "USD",
1164
- endpointId: "fal-ai/gemini-3-pro-image-preview",
1165
- estimatedCostPerImageUsd: .15,
1166
- source: "fal-pricing-api",
1167
- unit: "images",
1168
- unitPrice: .15
1169
- },
1170
- maxReferenceImages: 4,
1171
- name: "Gemini 3 Pro",
1172
- pricePerImageUsd: .15,
1173
- pricing: "$0.15",
1174
- sizeMode: "aspect_ratio",
1175
- supportedAspects: [
1176
- "auto",
1177
- "21:9",
1178
- "16:9",
1179
- "3:2",
1180
- "4:3",
1181
- "5:4",
1182
- "1:1",
1183
- "4:5",
1184
- "3:4",
1185
- "2:3",
1186
- "9:16"
1187
- ],
1188
- supportedResolutions: [
1189
- "1K",
1190
- "2K",
1191
- "4K"
1192
- ],
1193
- supportsAspect: true,
1194
- supportsEdit: true,
1195
- supportsNumImages: true,
1196
- supportsOutputFormat: true,
1197
- supportsResolution: true,
1198
- supportsSafetyTolerance: true,
1199
- supportsSeed: true,
1200
- supportsSyncMode: true,
1201
- supportsWebSearch: true,
1202
- type: "generation"
1203
- },
1204
- seedream4: {
1205
- benchmark: {
1206
- artificialAnalysis: {
1207
- editing: {
1208
- elo: 1015,
1209
- rank: 36
1210
- },
1211
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
1212
- sourceUrls: AA_IMAGE_SOURCES,
1213
- textToImage: {
1214
- elo: 1030,
1215
- rank: 21
1216
- }
1217
- },
1218
- speed: {
1219
- medianSeconds: 14.3,
1220
- p95Seconds: 21,
1221
- source: "artificial-analysis-models"
1222
- },
1223
- tiers: {
1224
- price: "budget",
1225
- quality: "best",
1226
- speed: "balanced"
1227
- },
1228
- useCase: "Legacy budget generation and large multi-reference edits"
1229
- },
1230
- customImageSize: {
1231
- maxPixels: 16777216,
1232
- minPixels: 921600
1233
- },
1234
- editEndpoint: "fal-ai/bytedance/seedream/v4/edit",
1235
- endpoint: "fal-ai/bytedance/seedream/v4/text-to-image",
1236
- falPricing: {
1237
- checkedAt: FAL_PRICING_CHECKED_AT,
1238
- currency: "USD",
1239
- endpointId: "fal-ai/bytedance/seedream/v4/text-to-image",
1240
- estimatedCostPerImageUsd: .03,
1241
- source: "fal-pricing-api",
1242
- unit: "images",
1243
- unitPrice: .03
1244
- },
1245
- maxReferenceImages: 10,
1246
- name: "Seedream 4.0",
1247
- pricePerImageUsd: .03,
1248
- pricing: "$0.03",
1249
- sizeMode: "image_size_enum",
1250
- supportsAspect: true,
1251
- supportsEdit: true,
1252
- supportsNumImages: true,
1253
- supportsResolution: false,
1254
- supportsSeed: true,
1255
- supportsSyncMode: true,
1256
- type: "generation"
1257
- },
1258
- seedream45: {
1259
- benchmark: {
1260
- artificialAnalysis: {
1261
- editing: {
1262
- elo: 1037,
1263
- rank: 32
1264
- },
1265
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
1266
- sourceUrls: AA_IMAGE_SOURCES,
1267
- textToImage: {
1268
- elo: 1022,
1269
- rank: 26
1270
- }
1271
- },
1272
- speed: {
1273
- medianSeconds: 18.2,
1274
- p95Seconds: 43.3,
1275
- source: "artificial-analysis-models"
1276
- },
1277
- tiers: {
1278
- price: "standard",
1279
- quality: "better",
1280
- speed: "balanced"
1281
- },
1282
- useCase: "Cheap current Seedream model with good animation/art scores"
1283
- },
1284
- customImageSize: {
1285
- maxPixels: 16777216,
1286
- minPixels: 3686400
1287
- },
1288
- editEndpoint: "fal-ai/bytedance/seedream/v4.5/edit",
1289
- endpoint: "fal-ai/bytedance/seedream/v4.5/text-to-image",
1290
- falPricing: {
1291
- checkedAt: FAL_PRICING_CHECKED_AT,
1292
- currency: "USD",
1293
- endpointId: "fal-ai/bytedance/seedream/v4.5/text-to-image",
1294
- estimatedCostPerImageUsd: .04,
1295
- source: "fal-pricing-api",
1296
- unit: "images",
1297
- unitPrice: .04
1298
- },
1299
- maxReferenceImages: 10,
1300
- name: "Seedream 4.5",
1301
- pricePerImageUsd: .04,
1302
- pricing: "$0.04",
1303
- sizeMode: "image_size_enum",
1304
- supportsAspect: true,
1305
- supportsEdit: true,
1306
- supportsNumImages: true,
1307
- supportsResolution: false,
1308
- supportsSeed: true,
1309
- supportsSyncMode: true,
1310
- type: "generation"
1311
- },
1312
1106
  seedream5: {
1313
1107
  benchmark: { artificialAnalysis: {
1314
1108
  editing: {
@@ -1392,65 +1186,6 @@ const MODELS = {
1392
1186
  supportsSyncMode: true,
1393
1187
  maxReferenceImages: 10
1394
1188
  },
1395
- "flux2-max": {
1396
- benchmark: {
1397
- artificialAnalysis: {
1398
- editing: {
1399
- elo: 997,
1400
- rank: 49
1401
- },
1402
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
1403
- sourceUrls: AA_IMAGE_SOURCES,
1404
- textToImage: {
1405
- elo: 1020,
1406
- rank: 28
1407
- }
1408
- },
1409
- speed: {
1410
- medianSeconds: 31.6,
1411
- p95Seconds: 41,
1412
- source: "artificial-analysis-models"
1413
- },
1414
- tiers: {
1415
- price: "premium",
1416
- quality: "best",
1417
- speed: "slow"
1418
- },
1419
- useCase: "Best FLUX quality and high-end editing"
1420
- },
1421
- customImageSize: {
1422
- maxEdge: 2560,
1423
- maxPixels: 4194304,
1424
- minEdge: 256,
1425
- multipleOf: 16
1426
- },
1427
- editEndpoint: "fal-ai/flux-2-max/edit",
1428
- endpoint: "fal-ai/flux-2-max",
1429
- falPricing: {
1430
- checkedAt: FAL_PRICING_CHECKED_AT,
1431
- currency: "USD",
1432
- endpointId: "fal-ai/flux-2-max",
1433
- estimatedCostPerImageUsd: .07,
1434
- source: "fal-pricing-api",
1435
- unit: "megapixels",
1436
- unitPrice: .07
1437
- },
1438
- maxReferenceImages: 10,
1439
- name: "FLUX.2 Max",
1440
- pricePerImageUsd: .07,
1441
- pricing: "$0.07/MP",
1442
- sizeMode: "image_size_enum",
1443
- supportsAspect: true,
1444
- supportsEdit: true,
1445
- supportsNumImages: false,
1446
- supportsOutputFormat: true,
1447
- supportsResolution: false,
1448
- supportsSafetyChecker: true,
1449
- supportsSafetyTolerance: true,
1450
- supportsSeed: true,
1451
- supportsSyncMode: true,
1452
- type: "generation"
1453
- },
1454
1189
  "flux2-pro": {
1455
1190
  benchmark: {
1456
1191
  artificialAnalysis: {
@@ -1572,69 +1307,6 @@ const MODELS = {
1572
1307
  supportsSyncMode: true,
1573
1308
  type: "generation"
1574
1309
  },
1575
- "flux2-dev": {
1576
- benchmark: {
1577
- artificialAnalysis: {
1578
- editing: {
1579
- elo: 1e3,
1580
- rank: 45
1581
- },
1582
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
1583
- sourceUrls: AA_IMAGE_SOURCES,
1584
- textToImage: {
1585
- elo: 1e3,
1586
- rank: 42
1587
- }
1588
- },
1589
- speed: {
1590
- medianSeconds: 4.6,
1591
- p95Seconds: 69.6,
1592
- source: "artificial-analysis-models"
1593
- },
1594
- tiers: {
1595
- price: "variable",
1596
- quality: "better",
1597
- speed: "fast"
1598
- },
1599
- useCase: "Open FLUX.2 quality with low average cost"
1600
- },
1601
- customImageSize: {
1602
- maxEdge: 2048,
1603
- minEdge: 512
1604
- },
1605
- editEndpoint: "fal-ai/flux-2/edit",
1606
- endpoint: "fal-ai/flux-2",
1607
- falPricing: {
1608
- checkedAt: FAL_PRICING_CHECKED_AT,
1609
- currency: "USD",
1610
- endpointId: "fal-ai/flux-2",
1611
- estimatedCostPerImageUsd: .012,
1612
- source: "fal-pricing-api",
1613
- unit: "compute seconds",
1614
- unitPrice: .00167
1615
- },
1616
- maxReferenceImages: 4,
1617
- name: "FLUX.2 [dev]",
1618
- pricePerImageUsd: .012,
1619
- pricing: "$0.00167/sec",
1620
- sizeMode: "image_size_enum",
1621
- supportsAspect: true,
1622
- streaming: {
1623
- generation: true,
1624
- edit: true
1625
- },
1626
- supportsEdit: true,
1627
- supportsGuidanceScale: true,
1628
- supportsInferenceSteps: true,
1629
- supportsNumImages: true,
1630
- supportsOutputFormat: true,
1631
- supportsResolution: false,
1632
- supportsSafetyChecker: true,
1633
- supportsSafetyTolerance: true,
1634
- supportsSeed: true,
1635
- supportsSyncMode: true,
1636
- type: "generation"
1637
- },
1638
1310
  "flux2-turbo": {
1639
1311
  customImageSize: {
1640
1312
  maxEdge: 2048,
@@ -1665,188 +1337,6 @@ const MODELS = {
1665
1337
  supportsSyncMode: true,
1666
1338
  type: "generation"
1667
1339
  },
1668
- flux: {
1669
- endpoint: "fal-ai/flux-pro/v1.1-ultra",
1670
- falPricing: {
1671
- checkedAt: FAL_PRICING_CHECKED_AT,
1672
- currency: "USD",
1673
- endpointId: "fal-ai/flux-pro/v1.1-ultra",
1674
- estimatedCostPerImageUsd: .06,
1675
- source: "fal-pricing-api",
1676
- unit: "images",
1677
- unitPrice: .06
1678
- },
1679
- maxReferenceImages: 1,
1680
- name: "FLUX Pro Ultra",
1681
- pricePerImageUsd: .06,
1682
- pricing: "$0.06",
1683
- sizeMode: "aspect_ratio",
1684
- supportsAspect: true,
1685
- supportsEdit: true,
1686
- supportsEnhancePrompt: true,
1687
- supportsImagePromptStrength: true,
1688
- supportsNumImages: true,
1689
- supportsOutputFormat: true,
1690
- supportsRaw: true,
1691
- supportsResolution: false,
1692
- supportsSafetyTolerance: true,
1693
- supportsSeed: true,
1694
- supportsSyncMode: true,
1695
- type: "generation"
1696
- },
1697
- "flux-fast": {
1698
- customImageSize: {},
1699
- endpoint: "fal-ai/flux/schnell",
1700
- falPricing: {
1701
- checkedAt: FAL_PRICING_CHECKED_AT,
1702
- currency: "USD",
1703
- endpointId: "fal-ai/flux/schnell",
1704
- estimatedCostPerImageUsd: .003,
1705
- source: "fal-pricing-api",
1706
- unit: "megapixels",
1707
- unitPrice: .003
1708
- },
1709
- name: "FLUX Schnell",
1710
- pricePerImageUsd: .003,
1711
- pricing: "$0.003",
1712
- sizeMode: "image_size_enum",
1713
- supportsAspect: true,
1714
- supportsEdit: false,
1715
- supportsGuidanceScale: true,
1716
- supportsInferenceSteps: true,
1717
- supportsNumImages: true,
1718
- supportsOutputFormat: true,
1719
- supportsResolution: false,
1720
- supportsSeed: true,
1721
- supportsSyncMode: true,
1722
- type: "generation"
1723
- },
1724
- recraft: {
1725
- customImageSize: {},
1726
- endpoint: "fal-ai/recraft/v3/text-to-image",
1727
- falPricing: {
1728
- checkedAt: FAL_PRICING_CHECKED_AT,
1729
- currency: "USD",
1730
- endpointId: "fal-ai/recraft/v3/text-to-image",
1731
- estimatedCostPerImageUsd: .04,
1732
- source: "fal-pricing-api",
1733
- unit: "images",
1734
- unitPrice: .04
1735
- },
1736
- name: "Recraft V3",
1737
- pricePerImageUsd: .04,
1738
- pricing: "$0.04",
1739
- sizeMode: "image_size_enum",
1740
- supportsAspect: true,
1741
- supportsEdit: false,
1742
- supportsNumImages: false,
1743
- supportsResolution: false,
1744
- supportsStyle: true,
1745
- supportsSyncMode: true,
1746
- type: "generation"
1747
- },
1748
- reve: {
1749
- benchmark: {
1750
- tiers: {
1751
- price: "premium",
1752
- quality: "frontier",
1753
- speed: "unknown"
1754
- },
1755
- useCase: "Historical Reve route; not currently verified on fal"
1756
- },
1757
- editEndpoint: "fal-ai/reve/edit",
1758
- endpoint: "fal-ai/reve/text-to-image",
1759
- falPricing: {
1760
- checkedAt: FAL_PRICING_CHECKED_AUG_2026,
1761
- currency: "USD",
1762
- endpointId: "fal-ai/reve/text-to-image",
1763
- estimatedCostPerImageUsd: .04,
1764
- source: "fal-pricing-api",
1765
- unit: "images",
1766
- unitPrice: .04
1767
- },
1768
- maxReferenceImages: 1,
1769
- name: "Reve 2.1",
1770
- pricePerImageUsd: .04,
1771
- pricing: "$0.04",
1772
- sizeMode: "aspect_ratio",
1773
- supportedAspects: [
1774
- "16:9",
1775
- "9:16",
1776
- "3:2",
1777
- "2:3",
1778
- "4:3",
1779
- "3:4",
1780
- "1:1"
1781
- ],
1782
- supportsAspect: true,
1783
- supportsEdit: true,
1784
- supportsNumImages: true,
1785
- supportsOutputFormat: true,
1786
- supportsResolution: false,
1787
- supportsSeed: false,
1788
- supportsSyncMode: true,
1789
- type: "generation"
1790
- },
1791
- recraft4: {
1792
- benchmark: { artificialAnalysis: {
1793
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
1794
- sourceUrls: AA_IMAGE_SOURCES,
1795
- textToImage: {
1796
- elo: 984,
1797
- rank: 62
1798
- }
1799
- } },
1800
- customImageSize: {},
1801
- endpoint: "fal-ai/recraft/v4/text-to-image",
1802
- falPricing: {
1803
- checkedAt: FAL_PRICING_CHECKED_JUL_2026,
1804
- currency: "USD",
1805
- endpointId: "fal-ai/recraft/v4/text-to-image",
1806
- estimatedCostPerImageUsd: .04,
1807
- source: "fal-pricing-api",
1808
- unit: "images",
1809
- unitPrice: .04
1810
- },
1811
- name: "Recraft V4",
1812
- pricePerImageUsd: .04,
1813
- pricing: "$0.04",
1814
- sizeMode: "image_size_enum",
1815
- supportsAspect: true,
1816
- supportsEdit: false,
1817
- supportsNumImages: false,
1818
- supportsResolution: false,
1819
- supportsSyncMode: true,
1820
- type: "generation"
1821
- },
1822
- ideogram: {
1823
- customImageSize: {},
1824
- endpoint: "fal-ai/ideogram/v3",
1825
- falPricing: {
1826
- checkedAt: FAL_PRICING_CHECKED_AT,
1827
- currency: "USD",
1828
- endpointId: "fal-ai/ideogram/v3",
1829
- estimatedCostPerImageUsd: .03,
1830
- source: "fal-pricing-api",
1831
- unit: "images",
1832
- unitPrice: .03
1833
- },
1834
- name: "Ideogram V3",
1835
- pricePerImageUsd: .03,
1836
- pricing: "$0.03",
1837
- sizeMode: "image_size_enum",
1838
- supportsAspect: true,
1839
- supportsEdit: false,
1840
- supportsExpandPrompt: true,
1841
- supportsNegativePrompt: true,
1842
- supportsNumImages: true,
1843
- supportsRenderingSpeed: true,
1844
- supportsResolution: false,
1845
- supportsSeed: true,
1846
- supportsStyle: true,
1847
- supportsSyncMode: true,
1848
- type: "generation"
1849
- },
1850
1340
  ideogram4: {
1851
1341
  benchmark: { artificialAnalysis: {
1852
1342
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
@@ -1947,52 +1437,6 @@ const MODELS = {
1947
1437
  supportsSyncMode: true,
1948
1438
  type: "generation"
1949
1439
  },
1950
- qwen: {
1951
- benchmark: {
1952
- artificialAnalysis: {
1953
- snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
1954
- sourceUrls: AA_IMAGE_SOURCES,
1955
- textToImage: {
1956
- elo: 890,
1957
- rank: 106
1958
- }
1959
- },
1960
- speed: {
1961
- medianSeconds: 23.3,
1962
- p95Seconds: 491,
1963
- source: "artificial-analysis-models"
1964
- },
1965
- tiers: {
1966
- price: "budget",
1967
- quality: "better",
1968
- speed: "slow"
1969
- },
1970
- useCase: "Low-cost open-weight text and design generation"
1971
- },
1972
- customImageSize: {},
1973
- endpoint: "fal-ai/qwen-image",
1974
- falPricing: {
1975
- checkedAt: FAL_PRICING_CHECKED_AT,
1976
- currency: "USD",
1977
- endpointId: "fal-ai/qwen-image",
1978
- estimatedCostPerImageUsd: .02,
1979
- source: "fal-pricing-api",
1980
- unit: "megapixels",
1981
- unitPrice: .02
1982
- },
1983
- name: "Qwen Image",
1984
- pricePerImageUsd: .02,
1985
- pricing: "$0.02/MP",
1986
- sizeMode: "image_size_enum",
1987
- supportsAspect: true,
1988
- supportsEdit: false,
1989
- supportsNumImages: true,
1990
- supportsOutputFormat: true,
1991
- supportsResolution: false,
1992
- supportsSeed: true,
1993
- supportsSyncMode: true,
1994
- type: "generation"
1995
- },
1996
1440
  qwen3: {
1997
1441
  benchmark: {
1998
1442
  artificialAnalysis: {
@@ -2393,29 +1837,9 @@ const MODELS = {
2393
1837
  supportsNumImages: false,
2394
1838
  supportsResolution: false,
2395
1839
  type: "utility"
2396
- },
2397
- rmbg: {
2398
- endpoint: "fal-ai/birefnet",
2399
- name: "BiRefNet (Background Removal)",
2400
- pricing: "$0.02",
2401
- supportsAspect: false,
2402
- supportsEdit: false,
2403
- supportsNumImages: false,
2404
- supportsResolution: false,
2405
- type: "utility"
2406
- },
2407
- bria: {
2408
- endpoint: "fal-ai/bria/background/remove",
2409
- name: "Bria RMBG 2.0",
2410
- pricing: "$0.02",
2411
- supportsAspect: false,
2412
- supportsEdit: false,
2413
- supportsNumImages: false,
2414
- supportsResolution: false,
2415
- type: "utility"
2416
1840
  }
2417
1841
  };
2418
- /** Curated generation choices; predecessor routes are explicit-only. */
1842
+ /** Curated generation Models, the only ones Motif recommends. */
2419
1843
  const GENERATION_MODELS = [
2420
1844
  "sunburst",
2421
1845
  "flare",
@@ -2441,28 +1865,7 @@ const GENERATION_MODELS = [
2441
1865
  "ideogram3-transparent",
2442
1866
  "recraft41"
2443
1867
  ];
2444
- /** Retained for old pins, explicit overrides and tuned Looks, never auto-selected. */
2445
- const LEGACY_GENERATION_MODELS = [
2446
- "gemini",
2447
- "gemini3",
2448
- "seedream4",
2449
- "seedream45",
2450
- "flux2-max",
2451
- "flux2-dev",
2452
- "flux",
2453
- "flux-fast",
2454
- "recraft",
2455
- "recraft4",
2456
- "ideogram",
2457
- "qwen"
2458
- ];
2459
1868
  GENERATION_MODELS.filter((id) => MODELS[id]?.supportsEdit === true);
2460
- const UTILITY_MODELS = [
2461
- "clarity",
2462
- "crystal",
2463
- "rmbg",
2464
- "bria"
2465
- ];
2466
1869
  //#endregion
2467
1870
  //#region src/cost.ts
2468
1871
  /**
@@ -2476,7 +1879,7 @@ function estimateCost(model, resolution, numImages = 1, editImages = 0) {
2476
1879
  if (model === "grok-image-2" && editImages > 0) return (resolution === "2K" || resolution === "4K" ? .08 : .06) * numImages + .01 * editImages;
2477
1880
  if (model === "grok-image-quality") return (resolution === "2K" || resolution === "4K" ? .07 : .05) * numImages + .01 * editImages;
2478
1881
  if (configuredPrice !== void 0) {
2479
- if ((model === "banana" || model === "gemini3") && resolution === "4K") return configuredPrice * 2 * numImages;
1882
+ if (model === "banana" && resolution === "4K") return configuredPrice * 2 * numImages;
2480
1883
  if (model === "seedream5" && (resolution === "2K" || resolution === "4K")) return configuredPrice * 2 * numImages;
2481
1884
  if (model === "grok-image-2" && (resolution === "2K" || resolution === "4K")) return .08 * numImages;
2482
1885
  if (model === "banana2") return configuredPrice * (resolution === "4K" ? 2 : resolution === "2K" ? 1.5 : resolution === "0.5K" ? .75 : 1) * numImages;
@@ -2484,17 +1887,8 @@ function estimateCost(model, resolution, numImages = 1, editImages = 0) {
2484
1887
  }
2485
1888
  switch (model) {
2486
1889
  case "gpt": return .133 * numImages;
2487
- case "banana":
2488
- case "gemini3": return (resolution === "4K" ? .3 : .15) * numImages;
2489
- case "gemini": return .039 * numImages;
2490
- case "flux":
2491
- case "ideogram": return .06 * numImages;
2492
- case "recraft": return .04 * numImages;
2493
- case "flux-fast": return .003 * numImages;
2494
1890
  case "clarity": return .03 * numImages;
2495
- case "crystal":
2496
- case "rmbg":
2497
- case "bria": return .02 * numImages;
1891
+ case "crystal": return .02 * numImages;
2498
1892
  default: return .1 * numImages;
2499
1893
  }
2500
1894
  }
@@ -2638,11 +2032,9 @@ const OPTION_CAPABILITIES = {
2638
2032
  enableGoogleSearch: (config) => config.supportsGoogleSearch === true,
2639
2033
  enableSafetyChecker: (config) => config.supportsSafetyChecker === true,
2640
2034
  enableWebSearch: (config) => config.supportsWebSearch === true,
2641
- enhancePrompt: (config) => config.supportsEnhancePrompt === true,
2642
2035
  expandPrompt: (config) => config.supportsExpandPrompt === true,
2643
2036
  guidanceScale: (config) => config.supportsGuidanceScale === true,
2644
2037
  "image editing": (config) => config.supportsEdit,
2645
- imagePromptStrength: (config) => config.supportsImagePromptStrength === true,
2646
2038
  imageSize: (config) => config.sizeMode === "gpt_size" || config.sizeMode === "image_size_enum",
2647
2039
  inputFidelity: (config) => config.sizeMode === "gpt_size",
2648
2040
  limitGenerations: (config) => config.supportsLimitGenerations === true,
@@ -2653,12 +2045,10 @@ const OPTION_CAPABILITIES = {
2653
2045
  numInferenceSteps: (config) => config.supportsInferenceSteps === true,
2654
2046
  outputFormat: (config) => config.supportsOutputFormat === true,
2655
2047
  quality: (config) => config.supportsQuality === true,
2656
- raw: (config) => config.supportsRaw === true,
2657
2048
  renderingSpeed: (config) => config.supportsRenderingSpeed === true,
2658
2049
  resolution: (config) => config.supportsResolution,
2659
2050
  safetyTolerance: (config) => config.supportsSafetyTolerance === true,
2660
2051
  seed: (config) => config.supportsSeed === true,
2661
- style: (config) => config.supportsStyle === true,
2662
2052
  syncMode: (config) => config.supportsSyncMode === true,
2663
2053
  thinkingLevel: (config) => config.supportsThinkingLevel === true,
2664
2054
  "transparent output": (config) => config.supportsBackground === true || config.transparentOutput === true
@@ -2686,7 +2076,7 @@ function modelsSupporting(option) {
2686
2076
  */
2687
2077
  var UnsupportedOptionError = class extends Error {
2688
2078
  code = "INVALID_OPTION";
2689
- /** Display name of the model that refused, e.g. `"Seedream 4.5"`. */
2079
+ /** Display name of the model that refused, e.g. `"Seedream 5 Pro"`. */
2690
2080
  model;
2691
2081
  /** Generation model ids that accept `option`. */
2692
2082
  modelsSupporting;
@@ -2775,8 +2165,6 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
2775
2165
  if (config.sizeMode !== "gpt_size") unsupported(config, "inputFidelity");
2776
2166
  }
2777
2167
  if (options.imageSize !== void 0 && sizeMode !== "gpt_size" && sizeMode !== "image_size_enum") unsupported(config, "imageSize");
2778
- if (options.imagePromptStrength !== void 0 && config.supportsImagePromptStrength !== true) unsupported(config, "imagePromptStrength");
2779
- if (options.imagePromptStrength !== void 0 && !hasEditImages) throw new Error("imagePromptStrength requires editImageUrls");
2780
2168
  if (options.maskImageUrl !== void 0 && config.supportsMaskImage !== true) unsupported(config, "maskImageUrl");
2781
2169
  if (options.maskImageUrl !== void 0 && !hasEditImages) throw new Error("maskImageUrl requires editImageUrls");
2782
2170
  if (options.markupImageUrl !== void 0 && config.supportsMarkupImage !== true) unsupported(config, "markupImageUrl");
@@ -2795,13 +2183,10 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
2795
2183
  if (!qualities.includes(options.quality)) throw new Error(`${config.name} quality must be one of ${qualities.join(", ")}`);
2796
2184
  }
2797
2185
  if (options.negativePrompt !== void 0 && config.supportsNegativePrompt !== true) unsupported(config, "negativePrompt");
2798
- if (options.style !== void 0 && config.supportsStyle !== true) unsupported(config, "style");
2799
2186
  if (options.syncMode !== void 0 && config.supportsSyncMode !== true) unsupported(config, "syncMode");
2800
2187
  if (options.renderingSpeed !== void 0 && config.supportsRenderingSpeed !== true) unsupported(config, "renderingSpeed");
2801
2188
  if (options.guidanceScale !== void 0 && config.supportsGuidanceScale !== true) unsupported(config, "guidanceScale");
2802
2189
  if (options.numInferenceSteps !== void 0 && config.supportsInferenceSteps !== true) unsupported(config, "numInferenceSteps");
2803
- if (options.raw !== void 0 && config.supportsRaw !== true) unsupported(config, "raw");
2804
- if (options.enhancePrompt !== void 0 && config.supportsEnhancePrompt !== true) unsupported(config, "enhancePrompt");
2805
2190
  if (options.safetyTolerance !== void 0 && config.supportsSafetyTolerance !== true) unsupported(config, "safetyTolerance");
2806
2191
  if (options.enableWebSearch !== void 0 && config.supportsWebSearch !== true) unsupported(config, "enableWebSearch");
2807
2192
  if (options.enableGoogleSearch !== void 0 && config.supportsGoogleSearch !== true) unsupported(config, "enableGoogleSearch");
@@ -2821,7 +2206,7 @@ function validateGenerateOptions(options, config, sizeMode, hasEditImages) {
2821
2206
  * - `none`: no size control (video models)
2822
2207
  */
2823
2208
  function buildGenerateBody(options) {
2824
- const { prompt, model, creative, aspect = "1:1", resolution = "2K", numImages = 1, background, editImageUrls, enableGoogleSearch, enableSafetyChecker, transparent, inputFidelity, imageSize, imagePromptStrength, limitGenerations, maskImageUrl, markupImageUrl, seed, outputFormat, quality, negativePrompt, style, syncMode, thinkingLevel, renderingSpeed, guidanceScale, numInferenceSteps, raw, enhancePrompt, safetyTolerance, enableWebSearch, expandPrompt } = options;
2209
+ const { prompt, model, creative, aspect = "1:1", resolution = "2K", numImages = 1, background, editImageUrls, enableGoogleSearch, enableSafetyChecker, transparent, inputFidelity, imageSize, limitGenerations, maskImageUrl, markupImageUrl, seed, outputFormat, quality, negativePrompt, syncMode, thinkingLevel, renderingSpeed, guidanceScale, numInferenceSteps, safetyTolerance, enableWebSearch, expandPrompt } = options;
2825
2210
  const config = MODELS[model];
2826
2211
  if (!config) throw new Error(`Unknown model: ${model}`);
2827
2212
  let { endpoint } = config;
@@ -2857,12 +2242,6 @@ function buildGenerateBody(options) {
2857
2242
  if (!formats.includes(outputFormat)) throw new Error(`${config.name} ${hasEditImages ? "edits support" : "supports"} output formats: ${formats.join(", ")}`);
2858
2243
  body.output_format = outputFormat;
2859
2244
  }
2860
- if (config.supportsRaw === true && raw !== void 0) body.raw = raw;
2861
- if (config.supportsEnhancePrompt === true && enhancePrompt !== void 0) body.enhance_prompt = enhancePrompt;
2862
- if (config.supportsImagePromptStrength === true && imagePromptStrength !== void 0) body.image_prompt_strength = imagePromptStrength;
2863
- if (model === "flux") {
2864
- if (safetyTolerance !== void 0 && safetyTolerance !== "") body.safety_tolerance = safetyTolerance;
2865
- }
2866
2245
  if (config.supportsGuidanceScale === true && guidanceScale !== void 0) body.guidance_scale = guidanceScale;
2867
2246
  if (config.supportsInferenceSteps === true && numInferenceSteps !== void 0) body.num_inference_steps = numInferenceSteps;
2868
2247
  if (config.supportsSafetyTolerance === true) {
@@ -2883,25 +2262,17 @@ function buildGenerateBody(options) {
2883
2262
  if (config.supportsThinkingLevel === true) {
2884
2263
  if (thinkingLevel) body.thinking_level = thinkingLevel;
2885
2264
  }
2886
- if (config.supportsStyle === true && style !== void 0 && style !== "") body.style = style;
2887
2265
  if (config.supportsNegativePrompt === true) {
2888
2266
  if (negativePrompt !== void 0 && negativePrompt !== "") body.negative_prompt = negativePrompt;
2889
2267
  }
2890
- if (model === "ideogram") {
2891
- if (style !== void 0 && style !== "") body.style = style;
2892
- }
2893
2268
  if (config.supportsRenderingSpeed === true) {
2894
2269
  if (renderingSpeed) body.rendering_speed = renderingSpeed;
2895
2270
  }
2896
2271
  if (config.supportsExpandPrompt === true) {
2897
2272
  if (expandPrompt !== void 0) body.expand_prompt = expandPrompt;
2898
2273
  }
2899
- if (model === "flux" && hasEditImages) {
2900
- body.image_url = editImages[0];
2901
- body.image_prompt_strength = imagePromptStrength ?? .5;
2902
- }
2903
2274
  if (config.supportsNumImages) body.num_images = numImages;
2904
- if (hasEditImages && sizeMode !== "gpt_size" && model !== "flux") {
2275
+ if (hasEditImages && sizeMode !== "gpt_size") {
2905
2276
  if (!config.supportsEdit) throw new Error(`Model ${model} does not support image editing`);
2906
2277
  endpoint = config.editEndpoint ?? `${endpoint}/edit`;
2907
2278
  const editImagesField = config.editImagesField ?? "image_urls";
@@ -4690,100 +4061,6 @@ var FalClient = class {
4690
4061
  const data = await response.value.json();
4691
4062
  return this.normalizeResponse(data, requestId);
4692
4063
  }
4693
- /** ─── Processing ──────────────────────────────────────────── */
4694
- /** Upscale an image using clarity or crystal upscaler. */
4695
- async upscale(options) {
4696
- const { imageUrl, model = "clarity", scaleFactor, creativity, resemblance, prompt: upscalePrompt, negativePrompt, numInferenceSteps, guidanceScale } = options;
4697
- const config = MODELS[model];
4698
- if (!config || config.type !== "utility") return err$1(new MotifError(`Invalid upscale model: ${model}`, 0));
4699
- const body = { image_url: imageUrl };
4700
- if (model === "crystal") {
4701
- if (scaleFactor !== void 0) body.scale_factor = scaleFactor;
4702
- if (creativity !== void 0) body.creativity = creativity;
4703
- } else {
4704
- if (scaleFactor !== void 0) body.upscale_factor = scaleFactor;
4705
- if (creativity !== void 0) body.creativity = creativity;
4706
- if (resemblance !== void 0) body.resemblance = resemblance;
4707
- if (upscalePrompt !== void 0 && upscalePrompt !== "") body.prompt = upscalePrompt;
4708
- if (negativePrompt !== void 0 && negativePrompt !== "") body.negative_prompt = negativePrompt;
4709
- if (numInferenceSteps !== void 0) body.num_inference_steps = numInferenceSteps;
4710
- if (guidanceScale !== void 0) body.guidance_scale = guidanceScale;
4711
- }
4712
- const response = await this.request(`${FAL_BASE_URL}/${config.endpoint}`, {
4713
- body: JSON.stringify(body),
4714
- method: "POST"
4715
- });
4716
- if (response.isErr()) return err$1(response.error);
4717
- const data = await response.value.json();
4718
- return this.normalizeResponse(data);
4719
- }
4720
- /** Remove the background from an image. */
4721
- async removeBackground(options) {
4722
- const { imageUrl, model = "rmbg", variant, operatingResolution, outputFormat, refineForeground, outputMask } = options;
4723
- const config = MODELS[model];
4724
- if (!config) return err$1(new MotifError(`Invalid background removal model: ${model}`, 0));
4725
- const rbBody = { image_url: imageUrl };
4726
- if (model === "rmbg") {
4727
- if (variant) rbBody.model = variant;
4728
- if (operatingResolution) rbBody.operating_resolution = operatingResolution;
4729
- if (outputFormat) rbBody.output_format = outputFormat;
4730
- if (refineForeground !== void 0) rbBody.refine_foreground = refineForeground;
4731
- if (outputMask !== void 0) rbBody.output_mask = outputMask;
4732
- }
4733
- const response = await this.request(`${FAL_BASE_URL}/${config.endpoint}`, {
4734
- body: JSON.stringify(rbBody),
4735
- method: "POST"
4736
- });
4737
- if (response.isErr()) return err$1(response.error);
4738
- const data = await response.value.json();
4739
- return this.normalizeResponse(data);
4740
- }
4741
- /** ─── Video Generation ───────────────────────────────────── */
4742
- /**
4743
- * Generate a video from an image using Kling v3 Pro.
4744
- * This uses the queue API since video generation takes 30-120s.
4745
- * Returns immediately with a job — poll with getJobStatus/getVideoResult.
4746
- */
4747
- async submitVideo(options) {
4748
- const { imageUrl, prompt, duration = 5, generateAudio = true, endImageUrl, negativePrompt, cfgScale } = options;
4749
- const config = MODELS.kling;
4750
- if (!config) return err$1(new MotifError("Kling video model not found", 0));
4751
- const body = {
4752
- duration: String(duration),
4753
- generate_audio: generateAudio,
4754
- prompt,
4755
- start_image_url: imageUrl
4756
- };
4757
- if (endImageUrl !== void 0 && endImageUrl !== "") body.end_image_url = endImageUrl;
4758
- if (negativePrompt !== void 0 && negativePrompt !== "") body.negative_prompt = negativePrompt;
4759
- if (cfgScale !== void 0) body.cfg_scale = cfgScale;
4760
- const response = await this.request(`${FAL_QUEUE_URL}/${config.endpoint}`, {
4761
- body: JSON.stringify(body),
4762
- method: "POST"
4763
- });
4764
- if (response.isErr()) return err$1(response.error);
4765
- const submission = parseQueueSubmission(await response.value.json());
4766
- return ok$1({
4767
- endpoint: endpointFromQueueUrl(submission.responseUrl, config.endpoint),
4768
- estimatedCost: estimateVideoCost(duration, generateAudio),
4769
- requestId: submission.requestId
4770
- });
4771
- }
4772
- /** Fetch the completed video result from the queue. */
4773
- async getVideoResult(endpoint, requestId) {
4774
- const url = `${FAL_QUEUE_URL}/${endpoint}/requests/${requestId}`;
4775
- const response = await this.request(url);
4776
- if (response.isErr()) return err$1(response.error);
4777
- const data = await response.value.json();
4778
- const video = isRecord$1(data) && isRecord$1(data.video) ? data.video : void 0;
4779
- if (video === void 0) return err$1(new MotifError("No video in response", 0));
4780
- return ok$1({
4781
- contentType: asString(video.content_type) ?? "",
4782
- fileName: asString(video.file_name) ?? "",
4783
- fileSize: asNumber(video.file_size) ?? 0,
4784
- url: asString(video.url) ?? ""
4785
- });
4786
- }
4787
4064
  /** ─── File Upload ─────────────────────────────────────────── */
4788
4065
  /**
4789
4066
  * Upload a file to fal.ai CDN storage and return the public URL.
@@ -4879,10 +4156,6 @@ var FalClient = class {
4879
4156
  get generationModels() {
4880
4157
  return GENERATION_MODELS;
4881
4158
  }
4882
- /** Utility model keys. */
4883
- get utilityModels() {
4884
- return UTILITY_MODELS;
4885
- }
4886
4159
  /** Registered fal utility/tool endpoints. */
4887
4160
  get tools() {
4888
4161
  return FAL_TOOLS;
@@ -5814,7 +5087,7 @@ const TASKS = {
5814
5087
  summary: "Remove an object, person, text or clutter from an image and fill the gap."
5815
5088
  },
5816
5089
  generate: {
5817
- basis: "Order follows Arena text-to-image (https://arena.ai/leaderboard/image/text-to-image), read 2026-10-29 (MOT-72). Sunburst and Flare lead quality at the benchmarked max setting. Balanced starts with Nano Banana 2.1 on preliminary Arena evidence, then Grok Image 2 and Muse Image. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Fast keeps Nano Banana 2 Lite, budget Grok and Seedream 5 Lite, and FLUX.2 Turbo. Design, transparency and tuned Look routes are capability exceptions. Generation with References uses the separate editing ranking. Older routes are explicit/pin/Look-only.",
5090
+ basis: "Order follows Arena text-to-image (https://arena.ai/leaderboard/image/text-to-image), read 2026-10-29. Sunburst and Flare lead quality at the benchmarked max setting. Balanced starts with Nano Banana 2.1 on preliminary Arena evidence, then Grok Image 2 and Muse Image. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Fast keeps Nano Banana 2 Lite, budget Grok and Seedream 5 Lite, and FLUX.2 Turbo. Design, transparency and tuned Look routes are capability exceptions. Generation with References uses the separate editing ranking. Older routes are explicit/pin/Look-only.",
5818
5091
  models: GENERATION_MODELS_RANKED,
5819
5092
  notFor: "Variations of an image you already have (vary), or a consistent set of images (series run).",
5820
5093
  rankedAt: IMAGE_RANKED_AT,
@@ -6302,7 +5575,7 @@ const TASKS = {
6302
5575
  summary: "Trace a raster image to a clean SVG."
6303
5576
  },
6304
5577
  vary: {
6305
- basis: "Order follows Arena image edit, single image (https://arena.ai/leaderboard/image/image-edit/single-image), read 2026-10-29 (MOT-72). Editing order differs from generation: Grok Image 2 leads balanced, ahead of Nano Banana 2.1, and Seedream 5 Pro outranks GPT Image 1.5. Arena scores Krea 3 only at its Ultra setting, which costs $0.27 to $0.35 an edit and ignores the requested size, so krea3, which runs fal's default setting, is ranked as unscored. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Sunburst and Flare use max quality. vary reuses a previously chosen Model when explicitly supplied, including retained legacy routes.",
5578
+ basis: "Order follows Arena image edit, single image (https://arena.ai/leaderboard/image/image-edit/single-image), read 2026-10-29. Editing order differs from generation: Grok Image 2 leads balanced, ahead of Nano Banana 2.1, and Seedream 5 Pro outranks GPT Image 1.5. Arena scores Krea 3 only at its Ultra setting, which costs $0.27 to $0.35 an edit and ignores the requested size, so krea3, which runs fal's default setting, is ranked as unscored. Models not yet on the board follow the ranked ones in their Tier, in their previous order. The Artificial Analysis v2 snapshot of 2026-10-07 stays as recorded benchmark data. Sunburst and Flare use max quality. vary reuses a previously chosen Model when explicitly supplied, including retained legacy routes.",
6306
5579
  models: EDITING_MODELS_RANKED,
6307
5580
  notFor: "A specific change to an image described in words (generate with a reference), or a set of different scenes in one style (series run).",
6308
5581
  rankedAt: IMAGE_RANKED_AT,
@@ -8682,7 +7955,7 @@ function resolveFixed(task, fixed, request, needed, keys) {
8682
7955
  const { chosenBy, model } = fixed;
8683
7956
  const tier = request.tier ?? "balanced";
8684
7957
  const entries = TASKS[task].models;
8685
- const entry = entries.find((candidate) => candidate.model === model && candidate.mode === request.mode) ?? legacyEntry(task, model, request.mode, tier);
7958
+ const entry = entries.find((candidate) => candidate.model === model && candidate.mode === request.mode);
8686
7959
  const profile = modelProfile(model);
8687
7960
  if (entry === void 0 || profile === void 0) {
8688
7961
  const servesTask = entries.some((candidate) => candidate.model === model);
@@ -8695,14 +7968,6 @@ function resolveFixed(task, fixed, request, needed, keys) {
8695
7968
  unblockers.push(unblockerFor(result));
8696
7969
  return unresolved(task, result.blockedBy, unblockers, `${model} (${chosenBy}) ${describeBlock(result.blockedBy, request, result.missingKey, result.missingInput)}.`, result.missingKey);
8697
7970
  }
8698
- /** Old pins and tuned Looks remain usable without recommending predecessors. */
8699
- function legacyEntry(task, model, mode, tier) {
8700
- if (mode !== void 0 || task !== "generate" && task !== "vary" || !LEGACY_GENERATION_MODELS.some((id) => id === model) || task === "vary" && MODELS[model]?.supportsEdit !== true) return;
8701
- return {
8702
- model,
8703
- tier
8704
- };
8705
- }
8706
7971
  function unknownForMode(task, model, mode, entries) {
8707
7972
  const otherModes = entries.filter((candidate) => candidate.model === model).map((candidate) => candidate.mode ?? "no mode");
8708
7973
  const inMode = [...new Set(entries.filter((candidate) => candidate.mode === mode).map((candidate) => candidate.model))];