@shop-os/modelkit 0.1.10 → 0.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/catalog.cjs CHANGED
@@ -20,7 +20,10 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  // src/catalog-entry.ts
21
21
  var catalog_entry_exports = {};
22
22
  __export(catalog_entry_exports, {
23
+ ADAPTER_IDS: () => ADAPTER_IDS,
23
24
  Catalog: () => Catalog,
25
+ CatalogModelSchema: () => CatalogModelSchema,
26
+ CatalogSchema: () => CatalogSchema,
24
27
  IMAGE_MODELS: () => IMAGE_MODELS,
25
28
  MODELS: () => MODELS,
26
29
  ModelKitError: () => ModelKitError,
@@ -28,7 +31,10 @@ __export(catalog_entry_exports, {
28
31
  TEXT_MODELS: () => TEXT_MODELS,
29
32
  VIDEO_MODELS: () => VIDEO_MODELS,
30
33
  isModelKitError: () => isModelKitError,
34
+ liveCatalog: () => liveCatalog,
31
35
  nearestAspectRatio: () => nearestAspectRatio,
36
+ primeCatalog: () => primeCatalog,
37
+ resetLiveCatalog: () => resetLiveCatalog,
32
38
  resolveAudioOptions: () => resolveAudioOptions,
33
39
  resolveImageOptions: () => resolveImageOptions,
34
40
  resolveOptions: () => resolveOptions,
@@ -131,11 +137,18 @@ var Catalog = class _Catalog {
131
137
  ...rows
132
138
  ]);
133
139
  }
140
+ /** Atomically swap every row. Builds the maps first so a bad batch throws before anything changes. */
141
+ replace(rows) {
142
+ const next = new _Catalog(rows);
143
+ this.byId.clear();
144
+ this.byAlias.clear();
145
+ for (const [id, model] of next.byId) this.byId.set(id, model);
146
+ for (const [alias, id] of next.byAlias) this.byAlias.set(alias, id);
147
+ }
134
148
  };
135
149
 
136
150
  // src/catalog/image.ts
137
- var IMAGE_USAGE = { unit: "images", modality: "IMAGE" };
138
- var IMAGE_GENERATE_EDIT = { imageGenerate: true, imageEdit: true };
151
+ var IMAGE_USAGE = { unit: "images" };
139
152
  var GEMINI_RATIOS = [
140
153
  "1:1",
141
154
  "2:3",
@@ -167,8 +180,7 @@ function geminiImage(id, label, aliases, sizes, display, exposure) {
167
180
  defaults: { size: "1K", aspectRatio: "1:1" },
168
181
  usage: { ...IMAGE_USAGE, slug: "gemini" },
169
182
  pricingKind: "image_size",
170
- display,
171
- capabilities: { ...IMAGE_GENERATE_EDIT }
183
+ display
172
184
  };
173
185
  return model;
174
186
  }
@@ -255,8 +267,7 @@ var IMAGE_MODELS = [
255
267
  provider: "openai",
256
268
  description: "The most capable model for image editing and complex creative instructions. Excels at text rendering, visual consistency, design generation, and iterative refinement.",
257
269
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/1204b494-8962-4f7f-af5e-699a8bdc98e3_compressed.png"
258
- },
259
- capabilities: { ...IMAGE_GENERATE_EDIT }
270
+ }
260
271
  },
261
272
  {
262
273
  // Same Images API surface as gpt-image-2 plus the "xhigh" and "max" quality tiers.
@@ -290,8 +301,7 @@ var IMAGE_MODELS = [
290
301
  display: {
291
302
  provider: "openai",
292
303
  description: "OpenAI's most capable GPT Image 2.5 tier, built for editing precision; takes text and image input and outputs images in low, medium, high, xhigh, or max quality."
293
- },
294
- capabilities: { ...IMAGE_GENERATE_EDIT }
304
+ }
295
305
  },
296
306
  {
297
307
  // Same Images API surface as gpt-image-2 plus the "xhigh" and "max" quality tiers.
@@ -325,8 +335,7 @@ var IMAGE_MODELS = [
325
335
  display: {
326
336
  provider: "openai",
327
337
  description: "OpenAI's fastest GPT Image 2.5 tier for high-quality everyday image generation and editing; takes text and image input and outputs images."
328
- },
329
- capabilities: { ...IMAGE_GENERATE_EDIT }
338
+ }
330
339
  },
331
340
  {
332
341
  // Cowork allows 7 ratios; trigger-workflows allows 9 (adds 3:2, 9:21, drops 4:5/5:4).
@@ -364,8 +373,7 @@ var IMAGE_MODELS = [
364
373
  provider: "meta",
365
374
  description: "Meta's image model with built-in world knowledge and strong prompt adherence. Great for concept-rich scenes, realistic compositions, and reference-guided edits.",
366
375
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/characters/1787909065794-k9a7zz.png"
367
- },
368
- capabilities: { ...IMAGE_GENERATE_EDIT }
376
+ }
369
377
  },
370
378
  {
371
379
  id: "seedream-5-0-pro",
@@ -391,8 +399,7 @@ var IMAGE_MODELS = [
391
399
  provider: "bytedance",
392
400
  logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png",
393
401
  description: "ByteDance Seedream 5.0 Pro text-to-image and multi-reference image-to-image (up to 10 inputs) with region editing; outputs 1K, 1.5K, or 2K."
394
- },
395
- capabilities: { ...IMAGE_GENERATE_EDIT }
402
+ }
396
403
  },
397
404
  {
398
405
  id: "seedream-4.5",
@@ -429,8 +436,7 @@ var IMAGE_MODELS = [
429
436
  display: {
430
437
  provider: "bytedance",
431
438
  description: "ByteDance Seedream 4.5 text-to-image and image-to-image with up to 14 reference images and batch image-set generation; outputs 2K or 4K."
432
- },
433
- capabilities: { ...IMAGE_GENERATE_EDIT }
439
+ }
434
440
  },
435
441
  {
436
442
  id: "flux-2-pro",
@@ -452,9 +458,9 @@ var IMAGE_MODELS = [
452
458
  pricingKind: "image_size",
453
459
  display: {
454
460
  provider: "black-forest-labs",
461
+ logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/flux.png",
455
462
  description: "Black Forest Labs FLUX.2 [pro] text-to-image and multi-reference editing model served via Prodia; takes a prompt and up to 8 input images and outputs up to 4096x4096."
456
- },
457
- capabilities: { ...IMAGE_GENERATE_EDIT }
463
+ }
458
464
  },
