@howells/motif-sdk 1.1.0 → 1.2.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/image.js CHANGED
@@ -1,27 +1,28 @@
1
1
  // src/image/index.ts
2
2
  import { generateImage } from "ai";
3
- import { err as err2, ok as ok2 } from "neverthrow";
3
+ import { err as err3, ok as ok3 } from "neverthrow";
4
4
 
5
5
  // src/server.ts
6
- import { err, ok } from "neverthrow";
6
+ import { err as err2, ok as ok2 } from "neverthrow";
7
7
 
8
8
  // src/models.ts
9
- var AA_IMAGE_LEADERBOARD_SNAPSHOT = "2026-05-12";
9
+ var AA_IMAGE_LEADERBOARD_SNAPSHOT = "2026-08-23";
10
10
  var AA_IMAGE_SOURCES = [
11
11
  "https://artificialanalysis.ai/image/leaderboard/text-to-image",
12
12
  "https://artificialanalysis.ai/image/leaderboard/editing"
13
13
  ];
14
14
  var FAL_PRICING_CHECKED_AT = "2026-05-12";
15
15
  var FAL_PRICING_CHECKED_JUL_2026 = "2026-07-11";
16
+ var FAL_PRICING_CHECKED_AUG_2026 = "2026-08-23";
16
17
  var MODELS = {
17
18
  // ─── Generation Models ────────────────────────────────────────
18
19
  gpt2: {
19
20
  benchmark: {
20
21
  artificialAnalysis: {
21
- editing: { elo: 1249, pricePer1k: 211, rank: 3, winRate: 0.66 },
22
+ editing: { elo: 1256, pricePer1k: 211, rank: 3 },
22
23
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
23
24
  sourceUrls: AA_IMAGE_SOURCES,
24
- textToImage: { elo: 1337, pricePer1k: 211, rank: 1, winRate: 0.8 }
25
+ textToImage: { elo: 1369, pricePer1k: 211, rank: 1 }
25
26
  },
26
27
  speed: {
27
28
  medianSeconds: 200.7,
@@ -61,10 +62,10 @@ var MODELS = {
61
62
  gpt: {
62
63
  benchmark: {
63
64
  artificialAnalysis: {
64
- editing: { elo: 1262, pricePer1k: 133, rank: 2, winRate: 0.69 },
65
+ editing: { elo: 1250, pricePer1k: 133, rank: 5 },
65
66
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
66
67
  sourceUrls: AA_IMAGE_SOURCES,
67
- textToImage: { elo: 1268, pricePer1k: 133, rank: 2, winRate: 0.73 }
68
+ textToImage: { elo: 1310, pricePer1k: 133, rank: 4 }
68
69
  },
69
70
  speed: {
70
71
  medianSeconds: 34.6,
@@ -104,10 +105,10 @@ var MODELS = {
104
105
  banana2: {
105
106
  benchmark: {
106
107
  artificialAnalysis: {
107
- editing: { elo: 1231, pricePer1k: 67, rank: 5, winRate: 0.63 },
108
+ editing: { elo: 1249, pricePer1k: 67, rank: 7 },
108
109
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
109
110
  sourceUrls: AA_IMAGE_SOURCES,
110
- textToImage: { elo: 1263, pricePer1k: 67, rank: 3, winRate: 0.71 }
111
+ textToImage: { elo: 1320, pricePer1k: 67, rank: 3 }
111
112
  },
112
113
  tiers: { price: "premium", quality: "frontier", speed: "unknown" },
113
114
  useCase: "Best balance of quality, edit support, web search, and cost"
@@ -145,10 +146,10 @@ var MODELS = {
145
146
  banana: {
146
147
  benchmark: {
147
148
  artificialAnalysis: {
148
- editing: { elo: 1241, pricePer1k: 134, rank: 4, winRate: 0.65 },
149
+ editing: { elo: 1245, pricePer1k: 134, rank: 9 },
149
150
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
150
151
  sourceUrls: AA_IMAGE_SOURCES,
151
- textToImage: { elo: 1220, pricePer1k: 134, rank: 5, winRate: 0.66 }
152
+ textToImage: { elo: 1297, pricePer1k: 134, rank: 6 }
152
153
  },
153
154
  speed: {
154
155
  medianSeconds: 19.2,
@@ -188,6 +189,13 @@ var MODELS = {
188
189
  type: "generation"
189
190
  },
190
191
  gemini: {
192
+ benchmark: {
193
+ artificialAnalysis: {
194
+ editing: { elo: 1180, pricePer1k: 39, rank: 29 },
195
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
196
+ sourceUrls: AA_IMAGE_SOURCES
197
+ }
198
+ },
191
199
  editEndpoint: "fal-ai/gemini-25-flash-image/edit",
192
200
  endpoint: "fal-ai/gemini-25-flash-image",
193
201
  falPricing: {
@@ -215,6 +223,14 @@ var MODELS = {
215
223
  type: "generation"
216
224
  },
217
225
  gemini3: {
226
+ benchmark: {
227
+ artificialAnalysis: {
228
+ editing: { elo: 1245, pricePer1k: 134, rank: 9 },
229
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
230
+ sourceUrls: AA_IMAGE_SOURCES,
231
+ textToImage: { elo: 1297, pricePer1k: 134, rank: 6 }
232
+ }
233
+ },
218
234
  editEndpoint: "fal-ai/gemini-3-pro-image-preview/edit",
219
235
  endpoint: "fal-ai/gemini-3-pro-image-preview",
220
236
  falPricing: {
@@ -245,10 +261,10 @@ var MODELS = {
245
261
  seedream4: {
246
262
  benchmark: {
247
263
  artificialAnalysis: {
248
- editing: { elo: 1184, pricePer1k: 30, rank: 16, winRate: 0.61 },
264
+ editing: { elo: 1185, pricePer1k: 30, rank: 28 },
249
265
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
250
266
  sourceUrls: AA_IMAGE_SOURCES,
251
- textToImage: { elo: 1198, pricePer1k: 30, rank: 6, winRate: 0.59 }
267
+ textToImage: { elo: 1226, pricePer1k: 30, rank: 16 }
252
268
  },
253
269
  speed: {
254
270
  medianSeconds: 14.3,
@@ -285,10 +301,10 @@ var MODELS = {
285
301
  seedream45: {
286
302
  benchmark: {
287
303
  artificialAnalysis: {
288
- editing: { elo: 1184, pricePer1k: 40, rank: 17, winRate: 0.58 },
304
+ editing: { elo: 1186, pricePer1k: 40, rank: 26 },
289
305
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
290
306
  sourceUrls: AA_IMAGE_SOURCES,
291
- textToImage: { elo: 1167, pricePer1k: 40, rank: 17, winRate: 0.63 }
307
+ textToImage: { elo: 1203, pricePer1k: 40, rank: 32 }
292
308
  },
293
309
  speed: {
294
310
  medianSeconds: 18.2,
@@ -323,6 +339,14 @@ var MODELS = {
323
339
  type: "generation"
324
340
  },
325
341
  seedream5: {
342
+ benchmark: {
343
+ artificialAnalysis: {
344
+ editing: { elo: 1248, pricePer1k: 90, rank: 8 },
345
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
346
+ sourceUrls: AA_IMAGE_SOURCES,
347
+ textToImage: { elo: 1279, pricePer1k: 90, rank: 10 }
348
+ }
349
+ },
326
350
  name: "Seedream 5.0 Pro",
327
351
  endpoint: "bytedance/seedream/v5/pro/text-to-image",
328
352
  editEndpoint: "bytedance/seedream/v5/pro/edit",
@@ -349,6 +373,13 @@ var MODELS = {
349
373
  maxReferenceImages: 10
350
374
  },
351
375
  "seedream5-lite": {
376
+ benchmark: {
377
+ artificialAnalysis: {
378
+ editing: { elo: 1169, pricePer1k: 35, rank: 32 },
379
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
380
+ sourceUrls: AA_IMAGE_SOURCES
381
+ }
382
+ },
352
383
  name: "Seedream 5.0 Lite",
353
384
  endpoint: "fal-ai/bytedance/seedream/v5/lite/text-to-image",
354
385
  editEndpoint: "fal-ai/bytedance/seedream/v5/lite/edit",
@@ -377,10 +408,10 @@ var MODELS = {
377
408
  "flux2-max": {
378
409
  benchmark: {
379
410
  artificialAnalysis: {
380
- editing: { elo: 1206, pricePer1k: 140, rank: 10, winRate: 0.6 },
411
+ editing: { elo: 1201, pricePer1k: 140, rank: 19 },
381
412
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
382
413
  sourceUrls: AA_IMAGE_SOURCES,
383
- textToImage: { elo: 1197, pricePer1k: 70, rank: 8, winRate: 0.67 }
414
+ textToImage: { elo: 1225, pricePer1k: 70, rank: 17 }
384
415
  },
385
416
  speed: {
386
417
  medianSeconds: 31.6,
@@ -420,10 +451,10 @@ var MODELS = {
420
451
  "flux2-pro": {
421
452
  benchmark: {
422
453
  artificialAnalysis: {
423
- editing: { elo: 1170, pricePer1k: 30, rank: 21 },
454
+ editing: { elo: 1170, pricePer1k: 45, rank: 31 },
424
455
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
425
456
  sourceUrls: AA_IMAGE_SOURCES,
426
- textToImage: { elo: 1186, pricePer1k: 30, rank: 10, winRate: 0.62 }
457
+ textToImage: { elo: 1207, pricePer1k: 30, rank: 30 }
427
458
  },
428
459
  speed: {
429
460
  medianSeconds: 16.1,
@@ -463,10 +494,9 @@ var MODELS = {
463
494
  "flux2-flex": {
464
495
  benchmark: {
465
496
  artificialAnalysis: {
466
- editing: { elo: 1161, pricePer1k: 60, rank: 22 },
467
497
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
468
498
  sourceUrls: AA_IMAGE_SOURCES,
469
- textToImage: { elo: 1182, pricePer1k: 60, rank: 13, winRate: 0.61 }
499
+ textToImage: { elo: 1223, pricePer1k: 60, rank: 18 }
470
500
  },
471
501
  speed: {
472
502
  medianSeconds: 15.3,
@@ -510,8 +540,7 @@ var MODELS = {
510
540
  benchmark: {
511
541
  artificialAnalysis: {
512
542
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
513
- sourceUrls: AA_IMAGE_SOURCES,
514
- textToImage: { elo: 1160, pricePer1k: 12, rank: 19, winRate: 0.57 }
543
+ sourceUrls: AA_IMAGE_SOURCES
515
544
  },
516
545
  speed: {
517
546
  medianSeconds: 4.6,
@@ -654,7 +683,55 @@ var MODELS = {
654
683
  supportsSyncMode: true,
655
684
  type: "generation"
656
685
  },
686
+ reve: {
687
+ benchmark: {
688
+ artificialAnalysis: {
689
+ // #2 on BOTH boards, which almost nothing else manages - the top text-to-image model
690
+ // (GPT Image 2) drops to 3rd for editing, and the top editor (MAI-Image-2.5-Pro) is not
691
+ // on fal at all. Reve is the best editor motif can actually reach.
692
+ editing: { elo: 1263, pricePer1k: 200, rank: 2 },
693
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
694
+ sourceUrls: AA_IMAGE_SOURCES,
695
+ textToImage: { elo: 1322, pricePer1k: 200, rank: 2 }
696
+ },
697
+ tiers: { price: "premium", quality: "frontier", speed: "unknown" },
698
+ useCase: "Highest-ranked editor reachable on fal; strong prompt adherence and text rendering"
699
+ },
700
+ editEndpoint: "fal-ai/reve/edit",
701
+ endpoint: "fal-ai/reve/text-to-image",
702
+ falPricing: {
703
+ checkedAt: FAL_PRICING_CHECKED_AUG_2026,
704
+ currency: "USD",
705
+ endpointId: "fal-ai/reve/text-to-image",
706
+ estimatedCostPerImageUsd: 0.04,
707
+ source: "fal-pricing-api",
708
+ unit: "images",
709
+ unitPrice: 0.04
710
+ },
711
+ maxReferenceImages: 1,
712
+ name: "Reve 2.1",
713
+ pricePerImageUsd: 0.04,
714
+ pricing: "$0.04",
715
+ // fal's schema takes aspect_ratio from a fixed set and has no width/height or resolution
716
+ // parameter, so aspect is supported and resolution is not.
717
+ sizeMode: "aspect_ratio",
718
+ supportsAspect: true,
719
+ supportsEdit: true,
720
+ supportsNumImages: true,
721
+ supportsOutputFormat: true,
722
+ supportsResolution: false,
723
+ supportsSeed: false,
724
+ supportsSyncMode: true,
725
+ type: "generation"
726
+ },
657
727
  recraft4: {
728
+ benchmark: {
729
+ artificialAnalysis: {
730
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
731
+ sourceUrls: AA_IMAGE_SOURCES,
732
+ textToImage: { elo: 1215, pricePer1k: 35, rank: 25 }
733
+ }
734
+ },
658
735
  endpoint: "fal-ai/recraft/v4/text-to-image",
659
736
  falPricing: {
660
737
  checkedAt: FAL_PRICING_CHECKED_JUL_2026,
@@ -705,6 +782,13 @@ var MODELS = {
705
782
  type: "generation"
706
783
  },
707
784
  ideogram4: {
785
+ benchmark: {
786
+ artificialAnalysis: {
787
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
788
+ sourceUrls: AA_IMAGE_SOURCES,
789
+ textToImage: { elo: 1220, pricePer1k: 60, rank: 19 }
790
+ }
791
+ },
708
792
  name: "Ideogram V4",
709
793
  endpoint: "ideogram/v4",
710
794
  type: "generation",
@@ -737,10 +821,10 @@ var MODELS = {
737
821
  "grok-image": {
738
822
  benchmark: {
739
823
  artificialAnalysis: {
740
- editing: { elo: 1213, pricePer1k: 20, rank: 7, winRate: 0.6 },
824
+ editing: { elo: 1214, pricePer1k: 20, rank: 16 },
741
825
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
742
826
  sourceUrls: AA_IMAGE_SOURCES,
743
- textToImage: { elo: 1182, pricePer1k: 20, rank: 12, winRate: 0.6 }
827
+ textToImage: { elo: 1216, pricePer1k: 20, rank: 23 }
744
828
  },
745
829
  speed: {
746
830
  medianSeconds: 5.1,
@@ -778,8 +862,7 @@ var MODELS = {
778
862
  benchmark: {
779
863
  artificialAnalysis: {
780
864
  snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
781
- sourceUrls: AA_IMAGE_SOURCES,
782
- textToImage: { elo: 1158, pricePer1k: 20, rank: 20, winRate: 0.6 }
865
+ sourceUrls: AA_IMAGE_SOURCES
783
866
  },
784
867
  speed: {
785
868
  medianSeconds: 23.3,
@@ -814,8 +897,14 @@ var MODELS = {
814
897
  },
815
898
  qwen3: {
816
899
  benchmark: {
900
+ artificialAnalysis: {
901
+ editing: { elo: 1218, pricePer1k: 33, rank: 15 },
902
+ snapshotDate: AA_IMAGE_LEADERBOARD_SNAPSHOT,
903
+ sourceUrls: AA_IMAGE_SOURCES,
904
+ textToImage: { elo: 1272, pricePer1k: 30, rank: 11 }
905
+ },
817
906
  tiers: { price: "budget", quality: "better", speed: "unknown" },
818
- useCase: "Qwen Image 3 - successor to qwen; no leaderboard data yet"
907
+ useCase: "Strong quality per dollar - 11th on text-to-image at $30/1k"
819
908
  },
820
909
  endpoint: "fal-ai/qwen-image-3/text-to-image",
821
910
  falPricing: {
@@ -925,8 +1014,378 @@ var EDIT_CAPABLE_MODELS = GENERATION_MODELS.filter(
925
1014
  (id) => MODELS[id]?.supportsEdit === true
926
1015
  );
927
1016
 
928
- // src/tools.ts
929
- var FAL_TOOLS = {
1017
+ // src/errors.ts
1018
+ var MotifError = class extends Error {
1019
+ status;
1020
+ code;
1021
+ /** fal's request-correlation id (from the `x-fal-request-id` header or the
1022
+ * error body). Ties a failure back to fal's dashboard/support. */
1023
+ requestId;
1024
+ constructor(message, status, code, requestId) {
1025
+ super(message);
1026
+ this.name = "MotifError";
1027
+ this.status = status;
1028
+ this.code = code;
1029
+ this.requestId = requestId;
1030
+ }
1031
+ };
1032
+
1033
+ // src/server-tools.ts
1034
+ import { err, ok } from "neverthrow";
1035
+
1036
+ // src/tool-registry/analysis.ts
1037
+ var ANALYSIS_TOOLS = {
1038
+ "got-ocr": {
1039
+ category: "analysis",
1040
+ description: "Transcribe text from one or more images, optionally as formatted multi-page output.",
1041
+ endpoint: "fal-ai/got-ocr/v2",
1042
+ inputField: "input_image_urls",
1043
+ inputKind: "images",
1044
+ name: "GOT-OCR 2.0",
1045
+ outputKeys: ["outputs"],
1046
+ price: { kind: "metered" },
1047
+ pricing: "$0.05/image",
1048
+ queued: true,
1049
+ sourceUrl: "https://fal.ai/models/fal-ai/got-ocr/v2",
1050
+ task: "optical character recognition"
1051
+ },
1052
+ "moondream-caption": {
1053
+ category: "analysis",
1054
+ description: "Write a short, normal, or long caption for an image.",
1055
+ endpoint: "fal-ai/moondream3-preview/caption",
1056
+ inputField: "image_url",
1057
+ inputKind: "image",
1058
+ name: "Moondream 3 Caption",
1059
+ outputKeys: ["output"],
1060
+ price: { kind: "metered" },
1061
+ pricing: "$0.40/M input tokens, $3.50/M output tokens",
1062
+ sourceUrl: "https://fal.ai/models/fal-ai/moondream3-preview/caption",
1063
+ task: "image captioning"
1064
+ },
1065
+ "moondream-detect": {
1066
+ category: "analysis",
1067
+ description: "Detect prompted objects and return their bounding boxes, with an optional preview.",
1068
+ endpoint: "fal-ai/moondream3-preview/detect",
1069
+ inputField: "image_url",
1070
+ inputKind: "image",
1071
+ name: "Moondream 3 Detect",
1072
+ outputKeys: ["objects", "image"],
1073
+ price: { kind: "metered" },
1074
+ pricing: "$0.40/M input tokens, $3.50/M output tokens",
1075
+ sourceUrl: "https://fal.ai/models/fal-ai/moondream3-preview/detect",
1076
+ task: "object detection"
1077
+ },
1078
+ "moondream-point": {
1079
+ category: "analysis",
1080
+ description: "Return point coordinates for every instance of a prompted subject.",
1081
+ endpoint: "fal-ai/moondream3-preview/point",
1082
+ inputField: "image_url",
1083
+ inputKind: "image",
1084
+ name: "Moondream 3 Point",
1085
+ outputKeys: ["points", "image"],
1086
+ price: { kind: "metered" },
1087
+ pricing: "$0.40/M input tokens, $3.50/M output tokens",
1088
+ sourceUrl: "https://fal.ai/models/fal-ai/moondream3-preview/point",
1089
+ task: "object pointing"
1090
+ },
1091
+ "moondream-query": {
1092
+ category: "analysis",
1093
+ description: "Answer a free-form question about an image, with reasoning.",
1094
+ endpoint: "fal-ai/moondream3-preview/query",
1095
+ inputField: "image_url",
1096
+ inputKind: "image",
1097
+ name: "Moondream 3 Query",
1098
+ outputKeys: ["output", "reasoning"],
1099
+ price: { kind: "metered" },
1100
+ pricing: "$0.40/M input tokens, $3.50/M output tokens",
1101
+ sourceUrl: "https://fal.ai/models/fal-ai/moondream3-preview/query",
1102
+ task: "visual question answering"
1103
+ },
1104
+ nsfw: {
1105
+ category: "moderation",
1106
+ description: "Predict whether one or more images contain NSFW concepts.",
1107
+ endpoint: "fal-ai/x-ailab/nsfw",
1108
+ inputField: "image_urls",
1109
+ inputKind: "images",
1110
+ name: "NSFW Checker",
1111
+ outputKeys: ["has_nsfw_concepts"],
1112
+ price: { kind: "metered" },
1113
+ pricing: "$0.001/image",
1114
+ sourceUrl: "https://fal.ai/models/fal-ai/x-ailab/nsfw",
1115
+ task: "vision moderation"
1116
+ }
1117
+ };
1118
+
1119
+ // src/tool-registry/assets.ts
1120
+ var ASSET_TOOLS = {
1121
+ "hunyuan3d-v3": {
1122
+ category: "3d",
1123
+ description: "Reconstruct a textured 3D mesh from a single image, with optional multi-view inputs.",
1124
+ endpoint: "fal-ai/hunyuan3d-v3/image-to-3d",
1125
+ inputField: "input_image_url",
1126
+ inputKind: "image",
1127
+ name: "Hunyuan3D v3 Image to 3D",
1128
+ outputKeys: ["model_glb", "model_urls", "thumbnail"],
1129
+ price: { kind: "call", usd: 0.375 },
1130
+ pricing: "$0.375/generation at the default Normal type; $0.45 low poly, $0.225 geometry only, and $0.15 each for PBR, multi-view, or a custom face count",
1131
+ queued: true,
1132
+ sourceUrl: "https://fal.ai/models/fal-ai/hunyuan3d-v3/image-to-3d",
1133
+ task: "single-image 3D reconstruction"
1134
+ },
1135
+ "ideogram-layerize-text": {
1136
+ category: "layers",
1137
+ description: "Split an image into background image layers plus editable HTML text containers.",
1138
+ endpoint: "fal-ai/ideogram/v3/layerize-text",
1139
+ inputField: "image_url",
1140
+ inputKind: "image",
1141
+ name: "Ideogram v3 Text Layerize",
1142
+ outputKeys: ["image_layers", "image", "text_html", "text_containers"],
1143
+ price: { kind: "call", usd: 0.09 },
1144
+ pricing: "$0.09/image",
1145
+ queued: true,
1146
+ sourceUrl: "https://fal.ai/models/fal-ai/ideogram/v3/layerize-text",
1147
+ task: "text and image layer extraction"
1148
+ },
1149
+ "ideogram-tiling": {
1150
+ category: "material",
1151
+ description: "Generate a seamlessly tiling texture, optionally conditioned on a source image.",
1152
+ endpoint: "ideogram/v4/tiling",
1153
+ inputField: "image_url",
1154
+ inputKind: "image",
1155
+ name: "Ideogram v4 Tiling",
1156
+ outputKeys: ["images"],
1157
+ price: { kind: "megapixel", usd: 0.06 },
1158
+ pricing: "$0.06/megapixel at the default BALANCED speed; $0.03 turbo, $0.10 quality",
1159
+ sourceUrl: "https://fal.ai/models/ideogram/v4/tiling",
1160
+ task: "seamless texture generation"
1161
+ },
1162
+ image2svg: {
1163
+ category: "vector",
1164
+ description: "Trace a raster image into layered SVG paths.",
1165
+ endpoint: "fal-ai/image2svg",
1166
+ inputField: "image_url",
1167
+ inputKind: "image",
1168
+ name: "Image to SVG",
1169
+ outputKeys: ["images"],
1170
+ price: { kind: "call", usd: 5e-3 },
1171
+ pricing: "$0.005/image",
1172
+ sourceUrl: "https://fal.ai/models/fal-ai/image2svg",
1173
+ task: "raster to vector tracing"
1174
+ },
1175
+ patina: {
1176
+ category: "material",
1177
+ description: "Decompose a surface photograph into PBR maps: basecolor, normal, roughness, metalness, height.",
1178
+ endpoint: "fal-ai/patina",
1179
+ inputField: "image_url",
1180
+ inputKind: "image",
1181
+ name: "Patina PBR Maps",
1182
+ outputKeys: ["images"],
1183
+ // Output order follows the request's `maps` array, so read that first;
1184
+ // the fallback is the endpoint's own schema default.
1185
+ outputLabels: {
1186
+ images: {
1187
+ fallback: ["basecolor", "normal", "roughness", "metalness", "height"],
1188
+ fromOption: "maps"
1189
+ }
1190
+ },
1191
+ price: { kind: "metered" },
1192
+ pricing: "$0.01 base plus $0.01/megapixel per output map, so all 5 maps on a 1MP image cost $0.06; the listed rate is per map",
1193
+ queued: true,
1194
+ sourceUrl: "https://fal.ai/models/fal-ai/patina",
1195
+ task: "PBR material decomposition"
1196
+ },
1197
+ "patina-extract": {
1198
+ category: "material",
1199
+ description: "Extract a tiling PBR material from a prompted region of a photograph.",
1200
+ endpoint: "fal-ai/patina/material/extract",
1201
+ inputField: "image_url",
1202
+ inputKind: "image",
1203
+ name: "Patina Material Extract",
1204
+ outputKeys: ["images"],
1205
+ // Same `maps` option and schema default as fal-ai/patina, both verified.
1206
+ // `num_images` above 1 multiplies the array and breaks the mapping, which
1207
+ // the count check catches.
1208
+ outputLabels: {
1209
+ images: {
1210
+ fallback: ["basecolor", "normal", "roughness", "metalness", "height"],
1211
+ fromOption: "maps"
1212
+ }
1213
+ },
1214
+ price: { kind: "metered" },
1215
+ pricing: "$0.10 base only; add $0.02/megapixel plus $0.01/megapixel per map, so 1MP with all 5 maps is $0.17",
1216
+ queued: true,
1217
+ sourceUrl: "https://fal.ai/models/fal-ai/patina/material/extract",
1218
+ task: "tiling material extraction"
1219
+ },
1220
+ "qwen-layered": {
1221
+ category: "layers",
1222
+ description: "Split an image into a configurable number of stacked RGBA layers.",
1223
+ endpoint: "fal-ai/qwen-image-layered",
1224
+ inputField: "image_url",
1225
+ inputKind: "image",
1226
+ name: "Qwen Image Layered",
1227
+ outputKeys: ["images"],
1228
+ // fal publishes a figure but not what it counts. Encoding it as `call`
1229
+ // would assert a per-request price we cannot support, and this endpoint
1230
+ // returns several outputs — the same shape that made `seedream-layerize`
1231
+ // under-report by a factor of its layer count. `metered` reports null
1232
+ // rather than a number that is right only on one reading.
1233
+ price: { kind: "metered" },
1234
+ pricing: "$0.05 per image; fal does not say whether that counts the input image or each of the generated layers, so no estimate is reported",
1235
+ queued: true,
1236
+ sourceUrl: "https://fal.ai/models/fal-ai/qwen-image-layered",
1237
+ task: "image layer decomposition"
1238
+ },
1239
+ "recraft-vectorize": {
1240
+ category: "vector",
1241
+ description: "Convert a raster image into a clean SVG.",
1242
+ endpoint: "fal-ai/recraft/vectorize",
1243
+ inputField: "image_url",
1244
+ inputKind: "image",
1245
+ name: "Recraft Vectorize",
1246
+ outputKeys: ["image"],
1247
+ price: { kind: "call", usd: 0.04 },
1248
+ pricing: "$0.04/image, $0.08 with a vector style",
1249
+ sourceUrl: "https://fal.ai/models/fal-ai/recraft/vectorize",
1250
+ task: "raster to vector conversion"
1251
+ },
1252
+ "sam3-3d-align": {
1253
+ category: "3d",
1254
+ description: "Align SAM 3D objects and bodies into a shared scene.",
1255
+ endpoint: "fal-ai/sam-3/3d-align",
1256
+ inputField: "image_url",
1257
+ inputKind: "image",
1258
+ name: "SAM 3D Align",
1259
+ outputKeys: [
1260
+ "scene_glb",
1261
+ "model_glb",
1262
+ "visualization",
1263
+ "body_mesh_ply",
1264
+ "metadata"
1265
+ ],
1266
+ // fal publishes a figure but not what it counts. Encoding it as `call`
1267
+ // would assert a per-request price we cannot support, and this endpoint
1268
+ // returns several outputs — the same shape that made `seedream-layerize`
1269
+ // under-report by a factor of its layer count. `metered` reports null
1270
+ // rather than a number that is right only on one reading.
1271
+ price: { kind: "metered" },
1272
+ pricing: "$0.02 per unit; fal does not define a unit, so no estimate is reported",
1273
+ queued: true,
1274
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-align",
1275
+ task: "3D scene alignment"
1276
+ },
1277
+ "sam3-3d-body": {
1278
+ category: "3d",
1279
+ defaultOptions: {
1280
+ export_meshes: true,
1281
+ include_3d_keypoints: true,
1282
+ include_mhr_params: true
1283
+ },
1284
+ description: "Reconstruct human body meshes and keypoints from a single image.",
1285
+ endpoint: "fal-ai/sam-3/3d-body",
1286
+ inputField: "image_url",
1287
+ inputKind: "image",
1288
+ name: "SAM 3D Body",
1289
+ outputKeys: ["model_glb", "visualization", "meshes", "metadata"],
1290
+ // fal publishes a figure but not what it counts. Encoding it as `call`
1291
+ // would assert a per-request price we cannot support, and this endpoint
1292
+ // returns several outputs — the same shape that made `seedream-layerize`
1293
+ // under-report by a factor of its layer count. `metered` reports null
1294
+ // rather than a number that is right only on one reading.
1295
+ price: { kind: "metered" },
1296
+ pricing: "$0.02 per unit; fal does not define a unit, so no estimate is reported",
1297
+ queued: true,
1298
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-body",
1299
+ task: "single-image 3D body reconstruction"
1300
+ },
1301
+ "sam3-3d-objects": {
1302
+ category: "3d",
1303
+ defaultOptions: {
1304
+ prompt: "car"
1305
+ },
1306
+ description: "Reconstruct one or more 3D objects from an image and prompts.",
1307
+ endpoint: "fal-ai/sam-3/3d-objects",
1308
+ inputField: "image_url",
1309
+ inputKind: "image",
1310
+ name: "SAM 3D Objects",
1311
+ outputKeys: [
1312
+ "gaussian_splat",
1313
+ "model_glb",
1314
+ "metadata",
1315
+ "individual_splats",
1316
+ "individual_glbs",
1317
+ "artifacts_zip"
1318
+ ],
1319
+ // fal publishes a figure but not what it counts. Encoding it as `call`
1320
+ // would assert a per-request price we cannot support, and this endpoint
1321
+ // returns several outputs — the same shape that made `seedream-layerize`
1322
+ // under-report by a factor of its layer count. `metered` reports null
1323
+ // rather than a number that is right only on one reading.
1324
+ price: { kind: "metered" },
1325
+ pricing: "$0.02 per unit; fal does not define a unit, and this endpoint returns one mesh per detected object, so no estimate is reported",
1326
+ queued: true,
1327
+ sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-objects",
1328
+ task: "single-image 3D object reconstruction"
1329
+ },
1330
+ "seedream-layerize": {
1331
+ category: "layers",
1332
+ description: "Split an image into editable Seedream layers plus flattened previews.",
1333
+ endpoint: "bytedance/seedream/v5/pro/layerize",
1334
+ inputField: "image_url",
1335
+ inputKind: "image",
1336
+ name: "Seedream v5 Pro Layerize",
1337
+ outputKeys: ["layers", "images"],
1338
+ price: { kind: "metered" },
1339
+ pricing: "$0.03375 per generated layer below 1536x1536 total area, $0.0675 per layer above; the listed rate is per layer, not per call",
1340
+ queued: true,
1341
+ sourceUrl: "https://fal.ai/models/bytedance/seedream/v5/pro/layerize",
1342
+ task: "image layer decomposition"
1343
+ },
1344
+ "seedvr-seamless": {
1345
+ category: "material",
1346
+ description: "Upscale a tiling texture while keeping its edges seamless. ",
1347
+ endpoint: "fal-ai/seedvr/upscale/image/seamless",
1348
+ inputField: "image_url",
1349
+ inputKind: "image",
1350
+ name: "SeedVR Seamless Upscale",
1351
+ outputKeys: ["image"],
1352
+ price: { kind: "megapixel", usd: 25e-4 },
1353
+ pricing: "$0.0025/megapixel",
1354
+ queued: true,
1355
+ sourceUrl: "https://fal.ai/models/fal-ai/seedvr/upscale/image/seamless",
1356
+ task: "seamless texture upscaling"
1357
+ },
1358
+ "trellis-2": {
1359
+ category: "3d",
1360
+ description: "Reconstruct a textured 3D mesh from a single image, with UV unwrapping.",
1361
+ endpoint: "fal-ai/trellis-2",
1362
+ inputField: "image_url",
1363
+ inputKind: "image",
1364
+ name: "TRELLIS 2",
1365
+ outputKeys: ["model_glb"],
1366
+ price: { kind: "call", usd: 0.3 },
1367
+ pricing: "$0.30 at the default 1024p; $0.25 at 512p, $0.35 at 1536p",
1368
+ queued: true,
1369
+ sourceUrl: "https://fal.ai/models/fal-ai/trellis-2",
1370
+ task: "single-image 3D reconstruction"
1371
+ }
1372
+ };
1373
+
1374
+ // src/tool-registry/background.ts
1375
+ var BACKGROUND_TOOLS = {
1376
+ "ben-v2": {
1377
+ category: "background",
1378
+ description: "Fast, high-quality background removal for images.",
1379
+ endpoint: "fal-ai/ben/v2/image",
1380
+ inputField: "image_url",
1381
+ inputKind: "image",
1382
+ name: "BEN v2 Background Removal",
1383
+ outputKeys: ["image"],
1384
+ price: { kind: "megapixel", usd: 0.025 },
1385
+ pricing: "$0.025/megapixel",
1386
+ sourceUrl: "https://fal.ai/models/fal-ai/ben/v2/image",
1387
+ task: "image background removal"
1388
+ },
930
1389
  birefnet: {
931
1390
  category: "background",
932
1391
  defaultOptions: {
@@ -941,6 +1400,7 @@ var FAL_TOOLS = {
941
1400
  inputKind: "image",
942
1401
  name: "BirefNet Background Removal",
943
1402
  outputKeys: ["image", "mask_image"],
1403
+ price: { kind: "metered" },
944
1404
  pricing: "$0/compute-second listed by fal",
945
1405
  sourceUrl: "https://fal.ai/models/fal-ai/birefnet/v2",
946
1406
  task: "image background removal"
@@ -953,7 +1413,8 @@ var FAL_TOOLS = {
953
1413
  inputKind: "image",
954
1414
  name: "Bria RMBG 2.0",
955
1415
  outputKeys: ["image"],
956
- pricing: "$0.02/image",
1416
+ price: { kind: "call", usd: 0.018 },
1417
+ pricing: "$0.018/generation",
957
1418
  sourceUrl: "https://fal.ai/models/fal-ai/bria/background/remove",
958
1419
  task: "image background removal"
959
1420
  },
@@ -970,65 +1431,507 @@ var FAL_TOOLS = {
970
1431
  inputKind: "video",
971
1432
  name: "Bria Video Background Removal",
972
1433
  outputKeys: ["video"],
973
- pricing: "$0.14/sec",
1434
+ price: { kind: "second", usd: 425e-5 },
1435
+ pricing: "$0.00425/sec",
1436
+ queued: true,
974
1437
  sourceUrl: "https://fal.ai/models/bria/video/background-removal",
975
1438
  task: "video background removal"
976
1439
  },
977
- "depth-anything": {
978
- category: "preprocess",
979
- description: "Generate Depth Anything v2 depth maps from input images.",
980
- endpoint: "fal-ai/image-preprocessors/depth-anything/v2",
1440
+ ddcolor: {
1441
+ category: "restoration",
1442
+ description: "Colourise black-and-white photographs.",
1443
+ endpoint: "fal-ai/ddcolor",
981
1444
  inputField: "image_url",
982
1445
  inputKind: "image",
983
- name: "Depth Anything v2 Preprocessor",
1446
+ name: "DDColor Colourisation",
984
1447
  outputKeys: ["image"],
985
- pricing: "$0/compute-second listed by fal",
986
- sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/depth-anything/v2",
987
- task: "depth preprocessing"
1448
+ price: { kind: "megapixel", usd: 1e-3 },
1449
+ pricing: "$0.001/megapixel",
1450
+ sourceUrl: "https://fal.ai/models/fal-ai/ddcolor",
1451
+ task: "photo colourisation"
988
1452
  },
989
- lineart: {
990
- category: "preprocess",
1453
+ rembg: {
1454
+ category: "background",
991
1455
  defaultOptions: {
992
- coarse: false
1456
+ crop_to_bbox: false
993
1457
  },
994
- description: "Generate line art/control-style edges from an input image.",
995
- endpoint: "fal-ai/image-preprocessors/lineart",
1458
+ description: "Generic image background removal utility.",
1459
+ endpoint: "fal-ai/imageutils/rembg",
996
1460
  inputField: "image_url",
997
1461
  inputKind: "image",
998
- name: "Line Art Preprocessor",
1462
+ name: "Remove Background",
999
1463
  outputKeys: ["image"],
1464
+ price: { kind: "metered" },
1000
1465
  pricing: "$0/compute-second listed by fal",
1001
- sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/lineart",
1002
- task: "image preprocessing"
1466
+ sourceUrl: "https://fal.ai/models/fal-ai/imageutils/rembg",
1467
+ task: "image background removal"
1003
1468
  },
1004
- "marigold-depth": {
1005
- category: "depth",
1469
+ "seedvr-upscale": {
1470
+ category: "restoration",
1006
1471
  defaultOptions: {
1007
- ensemble_size: 10,
1008
- num_inference_steps: 10
1472
+ output_format: "png"
1009
1473
  },
1010
- description: "Create depth maps using Marigold depth estimation.",
1011
- endpoint: "fal-ai/imageutils/marigold-depth",
1474
+ description: "Diffusion image restoration and upscaling with SeedVR.",
1475
+ endpoint: "fal-ai/seedvr/upscale/image",
1012
1476
  inputField: "image_url",
1013
1477
  inputKind: "image",
1014
- name: "Marigold Depth Estimation",
1478
+ name: "SeedVR Image Upscale",
1015
1479
  outputKeys: ["image"],
1016
- pricing: "$0/compute-second listed by fal",
1017
- sourceUrl: "https://fal.ai/models/fal-ai/imageutils/marigold-depth",
1018
- task: "depth map"
1480
+ price: { kind: "megapixel", usd: 1e-3 },
1481
+ pricing: "$0.001/megapixel",
1482
+ queued: true,
1483
+ sourceUrl: "https://fal.ai/models/fal-ai/seedvr/upscale/image",
1484
+ task: "image restoration and upscaling"
1019
1485
  },
1020
- "midas-depth": {
1021
- category: "depth",
1486
+ "topaz-adjust": {
1487
+ category: "restoration",
1022
1488
  defaultOptions: {
1023
- a: Math.PI * 2,
1024
- bg_th: 0.1
1489
+ output_format: "png"
1025
1490
  },
1026
- description: "Create MiDaS depth maps from input images.",
1027
- endpoint: "fal-ai/imageutils/depth",
1491
+ description: "Topaz exposure, white balance, and colour adjustment.",
1492
+ endpoint: "topaz/adjust/image",
1493
+ inputField: "image_url",
1494
+ inputKind: "image",
1495
+ name: "Topaz Adjust",
1496
+ outputKeys: ["image"],
1497
+ price: { kind: "megapixel", usd: 0.08 / 24 },
1498
+ pricing: "$0.08 per 24 output megapixels",
1499
+ queued: true,
1500
+ sourceUrl: "https://fal.ai/models/topaz/adjust/image",
1501
+ task: "tonal adjustment"
1502
+ },
1503
+ "topaz-creative": {
1504
+ category: "restoration",
1505
+ defaultOptions: {
1506
+ output_format: "png"
1507
+ },
1508
+ description: "Topaz Bloom upscaling, which reimagines detail rather than preserving it.",
1509
+ endpoint: "topaz/upscale/image/creative",
1510
+ inputField: "image_url",
1511
+ inputKind: "image",
1512
+ name: "Topaz Creative Upscale",
1513
+ outputKeys: ["image"],
1514
+ price: { kind: "megapixel", usd: 0.96 / 24 },
1515
+ pricing: "$0.96 per 24 output megapixels",
1516
+ queued: true,
1517
+ sourceUrl: "https://fal.ai/models/topaz/upscale/image/creative",
1518
+ task: "creative image upscaling"
1519
+ },
1520
+ "topaz-denoise": {
1521
+ category: "restoration",
1522
+ defaultOptions: {
1523
+ output_format: "png"
1524
+ },
1525
+ description: "Topaz noise reduction at source resolution.",
1526
+ endpoint: "topaz/denoise/image",
1527
+ inputField: "image_url",
1528
+ inputKind: "image",
1529
+ name: "Topaz Denoise",
1530
+ outputKeys: ["image"],
1531
+ price: { kind: "megapixel", usd: 0.08 / 24 },
1532
+ pricing: "$0.08 per 24 output megapixels at the default Normal model; $0.16 with Denoise Max",
1533
+ queued: true,
1534
+ sourceUrl: "https://fal.ai/models/topaz/denoise/image",
1535
+ task: "image denoising"
1536
+ },
1537
+ "topaz-generative": {
1538
+ category: "restoration",
1539
+ defaultOptions: {
1540
+ output_format: "png"
1541
+ },
1542
+ description: "Topaz Wonder upscaling, which synthesises plausible detail as it enlarges.",
1543
+ endpoint: "topaz/upscale/image/generative",
1544
+ inputField: "image_url",
1545
+ inputKind: "image",
1546
+ name: "Topaz Generative Upscale",
1547
+ outputKeys: ["image"],
1548
+ price: { kind: "megapixel", usd: 0.24 / 24 },
1549
+ pricing: "$0.24 per 24 output megapixels at the default Wonder 3; $0.48 with Wonder, Wonder 2, Standard MAX, Redefine or Recover 3",
1550
+ queued: true,
1551
+ sourceUrl: "https://fal.ai/models/topaz/upscale/image/generative",
1552
+ task: "generative image upscaling"
1553
+ },
1554
+ "topaz-image": {
1555
+ category: "upscale",
1556
+ defaultOptions: {
1557
+ model: "Standard V2",
1558
+ output_format: "jpeg",
1559
+ upscale_factor: 2
1560
+ },
1561
+ description: "Professional Topaz image enhancement and upscaling.",
1562
+ endpoint: "fal-ai/topaz/upscale/image",
1563
+ inputField: "image_url",
1564
+ inputKind: "image",
1565
+ name: "Topaz Image Upscale",
1566
+ outputKeys: ["image"],
1567
+ price: { kind: "megapixel", usd: 0.08 / 24 },
1568
+ pricing: "$0.08 for output up to 24MP; $0.16 to 48MP, $0.32 to 96MP, up to $1.36 at 512MP",
1569
+ queued: true,
1570
+ sourceUrl: "https://fal.ai/models/fal-ai/topaz/upscale/image",
1571
+ task: "image enhancement"
1572
+ },
1573
+ "topaz-precision": {
1574
+ category: "restoration",
1575
+ defaultOptions: {
1576
+ output_format: "png"
1577
+ },
1578
+ description: "Topaz upscaling that preserves the original detail rather than inventing it.",
1579
+ endpoint: "topaz/upscale/image/precision",
1580
+ inputField: "image_url",
1581
+ inputKind: "image",
1582
+ name: "Topaz Precision Upscale",
1583
+ outputKeys: ["image"],
1584
+ price: { kind: "megapixel", usd: 0.08 / 24 },
1585
+ pricing: "$0.08 per 24 output megapixels, any precision model",
1586
+ queued: true,
1587
+ sourceUrl: "https://fal.ai/models/topaz/upscale/image/precision",
1588
+ task: "faithful image upscaling"
1589
+ },
1590
+ "topaz-restore": {
1591
+ category: "restoration",
1592
+ defaultOptions: {
1593
+ output_format: "png"
1594
+ },
1595
+ description: "Topaz restoration of damaged or degraded photographs at source resolution.",
1596
+ endpoint: "topaz/restore/image",
1597
+ inputField: "image_url",
1598
+ inputKind: "image",
1599
+ name: "Topaz Restore",
1600
+ outputKeys: ["image"],
1601
+ price: { kind: "megapixel", usd: 0.48 / 24 },
1602
+ pricing: "$0.48 per 24 output megapixels at the default Recover 3; $0.08 with Dust-Scratch V2",
1603
+ queued: true,
1604
+ sourceUrl: "https://fal.ai/models/topaz/restore/image",
1605
+ task: "photo restoration"
1606
+ },
1607
+ "topaz-sharpen": {
1608
+ category: "restoration",
1609
+ defaultOptions: {
1610
+ output_format: "png"
1611
+ },
1612
+ description: "Topaz deblurring and sharpening at source resolution.",
1613
+ endpoint: "topaz/sharpen/image",
1614
+ inputField: "image_url",
1615
+ inputKind: "image",
1616
+ name: "Topaz Sharpen",
1617
+ outputKeys: ["image"],
1618
+ price: { kind: "megapixel", usd: 0.08 / 24 },
1619
+ pricing: "$0.08 per 24 output megapixels at the default Standard model; $0.16 with Super Focus",
1620
+ queued: true,
1621
+ sourceUrl: "https://fal.ai/models/topaz/sharpen/image",
1622
+ task: "image sharpening"
1623
+ },
1624
+ "topaz-transparent": {
1625
+ category: "restoration",
1626
+ description: "Topaz upscaling that preserves the alpha channel end to end.",
1627
+ endpoint: "topaz/upscale/image/transparent",
1628
+ inputField: "image_url",
1629
+ inputKind: "image",
1630
+ name: "Topaz Transparent Upscale",
1631
+ outputKeys: ["image"],
1632
+ price: { kind: "megapixel", usd: 0.08 / 24 },
1633
+ pricing: "$0.08 per 24 output megapixels",
1634
+ queued: true,
1635
+ sourceUrl: "https://fal.ai/models/topaz/upscale/image/transparent",
1636
+ task: "transparent image upscaling"
1637
+ },
1638
+ "topaz-video": {
1639
+ category: "upscale",
1640
+ defaultOptions: {
1641
+ model: "Proteus",
1642
+ upscale_factor: 2
1643
+ },
1644
+ description: "Professional Topaz video enhancement and upscaling.",
1645
+ endpoint: "fal-ai/topaz/upscale/video",
1646
+ inputField: "video_url",
1647
+ inputKind: "video",
1648
+ name: "Topaz Video Upscale",
1649
+ outputKeys: ["video"],
1650
+ price: { kind: "second", usd: 0.01 },
1651
+ pricing: "$0.01/sec up to 720p; $0.02 to 1080p, $0.08 above, doubled at 60fps, halved with Gaia 2",
1652
+ queued: true,
1653
+ sourceUrl: "https://fal.ai/models/fal-ai/topaz/upscale/video",
1654
+ task: "video enhancement"
1655
+ }
1656
+ };
1657
+
1658
+ // src/tool-registry/editing.ts
1659
+ var EDITING_TOOLS = {
1660
+ "bria-eraser": {
1661
+ category: "erase",
1662
+ description: "Commercial-safe removal of a masked region, inpainted from surrounding context.",
1663
+ endpoint: "fal-ai/bria/eraser",
1664
+ inputField: "image_url",
1665
+ inputKind: "image",
1666
+ name: "Bria Eraser",
1667
+ outputKeys: ["image"],
1668
+ price: { kind: "call", usd: 0.04 },
1669
+ pricing: "$0.04/generation",
1670
+ sourceUrl: "https://fal.ai/models/fal-ai/bria/eraser",
1671
+ task: "masked object erasure"
1672
+ },
1673
+ "bria-expand": {
1674
+ category: "reframe",
1675
+ description: "Expand an image onto a larger canvas, generating the new border area.",
1676
+ endpoint: "fal-ai/bria/expand",
1677
+ inputField: "image_url",
1678
+ inputKind: "image",
1679
+ name: "Bria Image Expansion",
1680
+ outputKeys: ["image"],
1681
+ price: { kind: "call", usd: 0.04 },
1682
+ pricing: "$0.04/generation",
1683
+ sourceUrl: "https://fal.ai/models/fal-ai/bria/expand",
1684
+ task: "image outpainting"
1685
+ },
1686
+ "bria-genfill": {
1687
+ category: "erase",
1688
+ description: "Generative fill of a masked region from a prompt, commercial-safe.",
1689
+ endpoint: "fal-ai/bria/genfill",
1690
+ inputField: "image_url",
1691
+ inputKind: "image",
1692
+ name: "Bria GenFill",
1693
+ outputKeys: ["images"],
1694
+ price: { kind: "call", usd: 0.04 },
1695
+ pricing: "$0.04/generation",
1696
+ sourceUrl: "https://fal.ai/models/fal-ai/bria/genfill",
1697
+ task: "masked generative fill"
1698
+ },
1699
+ "finegrain-eraser": {
1700
+ category: "erase",
1701
+ description: "Remove a prompted object and its shadows and reflections, filling the gap.",
1702
+ endpoint: "fal-ai/finegrain-eraser",
1703
+ inputField: "image_url",
1704
+ inputKind: "image",
1705
+ name: "Finegrain Object Eraser",
1706
+ outputKeys: ["image"],
1707
+ price: { kind: "call", usd: 0.27 },
1708
+ pricing: "$0.27/image at the default standard mode; $0.18 express, $0.36 premium",
1709
+ sourceUrl: "https://fal.ai/models/fal-ai/finegrain-eraser",
1710
+ task: "prompted object erasure"
1711
+ },
1712
+ "flux-outpaint": {
1713
+ category: "reframe",
1714
+ description: "Outpaint an image by a per-edge pixel expansion with FLUX.2 pro.",
1715
+ endpoint: "fal-ai/flux-2-pro/outpaint",
1716
+ inputField: "image_url",
1717
+ inputKind: "image",
1718
+ name: "FLUX.2 Pro Outpaint",
1719
+ outputKeys: ["images"],
1720
+ price: { kind: "metered" },
1721
+ pricing: "$0.03 for the first output megapixel, then $0.015 per extra megapixel of input and output, rounded up",
1722
+ sourceUrl: "https://fal.ai/models/fal-ai/flux-2-pro/outpaint",
1723
+ task: "image outpainting"
1724
+ },
1725
+ "iclight-v2": {
1726
+ category: "relight",
1727
+ description: "Relight a subject from a prompt, harmonising it with a new light direction.",
1728
+ endpoint: "fal-ai/iclight-v2",
1729
+ inputField: "image_url",
1730
+ inputKind: "image",
1731
+ name: "IC-Light v2",
1732
+ outputKeys: ["images"],
1733
+ price: { kind: "megapixel", usd: 0.1 },
1734
+ pricing: "$0.10/megapixel",
1735
+ sourceUrl: "https://fal.ai/models/fal-ai/iclight-v2",
1736
+ task: "prompted relighting"
1737
+ },
1738
+ "ideogram-reframe": {
1739
+ category: "reframe",
1740
+ description: "Reframe an image to a new aspect ratio, generating the fill.",
1741
+ endpoint: "fal-ai/ideogram/v3/reframe",
1742
+ inputField: "image_url",
1743
+ inputKind: "image",
1744
+ name: "Ideogram v3 Reframe",
1745
+ outputKeys: ["images"],
1746
+ price: { kind: "call", usd: 0.06 },
1747
+ pricing: "$0.06/image at the default BALANCED speed; $0.03 turbo, $0.09 quality",
1748
+ sourceUrl: "https://fal.ai/models/fal-ai/ideogram/v3/reframe",
1749
+ task: "aspect ratio reframing"
1750
+ },
1751
+ "lighting-restoration": {
1752
+ category: "relight",
1753
+ description: "Restore natural, even lighting across one or more input images.",
1754
+ endpoint: "fal-ai/qwen-image-edit-plus-lora-gallery/lighting-restoration",
1755
+ inputField: "image_urls",
1756
+ inputKind: "images",
1757
+ name: "Qwen Lighting Restoration",
1758
+ outputKeys: ["images"],
1759
+ price: { kind: "megapixel", usd: 0.035 },
1760
+ pricing: "$0.035/megapixel",
1761
+ sourceUrl: "https://fal.ai/models/fal-ai/qwen-image-edit-plus-lora-gallery/lighting-restoration",
1762
+ task: "lighting restoration"
1763
+ },
1764
+ "object-removal": {
1765
+ category: "erase",
1766
+ description: "Remove a prompted object from an image and fill the gap.",
1767
+ endpoint: "fal-ai/object-removal",
1768
+ inputField: "image_url",
1769
+ inputKind: "image",
1770
+ name: "Object Removal",
1771
+ outputKeys: ["images"],
1772
+ price: { kind: "call", usd: 0.024 },
1773
+ pricing: "$0.024/image at the default best quality; $0.006 low, $0.012 medium, $0.018 high",
1774
+ sourceUrl: "https://fal.ai/models/fal-ai/object-removal",
1775
+ task: "prompted object removal"
1776
+ },
1777
+ "object-removal-bbox": {
1778
+ category: "erase",
1779
+ description: "Remove whatever falls inside supplied bounding boxes.",
1780
+ endpoint: "fal-ai/object-removal/bbox",
1781
+ inputField: "image_url",
1782
+ inputKind: "image",
1783
+ name: "Object Removal by Box",
1784
+ outputKeys: ["images"],
1785
+ price: { kind: "call", usd: 0.024 },
1786
+ pricing: "$0.024/image at the default best quality; $0.006 low, $0.012 medium, $0.018 high",
1787
+ sourceUrl: "https://fal.ai/models/fal-ai/object-removal/bbox",
1788
+ task: "bounding box object removal"
1789
+ },
1790
+ "object-removal-mask": {
1791
+ category: "erase",
1792
+ description: "Remove the masked region of an image and fill the gap.",
1793
+ endpoint: "fal-ai/object-removal/mask",
1794
+ inputField: "image_url",
1795
+ inputKind: "image",
1796
+ name: "Object Removal by Mask",
1797
+ outputKeys: ["images"],
1798
+ price: { kind: "call", usd: 0.024 },
1799
+ pricing: "$0.024/image at the default best quality; $0.006 low, $0.012 medium, $0.018 high",
1800
+ sourceUrl: "https://fal.ai/models/fal-ai/object-removal/mask",
1801
+ task: "masked object removal"
1802
+ },
1803
+ "remove-lighting": {
1804
+ category: "relight",
1805
+ description: "Strip baked-in lighting and shadows to leave a flat, neutral surface.",
1806
+ endpoint: "fal-ai/qwen-image-edit-plus-lora-gallery/remove-lighting",
1807
+ inputField: "image_urls",
1808
+ inputKind: "images",
1809
+ name: "Qwen Remove Lighting",
1810
+ outputKeys: ["images"],
1811
+ price: { kind: "megapixel", usd: 0.035 },
1812
+ pricing: "$0.035/megapixel",
1813
+ sourceUrl: "https://fal.ai/models/fal-ai/qwen-image-edit-plus-lora-gallery/remove-lighting",
1814
+ task: "lighting removal"
1815
+ },
1816
+ "smart-resize": {
1817
+ category: "reframe",
1818
+ description: "Resize one image to several target sizes, recomposing rather than cropping.",
1819
+ endpoint: "fal-ai/smart-resize",
1820
+ inputField: "image_url",
1821
+ inputKind: "image",
1822
+ name: "Smart Resize",
1823
+ outputKeys: ["images", "results"],
1824
+ price: { kind: "metered" },
1825
+ pricing: "$0.15 per output image, doubled at 4K, plus a $0.05 vision analysis fee per request",
1826
+ queued: true,
1827
+ sourceUrl: "https://fal.ai/models/fal-ai/smart-resize",
1828
+ task: "multi-size recomposition"
1829
+ },
1830
+ "text-removal": {
1831
+ category: "erase",
1832
+ defaultOptions: {
1833
+ output_format: "png"
1834
+ },
1835
+ description: "Remove all rendered text from an image, rebuilding what sat behind it.",
1836
+ endpoint: "fal-ai/image-editing/text-removal",
1837
+ inputField: "image_url",
1838
+ inputKind: "image",
1839
+ name: "Text Removal",
1840
+ outputKeys: ["images"],
1841
+ price: { kind: "call", usd: 0.04 },
1842
+ pricing: "$0.04/image",
1843
+ sourceUrl: "https://fal.ai/models/fal-ai/image-editing/text-removal",
1844
+ task: "text removal"
1845
+ }
1846
+ };
1847
+
1848
+ // src/tool-registry/structure.ts
1849
+ var STRUCTURE_TOOLS = {
1850
+ "depth-anything": {
1851
+ category: "preprocess",
1852
+ description: "Generate Depth Anything v2 depth maps from input images.",
1853
+ endpoint: "fal-ai/image-preprocessors/depth-anything/v2",
1854
+ inputField: "image_url",
1855
+ inputKind: "image",
1856
+ name: "Depth Anything v2 Preprocessor",
1857
+ outputKeys: ["image"],
1858
+ price: { kind: "metered" },
1859
+ pricing: "$0/compute-second listed by fal",
1860
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/depth-anything/v2",
1861
+ task: "depth preprocessing"
1862
+ },
1863
+ dwpose: {
1864
+ category: "preprocess",
1865
+ description: "Extract body, hand, and face pose skeletons from an image.",
1866
+ endpoint: "fal-ai/dwpose",
1867
+ inputField: "image_url",
1868
+ inputKind: "image",
1869
+ name: "DWPose Estimation",
1870
+ outputKeys: ["image"],
1871
+ price: { kind: "second", usd: 6e-4 },
1872
+ pricing: "$0.0006/compute-second",
1873
+ sourceUrl: "https://fal.ai/models/fal-ai/dwpose",
1874
+ task: "pose preprocessing"
1875
+ },
1876
+ hed: {
1877
+ category: "preprocess",
1878
+ description: "Generate HED soft edge maps for ControlNet-style workflows.",
1879
+ endpoint: "fal-ai/image-preprocessors/hed",
1880
+ inputField: "image_url",
1881
+ inputKind: "image",
1882
+ name: "HED Edge Preprocessor",
1883
+ outputKeys: ["image"],
1884
+ price: { kind: "metered" },
1885
+ pricing: "$0/compute-second listed by fal",
1886
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/hed",
1887
+ task: "edge preprocessing"
1888
+ },
1889
+ lineart: {
1890
+ category: "preprocess",
1891
+ defaultOptions: {
1892
+ coarse: false
1893
+ },
1894
+ description: "Generate line art/control-style edges from an input image.",
1895
+ endpoint: "fal-ai/image-preprocessors/lineart",
1896
+ inputField: "image_url",
1897
+ inputKind: "image",
1898
+ name: "Line Art Preprocessor",
1899
+ outputKeys: ["image"],
1900
+ price: { kind: "metered" },
1901
+ pricing: "$0/compute-second listed by fal",
1902
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/lineart",
1903
+ task: "image preprocessing"
1904
+ },
1905
+ "marigold-depth": {
1906
+ category: "depth",
1907
+ defaultOptions: {
1908
+ ensemble_size: 10,
1909
+ num_inference_steps: 10
1910
+ },
1911
+ description: "Create depth maps using Marigold depth estimation.",
1912
+ endpoint: "fal-ai/imageutils/marigold-depth",
1913
+ inputField: "image_url",
1914
+ inputKind: "image",
1915
+ name: "Marigold Depth Estimation",
1916
+ outputKeys: ["image"],
1917
+ price: { kind: "metered" },
1918
+ pricing: "$0/compute-second listed by fal",
1919
+ sourceUrl: "https://fal.ai/models/fal-ai/imageutils/marigold-depth",
1920
+ task: "depth map"
1921
+ },
1922
+ "midas-depth": {
1923
+ category: "depth",
1924
+ defaultOptions: {
1925
+ a: Math.PI * 2,
1926
+ bg_th: 0.1
1927
+ },
1928
+ description: "Create MiDaS depth maps from input images.",
1929
+ endpoint: "fal-ai/imageutils/depth",
1028
1930
  inputField: "image_url",
1029
1931
  inputKind: "image",
1030
1932
  name: "MiDaS Depth Estimation",
1031
1933
  outputKeys: ["image"],
1934
+ price: { kind: "metered" },
1032
1935
  pricing: "$0/compute-second listed by fal",
1033
1936
  sourceUrl: "https://fal.ai/models/fal-ai/imageutils/depth",
1034
1937
  task: "depth map"
@@ -1041,36 +1944,36 @@ var FAL_TOOLS = {
1041
1944
  inputKind: "image",
1042
1945
  name: "MiDaS Preprocessor",
1043
1946
  outputKeys: ["depth_map", "normal_map"],
1947
+ price: { kind: "metered" },
1044
1948
  pricing: "$0/compute-second listed by fal",
1045
1949
  sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/midas",
1046
1950
  task: "depth and normal preprocessing"
1047
1951
  },
1048
- nsfw: {
1049
- category: "moderation",
1050
- description: "Predict whether one or more images contain NSFW concepts.",
1051
- endpoint: "fal-ai/x-ailab/nsfw",
1052
- inputField: "image_urls",
1053
- inputKind: "images",
1054
- name: "NSFW Checker",
1055
- outputKeys: ["has_nsfw_concepts"],
1056
- pricing: "$0.001/image",
1057
- sourceUrl: "https://fal.ai/models/fal-ai/x-ailab/nsfw",
1058
- task: "vision moderation"
1952
+ mlsd: {
1953
+ category: "preprocess",
1954
+ description: "Detect straight line segments for architectural and interior control maps.",
1955
+ endpoint: "fal-ai/image-preprocessors/mlsd",
1956
+ inputField: "image_url",
1957
+ inputKind: "image",
1958
+ name: "M-LSD Line Preprocessor",
1959
+ outputKeys: ["image"],
1960
+ price: { kind: "metered" },
1961
+ pricing: "$0/compute-second listed by fal",
1962
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/mlsd",
1963
+ task: "line segment preprocessing"
1059
1964
  },
1060
- rembg: {
1061
- category: "background",
1062
- defaultOptions: {
1063
- crop_to_bbox: false
1064
- },
1065
- description: "Generic image background removal utility.",
1066
- endpoint: "fal-ai/imageutils/rembg",
1965
+ pidi: {
1966
+ category: "preprocess",
1967
+ description: "Generate PiDiNet soft edge maps from an input image.",
1968
+ endpoint: "fal-ai/image-preprocessors/pidi",
1067
1969
  inputField: "image_url",
1068
1970
  inputKind: "image",
1069
- name: "Remove Background",
1971
+ name: "PiDiNet Edge Preprocessor",
1070
1972
  outputKeys: ["image"],
1973
+ price: { kind: "metered" },
1071
1974
  pricing: "$0/compute-second listed by fal",
1072
- sourceUrl: "https://fal.ai/models/fal-ai/imageutils/rembg",
1073
- task: "image background removal"
1975
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/pidi",
1976
+ task: "edge preprocessing"
1074
1977
  },
1075
1978
  "sam-preprocessor": {
1076
1979
  category: "preprocess",
@@ -1080,6 +1983,7 @@ var FAL_TOOLS = {
1080
1983
  inputKind: "image",
1081
1984
  name: "SAM Preprocessor",
1082
1985
  outputKeys: ["image"],
1986
+ price: { kind: "metered" },
1083
1987
  pricing: "$0/compute-second listed by fal",
1084
1988
  sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/sam",
1085
1989
  task: "segmentation preprocessing"
@@ -1099,7 +2003,9 @@ var FAL_TOOLS = {
1099
2003
  inputKind: "image",
1100
2004
  name: "SAM 2 Auto Segment",
1101
2005
  outputKeys: ["combined_mask", "individual_masks"],
2006
+ price: { kind: "metered" },
1102
2007
  pricing: "$0/compute-second listed by fal",
2008
+ queued: true,
1103
2009
  sourceUrl: "https://fal.ai/models/fal-ai/sam2/auto-segment",
1104
2010
  task: "automatic image segmentation"
1105
2011
  },
@@ -1116,7 +2022,8 @@ var FAL_TOOLS = {
1116
2022
  inputKind: "image",
1117
2023
  name: "SAM 3.1 Image",
1118
2024
  outputKeys: ["image", "masks", "metadata", "scores", "boxes"],
1119
- pricing: "$0.005/request",
2025
+ price: { kind: "call", usd: 0.01 },
2026
+ pricing: "$0.01/request",
1120
2027
  sourceUrl: "https://fal.ai/models/fal-ai/sam-3-1/image",
1121
2028
  task: "promptable image segmentation"
1122
2029
  },
@@ -1132,61 +2039,12 @@ var FAL_TOOLS = {
1132
2039
  inputKind: "video",
1133
2040
  name: "SAM 3.1 Video",
1134
2041
  outputKeys: ["video", "boundingbox_frames_zip"],
1135
- pricing: "fal pricing varies by frame count",
2042
+ price: { kind: "metered" },
2043
+ pricing: "$0.01/16 frames of video input",
2044
+ queued: true,
1136
2045
  sourceUrl: "https://fal.ai/models/fal-ai/sam-3-1/video",
1137
2046
  task: "multi-object video segmentation"
1138
2047
  },
1139
- "sam3-3d-align": {
1140
- category: "3d",
1141
- description: "Align SAM 3D objects and bodies into a shared scene.",
1142
- endpoint: "fal-ai/sam-3/3d-align",
1143
- inputField: "image_url",
1144
- inputKind: "image",
1145
- name: "SAM 3D Align",
1146
- outputKeys: ["scene_glb", "metadata", "artifacts_zip"],
1147
- pricing: "fal pricing varies by scene",
1148
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-align",
1149
- task: "3D scene alignment"
1150
- },
1151
- "sam3-3d-body": {
1152
- category: "3d",
1153
- defaultOptions: {
1154
- export_meshes: true,
1155
- include_3d_keypoints: true,
1156
- include_mhr_params: true
1157
- },
1158
- description: "Reconstruct human body meshes and keypoints from a single image.",
1159
- endpoint: "fal-ai/sam-3/3d-body",
1160
- inputField: "image_url",
1161
- inputKind: "image",
1162
- name: "SAM 3D Body",
1163
- outputKeys: ["model_glb", "visualization", "meshes", "metadata"],
1164
- pricing: "$0.015/inference",
1165
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-body",
1166
- task: "single-image 3D body reconstruction"
1167
- },
1168
- "sam3-3d-objects": {
1169
- category: "3d",
1170
- defaultOptions: {
1171
- prompt: "car"
1172
- },
1173
- description: "Reconstruct one or more 3D objects from an image and prompts.",
1174
- endpoint: "fal-ai/sam-3/3d-objects",
1175
- inputField: "image_url",
1176
- inputKind: "image",
1177
- name: "SAM 3D Objects",
1178
- outputKeys: [
1179
- "gaussian_splat",
1180
- "model_glb",
1181
- "metadata",
1182
- "individual_splats",
1183
- "individual_glbs",
1184
- "artifacts_zip"
1185
- ],
1186
- pricing: "$0.02/generation",
1187
- sourceUrl: "https://fal.ai/models/fal-ai/sam-3/3d-objects",
1188
- task: "single-image 3D object reconstruction"
1189
- },
1190
2048
  "sam3-image": {
1191
2049
  category: "segmentation",
1192
2050
  defaultOptions: {
@@ -1200,6 +2058,7 @@ var FAL_TOOLS = {
1200
2058
  inputKind: "image",
1201
2059
  name: "SAM 3 Image",
1202
2060
  outputKeys: ["image", "masks", "metadata", "scores", "boxes"],
2061
+ price: { kind: "call", usd: 5e-3 },
1203
2062
  pricing: "$0.005/request",
1204
2063
  sourceUrl: "https://fal.ai/models/fal-ai/sam-3/image",
1205
2064
  task: "promptable image segmentation"
@@ -1215,7 +2074,8 @@ var FAL_TOOLS = {
1215
2074
  inputField: "image_url",
1216
2075
  inputKind: "image",
1217
2076
  name: "SAM 3 Image RLE",
1218
- outputKeys: ["rle_masks", "metadata", "scores", "boxes"],
2077
+ outputKeys: ["rle", "scores", "boxes", "metadata"],
2078
+ price: { kind: "call", usd: 5e-3 },
1219
2079
  pricing: "$0.005/request",
1220
2080
  sourceUrl: "https://fal.ai/models/fal-ai/sam-3/image-rle",
1221
2081
  task: "promptable image segmentation to RLE"
@@ -1234,7 +2094,9 @@ var FAL_TOOLS = {
1234
2094
  inputKind: "video",
1235
2095
  name: "SAM 3 Video",
1236
2096
  outputKeys: ["video", "boundingbox_frames_zip"],
1237
- pricing: "$0.005/16 frames",
2097
+ price: { kind: "metered" },
2098
+ pricing: "$0.005/16 frames of video input",
2099
+ queued: true,
1238
2100
  sourceUrl: "https://fal.ai/models/fal-ai/sam-3/video",
1239
2101
  task: "promptable video segmentation"
1240
2102
  },
@@ -1250,65 +2112,68 @@ var FAL_TOOLS = {
1250
2112
  inputField: "video_url",
1251
2113
  inputKind: "video",
1252
2114
  name: "SAM 3 Video RLE",
1253
- outputKeys: ["rle_masks", "metadata"],
1254
- pricing: "$0.005/16 frames",
2115
+ // fal's video-rle endpoint no longer returns RLE data: its Output schema is
2116
+ // now identical to sam3-video's, so this entry differs only by endpoint.
2117
+ outputKeys: ["video", "boundingbox_frames_zip"],
2118
+ price: { kind: "metered" },
2119
+ pricing: "$0.005/16 frames of video",
2120
+ queued: true,
1255
2121
  sourceUrl: "https://fal.ai/models/fal-ai/sam-3/video-rle",
1256
2122
  task: "promptable video segmentation to RLE"
1257
2123
  },
1258
- "topaz-image": {
1259
- category: "upscale",
1260
- defaultOptions: {
1261
- model: "Standard V2",
1262
- output_format: "jpeg",
1263
- upscale_factor: 2
1264
- },
1265
- description: "Professional Topaz image enhancement and upscaling.",
1266
- endpoint: "fal-ai/topaz/upscale/image",
2124
+ scribble: {
2125
+ category: "preprocess",
2126
+ description: "Turn an image into a scribble-style control map.",
2127
+ endpoint: "fal-ai/image-preprocessors/scribble",
1267
2128
  inputField: "image_url",
1268
2129
  inputKind: "image",
1269
- name: "Topaz Image Upscale",
2130
+ name: "Scribble Preprocessor",
1270
2131
  outputKeys: ["image"],
1271
- pricing: "$0.08+ by output megapixels",
1272
- sourceUrl: "https://fal.ai/models/fal-ai/topaz/upscale/image",
1273
- task: "image enhancement"
2132
+ price: { kind: "metered" },
2133
+ pricing: "$0/compute-second listed by fal",
2134
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/scribble",
2135
+ task: "scribble preprocessing"
1274
2136
  },
1275
- "topaz-video": {
1276
- category: "upscale",
1277
- defaultOptions: {
1278
- model: "Proteus",
1279
- upscale_factor: 2
1280
- },
1281
- description: "Professional Topaz video enhancement and upscaling.",
1282
- endpoint: "fal-ai/topaz/upscale/video",
1283
- inputField: "video_url",
1284
- inputKind: "video",
1285
- name: "Topaz Video Upscale",
1286
- outputKeys: ["video"],
1287
- pricing: "$0.01-$0.08/sec by output resolution",
1288
- sourceUrl: "https://fal.ai/models/fal-ai/topaz/upscale/video",
1289
- task: "video enhancement"
2137
+ teed: {
2138
+ category: "preprocess",
2139
+ description: "Generate TEED edge maps from an input image.",
2140
+ endpoint: "fal-ai/image-preprocessors/teed",
2141
+ inputField: "image_url",
2142
+ inputKind: "image",
2143
+ name: "TEED Edge Preprocessor",
2144
+ outputKeys: ["image"],
2145
+ price: { kind: "metered" },
2146
+ pricing: "$0/compute-second listed by fal",
2147
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/teed",
2148
+ task: "edge preprocessing"
2149
+ },
2150
+ "zoe-depth": {
2151
+ category: "preprocess",
2152
+ description: "Generate ZoeDepth metric depth maps from an input image.",
2153
+ endpoint: "fal-ai/image-preprocessors/zoe",
2154
+ inputField: "image_url",
2155
+ inputKind: "image",
2156
+ name: "ZoeDepth Preprocessor",
2157
+ outputKeys: ["image"],
2158
+ price: { kind: "metered" },
2159
+ pricing: "$0/compute-second listed by fal",
2160
+ sourceUrl: "https://fal.ai/models/fal-ai/image-preprocessors/zoe",
2161
+ task: "depth preprocessing"
1290
2162
  }
1291
2163
  };
2164
+
2165
+ // src/tools.ts
2166
+ var FAL_TOOLS = {
2167
+ ...ANALYSIS_TOOLS,
2168
+ ...ASSET_TOOLS,
2169
+ ...BACKGROUND_TOOLS,
2170
+ ...EDITING_TOOLS,
2171
+ ...STRUCTURE_TOOLS
2172
+ };
1292
2173
  var FAL_TOOL_IDS = Object.keys(
1293
2174
  FAL_TOOLS
1294
2175
  );
1295
2176
 
1296
- // src/server.ts
1297
- var MotifError = class extends Error {
1298
- status;
1299
- code;
1300
- /** fal's request-correlation id (from the `x-fal-request-id` header or the
1301
- * error body). Ties a failure back to fal's dashboard/support. */
1302
- requestId;
1303
- constructor(message, status, code, requestId) {
1304
- super(message);
1305
- this.name = "MotifError";
1306
- this.status = status;
1307
- this.code = code;
1308
- this.requestId = requestId;
1309
- }
1310
- };
1311
-
1312
2177
  // src/image/fal.ts
1313
2178
  import { createFal } from "@ai-sdk/fal";
1314
2179
  var FAL_FLUX_MODEL = "fal-ai/flux-pro/v1.1-ultra";
@@ -1588,9 +2453,9 @@ function createMotifImage(config = {}, deps = {}) {
1588
2453
  ...opts.headers === void 0 ? {} : { headers: opts.headers },
1589
2454
  ...opts.providerOptions === void 0 ? {} : { providerOptions: toProviderOptions(opts.providerOptions) }
1590
2455
  });
1591
- return ok2(toMotifImageResult(result, provider, modelId));
2456
+ return ok3(toMotifImageResult(result, provider, modelId));
1592
2457
  } catch (error) {
1593
- return err2(toMotifError(error));
2458
+ return err3(toMotifError2(error));
1594
2459
  }
1595
2460
  }
1596
2461
  async function edit(opts) {
@@ -1611,9 +2476,9 @@ function createMotifImage(config = {}, deps = {}) {
1611
2476
  ...opts.headers === void 0 ? {} : { headers: opts.headers },
1612
2477
  ...opts.providerOptions === void 0 ? {} : { providerOptions: toProviderOptions(opts.providerOptions) }
1613
2478
  });
1614
- return ok2(toMotifImageResult(result, provider, modelId));
2479
+ return ok3(toMotifImageResult(result, provider, modelId));
1615
2480
  } catch (error) {
1616
- return err2(toMotifError(error));
2481
+ return err3(toMotifError2(error));
1617
2482
  }
1618
2483
  }
1619
2484
  async function bestOfN(opts) {
@@ -1635,19 +2500,19 @@ async function runCandidate(opts, index, generate, edit) {
1635
2500
  async function selectWinner(opts, candidates) {
1636
2501
  const { judge } = opts;
1637
2502
  if (judge === void 0) {
1638
- return ok2({ chosenIndex: 0 });
2503
+ return ok3({ chosenIndex: 0 });
1639
2504
  }
1640
2505
  const context = "images" in opts ? { instruction: opts.instruction } : { prompt: opts.prompt };
1641
2506
  const decision = await judge(candidates, context);
1642
2507
  if (!Number.isInteger(decision.index) || decision.index < 0 || decision.index >= candidates.length) {
1643
- return err2(
2508
+ return err3(
1644
2509
  new MotifError(
1645
2510
  `bestOfN judge returned an out-of-range index ${decision.index} (expected 0..${candidates.length - 1})`,
1646
2511
  0
1647
2512
  )
1648
2513
  );
1649
2514
  }
1650
- return ok2({
2515
+ return ok3({
1651
2516
  chosenIndex: decision.index,
1652
2517
  ...decision.reason === void 0 ? {} : { reason: decision.reason }
1653
2518
  });
@@ -1656,7 +2521,7 @@ async function runBestOfN(opts, generate, edit) {
1656
2521
  try {
1657
2522
  const { n } = opts;
1658
2523
  if (!Number.isInteger(n) || n < 1) {
1659
- return err2(
2524
+ return err3(
1660
2525
  new MotifError(`bestOfN requires an integer n >= 1 (got ${n})`, 0)
1661
2526
  );
1662
2527
  }
@@ -1675,23 +2540,23 @@ async function runBestOfN(opts, generate, edit) {
1675
2540
  }
1676
2541
  }
1677
2542
  if (candidates.length === 0) {
1678
- return err2(
2543
+ return err3(
1679
2544
  firstError ?? new MotifError(`all ${n} bestOfN candidates failed`, 0)
1680
2545
  );
1681
2546
  }
1682
2547
  const winner = await selectWinner(opts, candidates);
1683
2548
  if (winner.isErr()) {
1684
- return err2(winner.error);
2549
+ return err3(winner.error);
1685
2550
  }
1686
2551
  const { chosenIndex, reason } = winner.value;
1687
2552
  const best = candidates[chosenIndex];
1688
2553
  if (best === void 0) {
1689
- return err2(new MotifError("bestOfN failed to resolve a winner", 0));
2554
+ return err3(new MotifError("bestOfN failed to resolve a winner", 0));
1690
2555
  }
1691
2556
  const totalCostUsd = Number(
1692
2557
  candidates.reduce((sum, candidate) => sum + candidate.cost.usd, 0).toFixed(6)
1693
2558
  );
1694
- return ok2({
2559
+ return ok3({
1695
2560
  best,
1696
2561
  chosenIndex,
1697
2562
  ...reason === void 0 ? {} : { reason },
@@ -1699,7 +2564,7 @@ async function runBestOfN(opts, generate, edit) {
1699
2564
  totalCostUsd
1700
2565
  });
1701
2566
  } catch (error) {
1702
- return err2(toMotifError(error));
2567
+ return err3(toMotifError2(error));
1703
2568
  }
1704
2569
  }
1705
2570
  function defaultResolveModel(provider, modelId, apiKey) {
@@ -1815,7 +2680,7 @@ function toJsonValue(value, path) {
1815
2680
  0
1816
2681
  );
1817
2682
  }
1818
- function toMotifError(error) {
2683
+ function toMotifError2(error) {
1819
2684
  if (error instanceof MotifError) {
1820
2685
  return error;
1821
2686
  }