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