459
465
  {
460
466
  // Prodia's gemini-3-pro jobs take aspect_ratio + resolution (1K/2K/4K), not
@@ -480,8 +486,7 @@ var IMAGE_MODELS = [
480
486
  display: {
481
487
  provider: "google",
482
488
  description: "Google Gemini 3 Pro image generation and editing served via Prodia; takes a prompt plus up to 3 reference images and outputs 1K, 2K, or 4K."
483
- },
484
- capabilities: { ...IMAGE_GENERATE_EDIT }
489
+ }
485
490
  },
486
491
  {
487
492
  // Nano Banana (img2img.v2) is flat-rate with aspect_ratio only; 3:2, 4:5,
@@ -506,8 +511,7 @@ var IMAGE_MODELS = [
506
511
  display: {
507
512
  provider: "google",
508
513
  description: "Google Nano Banana image generation and editing served via Prodia; takes a prompt plus 1-3 input images and matches the first input's aspect ratio in roughly 8 seconds."
509
- },
510
- capabilities: { ...IMAGE_GENERATE_EDIT }
514
+ }
511
515
  },
512
516
  {
513
517
  // WAN 2.7 takes `images` (up to 9) and a `size` enum whose exact values
@@ -528,13 +532,12 @@ var IMAGE_MODELS = [
528
532
  supports: []
529
533
  },
530
534
  defaults: { size: "1K", aspectRatio: "1:1" },
531
- usage: { unit: "compute_seconds", modality: "IMAGE" },
535
+ usage: { unit: "compute_seconds" },
532
536
  pricingKind: "image_size",
533
537
  display: {
534
538
  provider: "alibaba",
535
539
  description: "Alibaba Wan 2.7 Image Pro text-to-image and image-editing model; takes a prompt plus up to 9 reference images and generates up to 4K (4096x4096)."
536
- },
537
- capabilities: { ...IMAGE_GENERATE_EDIT }
540
+ }
538
541
  },
539
542
  {
540
543
  // Qwen Image 2 Pro has aspect_ratio but no size input; resolution follows
@@ -556,13 +559,12 @@ var IMAGE_MODELS = [
556
559
  supports: []
557
560
  },
558
561
  defaults: { aspectRatio: "1:1" },
559
- usage: { unit: "compute_seconds", modality: "IMAGE" },
562
+ usage: { unit: "compute_seconds" },
560
563
  pricingKind: "image_size",
561
564
  display: {
562
565
  provider: "alibaba",
563
566
  description: "Alibaba Qwen Image 2 Pro unified text-to-image and editing model; takes a prompt plus an optional single reference image and generates native 2K output."
564
- },
565
- capabilities: { ...IMAGE_GENERATE_EDIT }
567
+ }
566
568
  },
567
569
  {
568
570
  // Qwen Image Edit Plus takes aspect_ratio (or match_input_image) and
@@ -584,13 +586,12 @@ var IMAGE_MODELS = [
584
586
  supports: ["outputFormat"]
585
587
  },
586
588
  defaults: { outputFormat: "png" },
587
- usage: { unit: "compute_seconds", modality: "IMAGE" },
589
+ usage: { unit: "compute_seconds" },
588
590
  pricingKind: "image_size",
589
591
  display: {
590
592
  provider: "alibaba",
591
593
  description: "Alibaba Qwen Image Edit Plus with Lightning and a skin-retouching LoRA; takes one or more reference images plus an edit prompt and outputs webp, jpg, or png."
592
- },
593
- capabilities: { ...IMAGE_GENERATE_EDIT }
594
+ }
594
595
  }
595
596
  ];
596
597
 
@@ -612,7 +613,7 @@ var OTHER_MODELS = [
612
613
  supports: []
613
614
  },
614
615
  defaults: {},
615
- usage: { unit: "images", modality: "AUDIO" },
616
+ usage: { unit: "images" },
616
617
  pricingKind: "flat",
617
618
  display: {
618
619
  provider: "google",
@@ -635,7 +636,7 @@ var OTHER_MODELS = [
635
636
  supports: []
636
637
  },
637
638
  defaults: {},
638
- usage: { unit: "images", modality: "AUDIO" },
639
+ usage: { unit: "images" },
639
640
  pricingKind: "flat",
640
641
  display: {
641
642
  provider: "google",
@@ -658,7 +659,7 @@ var OTHER_MODELS = [
658
659
  supports: ["seed", "negativePrompt"]
659
660
  },
660
661
  defaults: {},
661
- usage: { unit: "compute_seconds", modality: "AUDIO" },
662
+ usage: { unit: "compute_seconds" },
662
663
  pricingKind: "flat",
663
664
  display: {
664
665
  provider: "google",
@@ -681,7 +682,7 @@ var OTHER_MODELS = [
681
682
  supports: ["seed", "negativePrompt"]
682
683
  },
683
684
  defaults: { durationSeconds: 8 },
684
- usage: { unit: "compute_seconds", modality: "AUDIO" },
685
+ usage: { unit: "compute_seconds" },
685
686
  pricingKind: "flat",
686
687
  display: {
687
688
  provider: "mmaudio",
@@ -704,7 +705,7 @@ var OTHER_MODELS = [
704
705
  },
705
706
  defaults: { durationSeconds: 15, instrumental: true },
706
707
  // Replicate's run() does not report predict_time; billed by generated length.
707
- usage: { unit: "seconds", modality: "AUDIO" },
708
+ usage: { unit: "seconds" },
708
709
  pricingKind: "flat",
709
710
  display: {
710
711
  provider: "elevenlabs",
@@ -727,13 +728,13 @@ var OTHER_MODELS = [
727
728
  supports: ["scaleFactor", "outputFormat", "upscale"]
728
729
  },
729
730
  defaults: { scaleFactor: 2, outputFormat: "png" },
730
- usage: { unit: "compute_seconds", modality: "IMAGE" },
731
+ usage: { unit: "compute_seconds" },
731
732
  pricingKind: "flat",
732
733
  display: {
733
734
  provider: "topaz",
735
+ logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/topazlabs.png",
734
736
  description: "Topaz Labs AI image upscaling on Replicate; takes one input image and upscales up to 6x with selectable enhancement models and optional face enhancement."
735
- },
736
- capabilities: { imageEdit: true }
737
+ }
737
738
  },
738
739
  {
739
740
  id: "bg-remove",
@@ -750,19 +751,30 @@ var OTHER_MODELS = [
750
751
  supports: []
751
752
  },
752
753
  defaults: {},
753
- usage: { unit: "images", modality: "IMAGE" },
754
+ usage: { unit: "images" },
754
755
  pricingKind: "flat",
755
756
  display: {
756
757
  provider: "prodia",
757
758
  description: "Prodia background removal; takes one input image and returns a PNG cut-out of the subject plus an optional black-and-white mask."
758
- },
759
- capabilities: { imageEdit: true }
759
+ }
760
760
  }
761
761
  ];
762
762
 
763
763
  // src/catalog/text.ts
764
- var TEXT_USAGE = { unit: "tokens", modality: "TEXT" };
764
+ var TEXT_USAGE = { unit: "tokens" };
765
+ var CDN = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image";
766
+ var PROVIDER_LOGOS = {
767
+ anthropic: `${CDN}/25fad392-188c-4829-a3d0-c4fa02d1c8cd.png`,
768
+ openai: `${CDN}/1204b494-8962-4f7f-af5e-699a8bdc98e3.png`,
769
+ google: `${CDN}/192485d2-a76a-4657-9c06-930b2c70badd.png`,
770
+ deepseek: `${CDN}/9b7406d2-a696-4558-ab79-90a37e3f4574.png`,
771
+ xai: `${CDN}/858b7fcc-9bd5-411d-aea1-a7b35ecfffab.png`,
772
+ moonshot: `${CDN}/5a1e2eae-3474-4fac-9c74-a93f41ee6f67.png`,
773
+ minimax: `${CDN}/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png`
774
+ };
765
775
  function text(id, label, providerModelId, extra) {
776
+ const display = extra?.display;
777
+ const logo = display && !display.logo ? PROVIDER_LOGOS[display.provider] : void 0;
766
778
  return {
767
779
  id,
768
780
  aliases: [],
@@ -776,7 +788,8 @@ function text(id, label, providerModelId, extra) {
776
788
  defaults: {},
777
789
  usage: { ...TEXT_USAGE },
778
790
  pricingKind: "token",
779
- ...extra
791
+ ...extra,
792
+ ...display && logo ? { display: { ...display, logo } } : {}
780
793
  };
781
794
  }
782
795
  var ANTHROPIC_THINKING = {
@@ -795,7 +808,7 @@ var TEXT_MODELS = [
795
808
  costTier: 2,
796
809
  description: "OpenAI GPT-4o flagship multimodal model; text and image input, 128K-token context."
797
810
  },
798
- capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 128e3 }
811
+ capabilities: { vision: true, contextWindow: 128e3 }
799
812
  }),
800
813
  text("gpt-4o-mini", "GPT-4o mini", "gpt-4o-mini", {
801
814
  exposure: "internal",
@@ -804,7 +817,7 @@ var TEXT_MODELS = [
804
817
  costTier: 1,
805
818
  description: "OpenAI GPT-4o mini fast, low-cost multimodal model; text and image input, 128K-token context."
806
819
  },
807
- capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 128e3 }
820
+ capabilities: { vision: true, contextWindow: 128e3 }
808
821
  }),
809
822
  text("gpt-5.1", "GPT-5.1", "gpt-5.1", {
810
823
  exposure: "catalog",
@@ -813,7 +826,7 @@ var TEXT_MODELS = [
813
826
  costTier: 3,
814
827
  description: "OpenAI GPT-5.1 reasoning model for coding and agentic tasks; text and image input, 400K-token context."
815
828
  },
816
- capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 4e5 }
829
+ capabilities: { vision: true, contextWindow: 4e5 }
817
830
  }),
818
831
  text("gpt-5.2", "GPT-5.2", "gpt-5.2", {
819
832
  exposure: "catalog",
@@ -822,7 +835,7 @@ var TEXT_MODELS = [
822
835
  costTier: 3,
823
836
  description: "OpenAI GPT-5.2 reasoning model for complex professional work; text and image input, 400K-token context."
824
837
  },
825
- capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 4e5 }
838
+ capabilities: { vision: true, contextWindow: 4e5 }
826
839
  }),
827
840
  // 1.05M-token context: https://platform.openai.com/docs/models/gpt-5.4
828
841
  text("gpt-5.4", "GPT-5.4", "gpt-5.4", {
@@ -834,8 +847,6 @@ var TEXT_MODELS = [
834
847
  },
835
848
  capabilities: {
836
849
  vision: true,
837
- tools: true,
838
- structuredOutput: true,
839
850
  contextWindow: 105e4
840
851
  }
841
852
  }),
@@ -848,7 +859,7 @@ var TEXT_MODELS = [
848
859
  costTier: 1,
849
860
  description: "OpenAI GPT-5.4 mini efficient reasoning model for high-volume workloads; text and image input, 400K-token context."
850
861
  },
851
- capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 4e5 }
862
+ capabilities: { vision: true, contextWindow: 4e5 }
852
863
  }),
853
864
  text("gpt-5.5", "GPT-5.5", "gpt-5.5", {
854
865
  exposure: "catalog",
@@ -859,8 +870,6 @@ var TEXT_MODELS = [
859
870
  },
860
871
  capabilities: {
861
872
  vision: true,
862
- tools: true,
863
- structuredOutput: true,
864
873
  contextWindow: 105e4
865
874
  }
866
875
  }),
@@ -875,8 +884,6 @@ var TEXT_MODELS = [
875
884
  capabilities: {
876
885
  reasoning: true,
877
886
  vision: true,
878
- tools: true,
879
- structuredOutput: true,
880
887
  contextWindow: 105e4
881
888
  }
882
889
  }),
@@ -891,8 +898,6 @@ var TEXT_MODELS = [
891
898
  capabilities: {
892
899
  reasoning: true,
893
900
  vision: true,
894
- tools: true,
895
- structuredOutput: true,
896
901
  contextWindow: 105e4
897
902
  }
898
903
  }),
@@ -907,8 +912,6 @@ var TEXT_MODELS = [
907
912
  capabilities: {
908
913
  reasoning: true,
909
914
  vision: true,
910
- tools: true,
911
- structuredOutput: true,
912
915
  contextWindow: 105e4
913
916
  }
914
917
  }),
@@ -921,7 +924,7 @@ var TEXT_MODELS = [
921
924
  costTier: 2,
922
925
  description: "Anthropic Claude Sonnet 4.5 (legacy) balanced model; text and image input, 200K-token context."
923
926
  },
924
- capabilities: { vision: true, tools: true, structuredOutput: true, contextWindow: 2e5 }
927
+ capabilities: { vision: true, contextWindow: 2e5 }
925
928
  }),
926
929
  // Anthropic retired the dated 4.0 ids; callers that still send them get 4.6.
927
930
  text("claude-sonnet-4-6", "Claude Sonnet 4.6", "claude-sonnet-4-6", {
@@ -941,8 +944,6 @@ var TEXT_MODELS = [
941
944
  capabilities: {
942
945
  reasoning: true,
943
946
  vision: true,
944
- tools: true,
945
- structuredOutput: true,
946
947
  contextWindow: 1e6
947
948
  }
948
949
  }),
@@ -957,8 +958,6 @@ var TEXT_MODELS = [
957
958
  capabilities: {
958
959
  reasoning: true,
959
960
  vision: true,
960
- tools: true,
961
- structuredOutput: true,
962
961
  contextWindow: 1e6
963
962
  }
964
963
  }),
@@ -974,8 +973,6 @@ var TEXT_MODELS = [
974
973
  capabilities: {
975
974
  reasoning: true,
976
975
  vision: true,
977
- tools: true,
978
- structuredOutput: true,
979
976
  contextWindow: 1e6
980
977
  }
981
978
  }),
@@ -991,8 +988,6 @@ var TEXT_MODELS = [
991
988
  capabilities: {
992
989
  reasoning: true,
993
990
  vision: true,
994
- tools: true,
995
- structuredOutput: true,
996
991
  contextWindow: 2e5
997
992
  }
998
993
  }),
@@ -1006,8 +1001,6 @@ var TEXT_MODELS = [
1006
1001
  },
1007
1002
  capabilities: {
1008
1003
  vision: true,
1009
- tools: true,
1010
- structuredOutput: true,
1011
1004
  contextWindow: 1e6
1012
1005
  }
1013
1006
  }),
@@ -1020,8 +1013,6 @@ var TEXT_MODELS = [
1020
1013
  },
1021
1014
  capabilities: {
1022
1015
  vision: true,
1023
- tools: true,
1024
- structuredOutput: true,
1025
1016
  contextWindow: 1e6
1026
1017
  }
1027
1018
  }),
@@ -1034,8 +1025,6 @@ var TEXT_MODELS = [
1034
1025
  },
1035
1026
  capabilities: {
1036
1027
  vision: true,
1037
- tools: true,
1038
- structuredOutput: true,
1039
1028
  contextWindow: 1048576
1040
1029
  }
1041
1030
  }),
@@ -1048,8 +1037,6 @@ var TEXT_MODELS = [
1048
1037
  },
1049
1038
  capabilities: {
1050
1039
  vision: true,
1051
- tools: true,
1052
- structuredOutput: true,
1053
1040
  contextWindow: 1048576
1054
1041
  }
1055
1042
  }),
@@ -1062,8 +1049,6 @@ var TEXT_MODELS = [
1062
1049
  },
1063
1050
  capabilities: {
1064
1051
  vision: true,
1065
- tools: true,
1066
- structuredOutput: true,
1067
1052
  contextWindow: 1048576
1068
1053
  }
1069
1054
  }),
@@ -1076,8 +1061,6 @@ var TEXT_MODELS = [
1076
1061
  },
1077
1062
  capabilities: {
1078
1063
  vision: true,
1079
- tools: true,
1080
- structuredOutput: true,
1081
1064
  contextWindow: 1048576
1082
1065
  }
1083
1066
  }),
@@ -1090,8 +1073,6 @@ var TEXT_MODELS = [
1090
1073
  },
1091
1074
  capabilities: {
1092
1075
  vision: true,
1093
- tools: true,
1094
- structuredOutput: true,
1095
1076
  contextWindow: 1048576
1096
1077
  }
1097
1078
  }),
@@ -1105,8 +1086,6 @@ var TEXT_MODELS = [
1105
1086
  },
1106
1087
  capabilities: {
1107
1088
  vision: true,
1108
- tools: true,
1109
- structuredOutput: true,
1110
1089
  contextWindow: 1048576
1111
1090
  }
1112
1091
  }),
@@ -1119,8 +1098,6 @@ var TEXT_MODELS = [
1119
1098
  },
1120
1099
  capabilities: {
1121
1100
  vision: true,
1122
- tools: true,
1123
- structuredOutput: true,
1124
1101
  contextWindow: 1048576
1125
1102
  }
1126
1103
  }),
@@ -1139,8 +1116,6 @@ var TEXT_MODELS = [
1139
1116
  },
1140
1117
  capabilities: {
1141
1118
  vision: true,
1142
- tools: true,
1143
- structuredOutput: true,
1144
1119
  contextWindow: 1048576
1145
1120
  }
1146
1121
  }),
@@ -1153,8 +1128,6 @@ var TEXT_MODELS = [
1153
1128
  },
1154
1129
  capabilities: {
1155
1130
  vision: true,
1156
- tools: false,
1157
- structuredOutput: true,
1158
1131
  contextWindow: 128e3
1159
1132
  }
1160
1133
  }),
@@ -1167,8 +1140,6 @@ var TEXT_MODELS = [
1167
1140
  },
1168
1141
  capabilities: {
1169
1142
  vision: true,
1170
- tools: false,
1171
- structuredOutput: true,
1172
1143
  contextWindow: 2e5
1173
1144
  }
1174
1145
  }),
@@ -1181,8 +1152,6 @@ var TEXT_MODELS = [
1181
1152
  },
1182
1153
  capabilities: {
1183
1154
  vision: true,
1184
- tools: false,
1185
- structuredOutput: true,
1186
1155
  contextWindow: 128e3
1187
1156
  }
1188
1157
  }),
@@ -1202,8 +1171,6 @@ var TEXT_MODELS = [
1202
1171
  capabilities: {
1203
1172
  reasoning: true,
1204
1173
  vision: true,
1205
- tools: true,
1206
- structuredOutput: true,
1207
1174
  contextWindow: 1e6
1208
1175
  }
1209
1176
  }),
@@ -1218,8 +1185,6 @@ var TEXT_MODELS = [
1218
1185
  capabilities: {
1219
1186
  reasoning: true,
1220
1187
  vision: false,
1221
- tools: true,
1222
- structuredOutput: true,
1223
1188
  contextWindow: 1e6
1224
1189
  }
1225
1190
  }),
@@ -1233,14 +1198,14 @@ var TEXT_MODELS = [
1233
1198
  },
1234
1199
  capabilities: {
1235
1200
  vision: true,
1236
- tools: true,
1237
- structuredOutput: true,
1238
1201
  contextWindow: 1e6
1239
1202
  }
1240
1203
  }),
1241
1204
  // Moonshot retired kimi-k2.5 on 2026-08-31 and no longer publishes its specs.
1242
1205
  text("kimi-k2.5", "Kimi K2.5", "kimi-k2.5", {
1243
- exposure: "catalog",
1206
+ lifecycle: "deprecated",
1207
+ replacedBy: "kimi-k3",
1208
+ exposure: "internal",
1244
1209
  display: {
1245
1210
  chat: { temperature: 1 },
1246
1211
  provider: "moonshot",
@@ -1260,8 +1225,6 @@ var TEXT_MODELS = [
1260
1225
  capabilities: {
1261
1226
  reasoning: true,
1262
1227
  vision: true,
1263
- tools: true,
1264
- structuredOutput: true,
1265
1228
  contextWindow: 1e6
1266
1229
  }
1267
1230
  }),
@@ -1275,12 +1238,12 @@ var TEXT_MODELS = [
1275
1238
  costTier: 2,
1276
1239
  description: "MiniMax M3 frontier multimodal model for agentic reasoning, tool use, and coding; text, image, and video input, 1M-token context."
1277
1240
  },
1278
- capabilities: { vision: true, tools: true, contextWindow: 1e6 }
1241
+ capabilities: { vision: true, contextWindow: 1e6 }
1279
1242
  })
1280
1243
  ];
1281
1244
 
1282
1245
  // src/catalog/video.ts
1283
- var VIDEO_USAGE = { unit: "seconds", modality: "VIDEO" };
1246
+ var VIDEO_USAGE = { unit: "seconds" };
1284
1247
  var SEEDANCE_RATIOS = ["21:9", "16:9", "4:3", "1:1", "3:4", "9:16", "adaptive"];
1285
1248
  var KLING_RATIOS = ["16:9", "9:16", "1:1", "adaptive"];
1286
1249
  var OMNI_RATIOS = ["16:9", "9:16"];
@@ -1454,7 +1417,7 @@ var VIDEO_MODELS = [
1454
1417
  constraints: {
1455
1418
  aspectRatios: [...SEEDANCE_RATIOS],
1456
1419
  resolutions: ["480p", "720p", "1080p", "4k"],
1457
- durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
1420
+ durations: { min: 4, max: 15 },
1458
1421
  durationPresets: [4, 6, 8, 10, 12, 15],
1459
1422
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
1460
1423
  ratioIgnoredWith: ["firstFrame"],
@@ -1487,7 +1450,7 @@ var VIDEO_MODELS = [
1487
1450
  constraints: {
1488
1451
  aspectRatios: [...SEEDANCE_RATIOS],
1489
1452
  resolutions: ["480p", "720p"],
1490
- durations: [4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15],
1453
+ durations: { min: 4, max: 15 },
1491
1454
  references: { images: 9, videos: 3, audios: 3, audioOnly: false },
1492
1455
  ratioIgnoredWith: ["firstFrame"],
1493
1456
  supports: ["generateAudio", "seed", "lastFrame", "watermark"]
@@ -1560,7 +1523,7 @@ var VIDEO_MODELS = [
1560
1523
  },
1561
1524
  // No resolution default: xAI picks its own unless the caller asks.
1562
1525
  defaults: { durationSeconds: 8, aspectRatio: "16:9" },
1563
- usage: { unit: "reported_cost_usd", modality: "VIDEO", slug: "grok" },
1526
+ usage: { unit: "reported_cost_usd", slug: "grok" },
1564
1527
  pricingKind: "video_per_second",
1565
1528
  display: {
1566
1529
  provider: "xai",
@@ -1594,6 +1557,7 @@ var VIDEO_MODELS = [
1594
1557
  pricingKind: "video_per_second",
1595
1558
  display: {
1596
1559
  provider: "kuaishou",
1560
+ logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1597
1561
  description: "Kuaishou Kling 2.5 Turbo Pro image-to-video; animates a single input image from a text prompt with an optional end image, 5- or 10-second clips."
1598
1562
  },
1599
1563
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
@@ -1624,6 +1588,7 @@ var VIDEO_MODELS = [
1624
1588
  pricingKind: "video_per_second",
1625
1589
  display: {
1626
1590
  provider: "kuaishou",
1591
+ logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1627
1592
  description: "Kuaishou Kling 3.0 Pro image-to-video; a required start image plus a text prompt (or multi-shot prompts) with optional end image and native audio, 3-15 second clips."
1628
1593
  },
1629
1594
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
@@ -1657,10 +1622,11 @@ var VIDEO_MODELS = [
1657
1622
  supports: ["generateAudio", "lastFrame"]
1658
1623
  },
1659
1624
  defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
1660
- usage: { unit: "compute_seconds", modality: "VIDEO" },
1625
+ usage: { unit: "compute_seconds" },
1661
1626
  pricingKind: "video_per_second",
1662
1627
  display: {
1663
1628
  provider: "kuaishou",
1629
+ logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1664
1630
  description: "Kuaishou Kling Video 3.0 Omni unified generation and editing from text, start/end images, up to 7 reference images, and reference video; up to 15 seconds at up to 1080p."
1665
1631
  },
1666
1632
  capabilities: {}
@@ -1690,6 +1656,7 @@ var VIDEO_MODELS = [
1690
1656
  pricingKind: "video_per_second",
1691
1657
  display: {
1692
1658
  provider: "minimax",
1659
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png",
1693
1660
  description: "MiniMax H3 multimodal video model for text-to-video, image-to-video (first/last frame), and reference-to-video with native audio, up to 2K."
1694
1661
  },
1695
1662
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
@@ -1719,6 +1686,7 @@ var VIDEO_MODELS = [
1719
1686
  pricingKind: "video_per_second",
1720
1687
  display: {
1721
1688
  provider: "minimax",
1689
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png",
1722
1690
  description: "High-speed MiniMax H3 Max variant for text-to-video and image-to-video (first/last frame) only, up to 768p."
1723
1691
  },
1724
1692
  capabilities: { ...FRAMES_EXCLUDE_REFERENCES }
@@ -1749,6 +1717,7 @@ var VIDEO_MODELS = [
1749
1717
  pricingKind: "video_per_second",
1750
1718
  display: {
1751
1719
  provider: "minimax",
1720
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png",
1752
1721
  description: "MiniMax H3 reference-to-video mode guided by up to 9 reference images, 3 reference videos, and 3 audio clips; reference and image-to-video inputs cannot be mixed."
1753
1722
  },
1754
1723
  capabilities: {}
@@ -1779,6 +1748,7 @@ var VIDEO_MODELS = [
1779
1748
  pricingKind: "video_per_second",
1780
1749
  display: {
1781
1750
  provider: "minimax",
1751
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png",
1782
1752
  description: "MiniMax H3 Max reference-to-video on fal; conditions on reference images, videos, and audio. MiniMax's own docs do not list a reference mode for H3 Max."
1783
1753
  },
1784
1754
  capabilities: {}
@@ -1804,6 +1774,7 @@ var VIDEO_MODELS = [
1804
1774
  pricingKind: "video_per_second",
1805
1775
  display: {
1806
1776
  provider: "kuaishou",
1777
+ logo: "https://cdn.jsdelivr.net/npm/@lobehub/icons-static-png@1.97.0/light/kling-color.png",
1807
1778
  description: "Kuaishou Kling O1 Reference Video-to-Video; a prompt plus a 3-10 second reference video (optional reference images) generates 3-10 second clips preserving motion and camera language."
1808
1779
  },
1809
1780
  capabilities: { acceptsSourceVideo: true }
@@ -1823,10 +1794,11 @@ var VIDEO_MODELS = [
1823
1794
  supports: ["lastFrame"]
1824
1795
  },
1825
1796
  defaults: { durationSeconds: 6, resolution: "768p" },
1826
- usage: { unit: "compute_seconds", modality: "VIDEO" },
1797
+ usage: { unit: "compute_seconds" },
1827
1798
  pricingKind: "video_per_second",
1828
1799
  display: {
1829
1800
  provider: "minimax",
1801
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/f4c2e906-28b7-4a4a-b491-df8fef7808ed.png",
1830
1802
  description: "MiniMax Hailuo 02 text-to-video and image-to-video model with realistic physics and strong prompt following; outputs 768p at 6 or 10 seconds."
1831
1803
  },
1832
1804
  capabilities: {}
@@ -1863,6 +1835,387 @@ var MODELS = [
1863
1835
  ...OTHER_MODELS
1864
1836
  ];
1865
1837
 
1838
+ // src/catalog/schema.ts
1839
+ var import_zod = require("zod");
1840
+ var ADAPTER_IDS = [
1841
+ "ai-sdk-text",
1842
+ "google-image",
1843
+ "openai-image",
1844
+ "google-genai",
1845
+ "ark",
1846
+ "fal",
1847
+ "replicate",
1848
+ "prodia",
1849
+ "higgsfield",
1850
+ "xai-video"
1851
+ ];
1852
+ var ModelTypeSchema = import_zod.z.enum(["text", "image", "video", "audio", "upscale", "bg-remove"]);
1853
+ var LifecycleSchema = import_zod.z.enum(["active", "deprecated", "retired"]);
1854
+ var AdapterSchema = import_zod.z.enum(ADAPTER_IDS);
1855
+ var AspectRatioSchema = import_zod.z.enum([
1856
+ "1:1",
1857
+ "16:9",
1858
+ "9:16",
1859
+ "4:3",
1860
+ "3:4",
1861
+ "3:2",
1862
+ "2:3",
1863
+ "4:5",
1864
+ "5:4",
1865
+ "21:9",
1866
+ "9:21",
1867
+ "97:30",
1868
+ "adaptive"
1869
+ ]);
1870
+ var ImageSizeSchema = import_zod.z.enum(["512", "1K", "2K", "4K"]);
1871
+ var VideoResolutionSchema = import_zod.z.enum(["360p", "480p", "720p", "768p", "1080p", "2k", "4k"]);
1872
+ var ImageQualitySchema = import_zod.z.enum(["auto", "low", "medium", "high", "xhigh", "max"]);
1873
+ var OutputFormatSchema = import_zod.z.enum(["png", "jpeg", "webp"]);
1874
+ var RequiredInputSchema = import_zod.z.enum(["firstFrame", "sourceVideo", "references"]);
1875
+ var SupportedOptionSchema = import_zod.z.enum([
1876
+ "seed",
1877
+ "quality",
1878
+ "negativePrompt",
1879
+ "cfgScale",
1880
+ "generateAudio",
1881
+ "promptExpansion",
1882
+ "lastFrame",
1883
+ "count",
1884
+ "outputFormat",
1885
+ "scaleFactor",
1886
+ "safetySettings",
1887
+ "temperature",
1888
+ "upscale",
1889
+ "watermark",
1890
+ "safetyChecker",
1891
+ "instrumental"
1892
+ ]);
1893
+ var UsageUnitSchema = import_zod.z.enum([
1894
+ "tokens",
1895
+ "images",
1896
+ "seconds",
1897
+ "reported_cost_usd",
1898
+ "compute_seconds"
1899
+ ]);
1900
+ var PricingKindSchema = import_zod.z.enum(["token", "image_size", "video_per_second", "flat"]);
1901
+ var ProviderOptionValueSchema = import_zod.z.lazy(
1902
+ () => import_zod.z.union([
1903
+ import_zod.z.string(),
1904
+ import_zod.z.number(),
1905
+ import_zod.z.boolean(),
1906
+ import_zod.z.array(ProviderOptionValueSchema),
1907
+ import_zod.z.record(import_zod.z.string(), ProviderOptionValueSchema)
1908
+ ])
1909
+ );
1910
+ var ProviderOptionsSchema = import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), ProviderOptionValueSchema));
1911
+ var ReferenceLimitsSchema = import_zod.z.looseObject({
1912
+ images: import_zod.z.number().optional(),
1913
+ videos: import_zod.z.number().optional(),
1914
+ audios: import_zod.z.number().optional(),
1915
+ total: import_zod.z.number().optional(),
1916
+ audioOnly: import_zod.z.boolean().optional()
1917
+ });
1918
+ var ModelConstraintsSchema = import_zod.z.looseObject({
1919
+ aspectRatios: import_zod.z.array(AspectRatioSchema).optional(),
1920
+ sizes: import_zod.z.array(ImageSizeSchema).optional(),
1921
+ resolutions: import_zod.z.array(VideoResolutionSchema).optional(),
1922
+ durations: import_zod.z.union([import_zod.z.looseObject({ min: import_zod.z.number(), max: import_zod.z.number() }), import_zod.z.array(import_zod.z.number())]).optional(),
1923
+ durationPresets: import_zod.z.array(import_zod.z.number()).optional(),
1924
+ qualityOptions: import_zod.z.array(import_zod.z.string()).optional(),
1925
+ exactSizes: import_zod.z.record(import_zod.z.string(), import_zod.z.string()).optional(),
1926
+ references: ReferenceLimitsSchema.optional(),
1927
+ requires: import_zod.z.array(RequiredInputSchema).optional(),
1928
+ supports: import_zod.z.array(SupportedOptionSchema).optional(),
1929
+ ratioIgnoredWith: import_zod.z.array(import_zod.z.enum(["firstFrame", "sourceVideo"])).optional()
1930
+ });
1931
+ var MediaRefSchema = import_zod.z.union([
1932
+ import_zod.z.looseObject({ url: import_zod.z.string(), label: import_zod.z.string().optional() }),
1933
+ import_zod.z.looseObject({
1934
+ bytes: import_zod.z.instanceof(Uint8Array),
1935
+ mime: import_zod.z.string(),
1936
+ label: import_zod.z.string().optional()
1937
+ })
1938
+ ]);
1939
+ var ImageSafetySettingSchema = import_zod.z.looseObject({ category: import_zod.z.string(), threshold: import_zod.z.string() });
1940
+ var UpscaleOptionsSchema = import_zod.z.looseObject({
1941
+ enhanceModel: import_zod.z.string().optional(),
1942
+ subjectDetection: import_zod.z.string().optional(),
1943
+ faceEnhancement: import_zod.z.boolean().optional(),
1944
+ faceEnhancementCreativity: import_zod.z.number().optional(),
1945
+ faceEnhancementStrength: import_zod.z.number().optional()
1946
+ });
1947
+ var DefaultsSchema = import_zod.z.looseObject({
1948
+ references: import_zod.z.array(MediaRefSchema).optional(),
1949
+ aspectRatio: AspectRatioSchema.optional(),
1950
+ size: ImageSizeSchema.optional(),
1951
+ exactSize: import_zod.z.string().optional(),
1952
+ quality: ImageQualitySchema.optional(),
1953
+ seed: import_zod.z.number().optional(),
1954
+ count: import_zod.z.number().optional(),
1955
+ outputFormat: OutputFormatSchema.optional(),
1956
+ scaleFactor: import_zod.z.number().optional(),
1957
+ safetySettings: import_zod.z.array(ImageSafetySettingSchema).optional(),
1958
+ temperature: import_zod.z.number().optional(),
1959
+ upscale: UpscaleOptionsSchema.optional(),
1960
+ firstFrame: MediaRefSchema.optional(),
1961
+ lastFrame: MediaRefSchema.optional(),
1962
+ sourceVideo: MediaRefSchema.optional(),
1963
+ referenceImages: import_zod.z.array(MediaRefSchema).optional(),
1964
+ referenceVideos: import_zod.z.array(MediaRefSchema).optional(),
1965
+ referenceAudios: import_zod.z.array(MediaRefSchema).optional(),
1966
+ resolution: VideoResolutionSchema.optional(),
1967
+ durationSeconds: import_zod.z.number().optional(),
1968
+ generateAudio: import_zod.z.boolean().optional(),
1969
+ negativePrompt: import_zod.z.string().optional(),
1970
+ cfgScale: import_zod.z.number().optional(),
1971
+ promptExpansion: import_zod.z.enum(["fast", "balanced", "quality"]).optional(),
1972
+ watermark: import_zod.z.boolean().optional(),
1973
+ safetyChecker: import_zod.z.boolean().optional(),
1974
+ mode: import_zod.z.enum(["standard", "pro"]).optional(),
1975
+ instrumental: import_zod.z.boolean().optional()
1976
+ });
1977
+ var CatalogDisplaySchema = import_zod.z.looseObject({
1978
+ provider: import_zod.z.string(),
1979
+ description: import_zod.z.string(),
1980
+ logo: import_zod.z.string().optional(),
1981
+ costTier: import_zod.z.union([import_zod.z.literal(1), import_zod.z.literal(2), import_zod.z.literal(3)]).optional(),
1982
+ recommended: import_zod.z.boolean().optional(),
1983
+ sortOrder: import_zod.z.number().optional(),
1984
+ chat: import_zod.z.looseObject({
1985
+ providerOptions: ProviderOptionsSchema.optional(),
1986
+ temperature: import_zod.z.number().optional()
1987
+ }).optional()
1988
+ });
1989
+ var CatalogCapabilitiesSchema = import_zod.z.looseObject({
1990
+ acceptsSourceVideo: import_zod.z.boolean().optional(),
1991
+ framesExcludeReferences: import_zod.z.boolean().optional(),
1992
+ reasoning: import_zod.z.boolean().optional(),
1993
+ vision: import_zod.z.boolean().optional(),
1994
+ contextWindow: import_zod.z.number().optional()
1995
+ });
1996
+ var CatalogModelSchemaBase = import_zod.z.looseObject({
1997
+ id: import_zod.z.string().min(1),
1998
+ aliases: import_zod.z.array(import_zod.z.string()).default([]),
1999
+ label: import_zod.z.string().min(1),
2000
+ type: ModelTypeSchema,
2001
+ adapter: AdapterSchema,
2002
+ providerModelId: import_zod.z.string().min(1),
2003
+ lifecycle: LifecycleSchema,
2004
+ replacedBy: import_zod.z.string().optional(),
2005
+ exposure: import_zod.z.enum(["catalog", "internal"]),
2006
+ constraints: ModelConstraintsSchema,
2007
+ defaults: DefaultsSchema,
2008
+ providerOptions: ProviderOptionsSchema.optional(),
2009
+ usage: import_zod.z.looseObject({
2010
+ unit: UsageUnitSchema,
2011
+ slug: import_zod.z.string().optional(),
2012
+ providerLabel: import_zod.z.string().optional()
2013
+ }),
2014
+ pricingKind: PricingKindSchema,
2015
+ display: CatalogDisplaySchema.optional(),
2016
+ capabilities: CatalogCapabilitiesSchema.optional()
2017
+ }).superRefine((row, ctx) => {
2018
+ if (row.lifecycle === "active" && !row.display) {
2019
+ ctx.addIssue({
2020
+ code: "custom",
2021
+ path: ["display"],
2022
+ message: "active rows need display"
2023
+ });
2024
+ }
2025
+ if (row.lifecycle === "active" && row.type === "text" && !row.capabilities?.contextWindow) {
2026
+ ctx.addIssue({
2027
+ code: "custom",
2028
+ path: ["capabilities", "contextWindow"],
2029
+ message: "active text rows need contextWindow"
2030
+ });
2031
+ }
2032
+ if (row.replacedBy !== void 0 && row.replacedBy === row.id) {
2033
+ ctx.addIssue({
2034
+ code: "custom",
2035
+ path: ["replacedBy"],
2036
+ message: "replacedBy must not equal id"
2037
+ });
2038
+ }
2039
+ });
2040
+ var CatalogModelSchema = CatalogModelSchemaBase;
2041
+ var CatalogSchema = import_zod.z.array(CatalogModelSchemaBase).superRefine((rows, ctx) => {
2042
+ const seenIds = /* @__PURE__ */ new Map();
2043
+ rows.forEach((row, index) => {
2044
+ if (seenIds.has(row.id)) {
2045
+ ctx.addIssue({
2046
+ code: "custom",
2047
+ path: [index, "id"],
2048
+ message: `Duplicate model id "${row.id}"`
2049
+ });
2050
+ } else {
2051
+ seenIds.set(row.id, index);
2052
+ }
2053
+ });
2054
+ const seenAliases = /* @__PURE__ */ new Map();
2055
+ rows.forEach((row, index) => {
2056
+ row.aliases.forEach((alias, aliasIndex) => {
2057
+ const path = [index, "aliases", aliasIndex];
2058
+ if (alias === row.id || seenIds.has(alias)) {
2059
+ ctx.addIssue({
2060
+ code: "custom",
2061
+ path,
2062
+ message: `Alias "${alias}" collides with a model id`
2063
+ });
2064
+ return;
2065
+ }
2066
+ if (seenAliases.has(alias)) {
2067
+ ctx.addIssue({
2068
+ code: "custom",
2069
+ path,
2070
+ message: `Duplicate alias "${alias}"`
2071
+ });
2072
+ } else {
2073
+ seenAliases.set(alias, index);
2074
+ }
2075
+ });
2076
+ });
2077
+ });
2078
+
2079
+ // src/catalog/live.ts
2080
+ var liveCatalog = new Catalog(MODELS);
2081
+
2082
+ // src/catalog/load.ts
2083
+ var memo;
2084
+ var timer;
2085
+ var revision;
2086
+ var origin = "snapshot";
2087
+ function rowCount() {
2088
+ return liveCatalog.list({ lifecycle: ["active", "deprecated", "retired"] }).length;
2089
+ }
2090
+ function authHeaders(auth) {
2091
+ return "bearer" in auth ? { Authorization: `Bearer ${auth.bearer}` } : { "x-api-key": auth.apiKey };
2092
+ }
2093
+ function describeError(err) {
2094
+ return err instanceof Error ? err.message : String(err);
2095
+ }
2096
+ function parseEnvelope(json) {
2097
+ if (typeof json !== "object" || json === null || !("rows" in json)) {
2098
+ throw new Error("invalid catalog response: missing rows");
2099
+ }
2100
+ const rows = CatalogSchema.parse(json.rows);
2101
+ const rev = json.revision;
2102
+ if (typeof rev === "string") return { rows, revision: rev };
2103
+ return { rows };
2104
+ }
2105
+ function logPrimed(logger, result) {
2106
+ const { origin: loggedOrigin, rowCount: loggedCount, revision: loggedRevision } = result;
2107
+ logger.info("[modelkit] catalog primed", {
2108
+ origin: loggedOrigin,
2109
+ rowCount: loggedCount,
2110
+ revision: loggedRevision
2111
+ });
2112
+ }
2113
+ async function fetchRemote(source, logger) {
2114
+ const fetchImpl = source.fetch ?? globalThis.fetch;
2115
+ const headers = authHeaders(source.auth);
2116
+ if (revision !== void 0) headers["If-None-Match"] = `"${revision}"`;
2117
+ const res = await fetchImpl(`${source.baseUrl}/models/catalog`, {
2118
+ headers,
2119
+ signal: AbortSignal.timeout(source.timeoutMs ?? 1e4)
2120
+ });
2121
+ if (res.status === 304) return { kept: true };
2122
+ if (!res.ok) throw new Error(`catalog fetch failed with status ${res.status}`);
2123
+ const json = await res.json();
2124
+ const { rows, revision: rev } = parseEnvelope(json);
2125
+ if (rev === void 0) return { rows, fromCache: false };
2126
+ return { rows, revision: rev, fromCache: false };
2127
+ }
2128
+ async function writeCache(source, logger, rows, rev) {
2129
+ if (!source.cache) return;
2130
+ try {
2131
+ await source.cache.set(JSON.stringify({ rows, revision: rev }));
2132
+ } catch (err) {
2133
+ logger.warn("[modelkit] catalog cache write failed", err);
2134
+ }
2135
+ }
2136
+ async function prime(source, logger) {
2137
+ try {
2138
+ const fetched = await fetchRemote(source, logger);
2139
+ if ("kept" in fetched) {
2140
+ const result2 = { origin, rowCount: rowCount() };
2141
+ if (revision !== void 0) result2.revision = revision;
2142
+ logPrimed(logger, result2);
2143
+ return result2;
2144
+ }
2145
+ liveCatalog.replace(fetched.rows);
2146
+ origin = "remote";
2147
+ revision = fetched.revision;
2148
+ await writeCache(source, logger, fetched.rows, fetched.revision);
2149
+ const result = { origin, rowCount: rowCount() };
2150
+ if (fetched.revision !== void 0) result.revision = fetched.revision;
2151
+ logPrimed(logger, result);
2152
+ return result;
2153
+ } catch (err) {
2154
+ logger.warn("[modelkit] catalog remote failed, trying cache", err);
2155
+ const error = describeError(err);
2156
+ try {
2157
+ const cached = await source.cache?.get();
2158
+ if (!cached) throw new Error("no cached catalog");
2159
+ const { rows, revision: rev } = parseEnvelope(JSON.parse(cached));
2160
+ liveCatalog.replace(rows);
2161
+ origin = "last-good";
2162
+ revision = rev;
2163
+ const result = { origin, rowCount: rowCount(), error };
2164
+ if (rev !== void 0) result.revision = rev;
2165
+ logPrimed(logger, result);
2166
+ return result;
2167
+ } catch (cacheErr) {
2168
+ logger.warn("[modelkit] catalog cache failed, using snapshot", cacheErr);
2169
+ liveCatalog.replace([...MODELS]);
2170
+ origin = "snapshot";
2171
+ revision = void 0;
2172
+ const result = {
2173
+ origin,
2174
+ rowCount: rowCount(),
2175
+ error
2176
+ };
2177
+ logPrimed(logger, result);
2178
+ return result;
2179
+ }
2180
+ }
2181
+ }
2182
+ async function refresh(source, logger) {
2183
+ try {
2184
+ const fetched = await fetchRemote(source, logger);
2185
+ if ("kept" in fetched) return;
2186
+ liveCatalog.replace(fetched.rows);
2187
+ origin = "remote";
2188
+ revision = fetched.revision;
2189
+ await writeCache(source, logger, fetched.rows, fetched.revision);
2190
+ } catch (err) {
2191
+ logger.warn("[modelkit] catalog refresh failed, keeping current rows", err);
2192
+ }
2193
+ }
2194
+ function primeCatalog(source) {
2195
+ if (memo) return memo;
2196
+ const logger = source.logger ?? console;
2197
+ memo = prime(source, logger).then((result) => {
2198
+ const ttlMs = source.ttlMs ?? 3e5;
2199
+ if (ttlMs > 0 && timer === void 0) {
2200
+ timer = setInterval(() => void refresh(source, logger), ttlMs);
2201
+ const maybeUnref = timer;
2202
+ maybeUnref.unref?.();
2203
+ }
2204
+ return result;
2205
+ });
2206
+ return memo;
2207
+ }
2208
+ function resetLiveCatalog() {
2209
+ if (timer !== void 0) {
2210
+ clearInterval(timer);
2211
+ timer = void 0;
2212
+ }
2213
+ memo = void 0;
2214
+ revision = void 0;
2215
+ origin = "snapshot";
2216
+ liveCatalog.replace([...MODELS]);
2217
+ }
2218
+
1866
2219
  // src/resolve-options.ts
1867
2220
  var SIZE_ORDER = ["512", "1K", "2K", "4K"];
1868
2221
  var RESOLUTION_ORDER = ["360p", "480p", "720p", "768p", "1080p", "2k", "4k"];