@shop-os/modelkit 0.2.12 → 0.2.14

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.js CHANGED
@@ -126,7 +126,9 @@ function normalizeExposure(value) {
126
126
  if (value === "catalog") return ["cowork", "space_builder"];
127
127
  if (value === "internal") return [];
128
128
  if (Array.isArray(value)) {
129
- if (value.every((entry) => SURFACES.includes(entry))) {
129
+ if (value.every(
130
+ (entry) => SURFACES.includes(entry)
131
+ )) {
130
132
  const present = new Set(value);
131
133
  return SURFACES.filter((surface) => present.has(surface));
132
134
  }
@@ -1824,7 +1826,215 @@ var MODELS = [
1824
1826
  ];
1825
1827
 
1826
1828
  // src/catalog/schema.ts
1829
+ import { z as z2 } from "zod";
1830
+
1831
+ // src/catalog/collisions.ts
1832
+ function collisionIssues(rows) {
1833
+ const issues = [];
1834
+ const seenIds = /* @__PURE__ */ new Map();
1835
+ const seenAliases = /* @__PURE__ */ new Map();
1836
+ rows.forEach((row, index) => {
1837
+ if (seenIds.has(row.id)) {
1838
+ issues.push({
1839
+ index,
1840
+ path: ["id"],
1841
+ message: `Duplicate model id "${row.id}"`
1842
+ });
1843
+ } else {
1844
+ const aliasOwner = seenAliases.get(row.id);
1845
+ if (aliasOwner !== void 0) {
1846
+ issues.push({
1847
+ index: aliasOwner.index,
1848
+ path: ["aliases", aliasOwner.aliasIndex],
1849
+ message: `Alias "${row.id}" collides with a model id`
1850
+ });
1851
+ }
1852
+ seenIds.set(row.id, index);
1853
+ }
1854
+ const local = /* @__PURE__ */ new Set();
1855
+ for (const [aliasIndex, alias] of row.aliases.entries()) {
1856
+ if (alias === row.id || seenIds.has(alias)) {
1857
+ issues.push({
1858
+ index,
1859
+ path: ["aliases", aliasIndex],
1860
+ message: `Alias "${alias}" collides with a model id`
1861
+ });
1862
+ } else if (local.has(alias) || seenAliases.has(alias)) {
1863
+ issues.push({
1864
+ index,
1865
+ path: ["aliases", aliasIndex],
1866
+ message: `Duplicate alias "${alias}"`
1867
+ });
1868
+ }
1869
+ local.add(alias);
1870
+ if (!seenAliases.has(alias)) seenAliases.set(alias, { index, aliasIndex });
1871
+ }
1872
+ });
1873
+ return issues;
1874
+ }
1875
+
1876
+ // src/catalog/input-spec.ts
1827
1877
  import { z } from "zod";
1878
+ var VIDEO_INPUT_SPEC_KEYS = [
1879
+ "prompt",
1880
+ "durationSeconds",
1881
+ "aspectRatio",
1882
+ "resolution",
1883
+ "firstFrame",
1884
+ "lastFrame",
1885
+ "seed",
1886
+ "negativePrompt",
1887
+ "generateAudio",
1888
+ "cfgScale",
1889
+ "promptExpansion",
1890
+ "referenceImages"
1891
+ ];
1892
+ var AUDIO_INPUT_SPEC_KEYS = [
1893
+ "prompt",
1894
+ "durationSeconds",
1895
+ "seed",
1896
+ "negativePrompt",
1897
+ "references",
1898
+ "sourceVideo",
1899
+ "instrumental"
1900
+ ];
1901
+ var INPUT_SPEC_SUPPORTS_KEYS = [
1902
+ "lastFrame",
1903
+ "seed",
1904
+ "negativePrompt",
1905
+ "generateAudio",
1906
+ "cfgScale",
1907
+ "promptExpansion",
1908
+ "instrumental"
1909
+ ];
1910
+ var VIDEO_KEY_SET = new Set(VIDEO_INPUT_SPEC_KEYS);
1911
+ var AUDIO_KEY_SET = new Set(AUDIO_INPUT_SPEC_KEYS);
1912
+ var ALL_KEY_SET = /* @__PURE__ */ new Set([
1913
+ ...VIDEO_INPUT_SPEC_KEYS,
1914
+ ...AUDIO_INPUT_SPEC_KEYS
1915
+ ]);
1916
+ var ProviderScalarSchema = z.union([z.string(), z.number(), z.boolean()]);
1917
+ var FieldsSchema = z.record(z.string(), z.string().min(1)).superRefine((fields, ctx) => {
1918
+ const keys = Object.keys(fields);
1919
+ if (keys.length === 0) {
1920
+ ctx.addIssue({
1921
+ code: "custom",
1922
+ message: "inputSpec.fields must name at least one field"
1923
+ });
1924
+ return;
1925
+ }
1926
+ for (const key of keys) {
1927
+ if (!ALL_KEY_SET.has(key)) {
1928
+ ctx.addIssue({
1929
+ code: "custom",
1930
+ message: `inputSpec.fields has unknown canonical key "${key}"`
1931
+ });
1932
+ }
1933
+ }
1934
+ });
1935
+ var ValuesSchema = z.record(z.string(), z.record(z.string(), ProviderScalarSchema)).superRefine((values, ctx) => {
1936
+ for (const key of Object.keys(values)) {
1937
+ if (!ALL_KEY_SET.has(key)) {
1938
+ ctx.addIssue({
1939
+ code: "custom",
1940
+ message: `inputSpec.values has unknown canonical key "${key}"`
1941
+ });
1942
+ }
1943
+ }
1944
+ });
1945
+ var StaticSchema = z.record(z.string(), ProviderScalarSchema);
1946
+ var InputSpecSchema = z.strictObject({
1947
+ fields: FieldsSchema,
1948
+ values: ValuesSchema.optional(),
1949
+ static: StaticSchema.optional()
1950
+ }).superRefine((spec, ctx) => {
1951
+ const mappedNames = new Set(Object.values(spec.fields));
1952
+ for (const key of Object.keys(spec.static ?? {})) {
1953
+ if (mappedNames.has(key)) {
1954
+ ctx.addIssue({
1955
+ code: "custom",
1956
+ path: ["static", key],
1957
+ message: `inputSpec.static "${key}" collides with a mapped field name`
1958
+ });
1959
+ }
1960
+ }
1961
+ });
1962
+ function validateInputSpec(raw, row) {
1963
+ const parsed = InputSpecSchema.safeParse(raw);
1964
+ if (!parsed.success) {
1965
+ return {
1966
+ ok: false,
1967
+ issues: parsed.error.issues.map((issue) => ({
1968
+ path: issue.path,
1969
+ message: issue.message
1970
+ }))
1971
+ };
1972
+ }
1973
+ const spec = parsed.data;
1974
+ if (row.adapter !== "replicate") {
1975
+ return {
1976
+ ok: false,
1977
+ issues: [{ path: [], message: "inputSpec is only supported on replicate rows" }]
1978
+ };
1979
+ }
1980
+ const issues = [];
1981
+ if (row.type !== "video" && row.type !== "audio") {
1982
+ issues.push({
1983
+ path: [],
1984
+ message: `inputSpec is not supported for "${row.type}" rows`
1985
+ });
1986
+ } else {
1987
+ const allowed = row.type === "video" ? VIDEO_KEY_SET : AUDIO_KEY_SET;
1988
+ for (const key of Object.keys(spec.fields)) {
1989
+ if (!allowed.has(key)) {
1990
+ issues.push({
1991
+ path: ["fields", key],
1992
+ message: `inputSpec.fields "${key}" is not supported for ${row.type} rows`
1993
+ });
1994
+ }
1995
+ }
1996
+ for (const key of Object.keys(spec.values ?? {})) {
1997
+ if (!allowed.has(key)) {
1998
+ issues.push({
1999
+ path: ["values", key],
2000
+ message: `inputSpec.values "${key}" is not supported for ${row.type} rows`
2001
+ });
2002
+ }
2003
+ }
2004
+ }
2005
+ const seenNames = /* @__PURE__ */ new Map();
2006
+ for (const [key, name] of Object.entries(spec.fields)) {
2007
+ const first = seenNames.get(name);
2008
+ if (first === void 0) seenNames.set(name, key);
2009
+ else {
2010
+ issues.push({
2011
+ path: ["fields", key],
2012
+ message: `inputSpec.fields "${first}" and "${key}" both map to "${name}"`
2013
+ });
2014
+ }
2015
+ }
2016
+ for (const key of Object.keys(spec.values ?? {})) {
2017
+ if (!(key in spec.fields)) {
2018
+ issues.push({
2019
+ path: ["values", key],
2020
+ message: `inputSpec.values "${key}" has no matching fields entry`
2021
+ });
2022
+ }
2023
+ }
2024
+ const supports = row.constraints.supports ?? [];
2025
+ for (const key of INPUT_SPEC_SUPPORTS_KEYS) {
2026
+ if (key in spec.fields && !supports.includes(key)) {
2027
+ issues.push({
2028
+ path: ["fields", key],
2029
+ message: `inputSpec.fields "${key}" needs "${key}" in constraints.supports`
2030
+ });
2031
+ }
2032
+ }
2033
+ if (issues.length > 0) return { ok: false, issues };
2034
+ return { ok: true, spec };
2035
+ }
2036
+
2037
+ // src/catalog/schema.ts
1828
2038
  var ADAPTER_IDS = [
1829
2039
  "ai-sdk-text",
1830
2040
  "google-image",
@@ -1837,16 +2047,16 @@ var ADAPTER_IDS = [
1837
2047
  "higgsfield",
1838
2048
  "xai-video"
1839
2049
  ];
1840
- var ModelTypeSchema = z.enum(["text", "image", "video", "audio", "upscale", "bg-remove"]);
1841
- var LifecycleSchema = z.enum(["active", "deprecated", "retired"]);
1842
- var SurfaceSchema = z.enum(["cowork", "space_builder"]);
2050
+ var ModelTypeSchema = z2.enum(["text", "image", "video", "audio", "upscale", "bg-remove"]);
2051
+ var LifecycleSchema = z2.enum(["active", "deprecated", "retired"]);
2052
+ var SurfaceSchema = z2.enum(["cowork", "space_builder"]);
1843
2053
  var SURFACES2 = SurfaceSchema.options;
1844
- var ExposureSchema = z.preprocess(
2054
+ var ExposureSchema = z2.preprocess(
1845
2055
  normalizeExposure,
1846
- z.array(SurfaceSchema)
2056
+ z2.array(SurfaceSchema)
1847
2057
  );
1848
- var AdapterSchema = z.enum(ADAPTER_IDS);
1849
- var AspectRatioSchema = z.enum([
2058
+ var AdapterSchema = z2.enum(ADAPTER_IDS);
2059
+ var AspectRatioSchema = z2.enum([
1850
2060
  "1:1",
1851
2061
  "16:9",
1852
2062
  "9:16",
@@ -1861,12 +2071,12 @@ var AspectRatioSchema = z.enum([
1861
2071
  "97:30",
1862
2072
  "adaptive"
1863
2073
  ]);
1864
- var ImageSizeSchema = z.enum(["512", "1K", "2K", "4K"]);
1865
- var VideoResolutionSchema = z.enum(["360p", "480p", "720p", "768p", "1080p", "2k", "4k"]);
1866
- var ImageQualitySchema = z.enum(["auto", "low", "medium", "high", "xhigh", "max"]);
1867
- var OutputFormatSchema = z.enum(["png", "jpeg", "webp"]);
1868
- var RequiredInputSchema = z.enum(["firstFrame", "sourceVideo", "references"]);
1869
- var SupportedOptionSchema = z.enum([
2074
+ var ImageSizeSchema = z2.enum(["512", "1K", "2K", "4K"]);
2075
+ var VideoResolutionSchema = z2.enum(["360p", "480p", "720p", "768p", "1080p", "2k", "4k"]);
2076
+ var ImageQualitySchema = z2.enum(["auto", "low", "medium", "high", "xhigh", "max"]);
2077
+ var OutputFormatSchema = z2.enum(["png", "jpeg", "webp"]);
2078
+ var RequiredInputSchema = z2.enum(["firstFrame", "sourceVideo", "references"]);
2079
+ var SupportedOptionSchema = z2.enum([
1870
2080
  "seed",
1871
2081
  "quality",
1872
2082
  "negativePrompt",
@@ -1885,128 +2095,128 @@ var SupportedOptionSchema = z.enum([
1885
2095
  "taskType",
1886
2096
  "instrumental"
1887
2097
  ]);
1888
- var UsageUnitSchema = z.enum([
2098
+ var UsageUnitSchema = z2.enum([
1889
2099
  "tokens",
1890
2100
  "images",
1891
2101
  "seconds",
1892
2102
  "reported_cost_usd",
1893
2103
  "compute_seconds"
1894
2104
  ]);
1895
- var PricingKindSchema = z.enum(["token", "image_size", "video_per_second", "flat"]);
1896
- var ProviderOptionValueSchema = z.lazy(
1897
- () => z.union([
1898
- z.string(),
1899
- z.number(),
1900
- z.boolean(),
1901
- z.array(ProviderOptionValueSchema),
1902
- z.record(z.string(), ProviderOptionValueSchema)
2105
+ var PricingKindSchema = z2.enum(["token", "image_size", "video_per_second", "flat"]);
2106
+ var ProviderOptionValueSchema = z2.lazy(
2107
+ () => z2.union([
2108
+ z2.string(),
2109
+ z2.number(),
2110
+ z2.boolean(),
2111
+ z2.array(ProviderOptionValueSchema),
2112
+ z2.record(z2.string(), ProviderOptionValueSchema)
1903
2113
  ])
1904
2114
  );
1905
- var ProviderOptionsSchema = z.record(z.string(), z.record(z.string(), ProviderOptionValueSchema));
1906
- var ReferenceLimitsSchema = z.looseObject({
1907
- images: z.number().optional(),
1908
- imagesWithVideo: z.number().optional(),
1909
- videos: z.number().optional(),
1910
- audios: z.number().optional(),
1911
- total: z.number().optional(),
1912
- audioOnly: z.boolean().optional()
2115
+ var ProviderOptionsSchema = z2.record(z2.string(), z2.record(z2.string(), ProviderOptionValueSchema));
2116
+ var ReferenceLimitsSchema = z2.looseObject({
2117
+ images: z2.number().optional(),
2118
+ imagesWithVideo: z2.number().optional(),
2119
+ videos: z2.number().optional(),
2120
+ audios: z2.number().optional(),
2121
+ total: z2.number().optional(),
2122
+ audioOnly: z2.boolean().optional()
1913
2123
  });
1914
- var ModelConstraintsSchema = z.looseObject({
1915
- aspectRatios: z.array(AspectRatioSchema).optional(),
1916
- sizes: z.array(ImageSizeSchema).optional(),
1917
- resolutions: z.array(VideoResolutionSchema).optional(),
1918
- durations: z.union([z.looseObject({ min: z.number(), max: z.number() }), z.array(z.number())]).optional(),
1919
- durationPresets: z.array(z.number()).optional(),
1920
- qualityOptions: z.array(z.string()).optional(),
1921
- exactSizes: z.record(z.string(), z.string()).optional(),
2124
+ var ModelConstraintsSchema = z2.looseObject({
2125
+ aspectRatios: z2.array(AspectRatioSchema).optional(),
2126
+ sizes: z2.array(ImageSizeSchema).optional(),
2127
+ resolutions: z2.array(VideoResolutionSchema).optional(),
2128
+ durations: z2.union([z2.looseObject({ min: z2.number(), max: z2.number() }), z2.array(z2.number())]).optional(),
2129
+ durationPresets: z2.array(z2.number()).optional(),
2130
+ qualityOptions: z2.array(z2.string()).optional(),
2131
+ exactSizes: z2.record(z2.string(), z2.string()).optional(),
1922
2132
  references: ReferenceLimitsSchema.optional(),
1923
- requires: z.array(RequiredInputSchema).optional(),
1924
- supports: z.array(SupportedOptionSchema).optional(),
1925
- ratioIgnoredWith: z.array(z.enum(["firstFrame", "sourceVideo"])).optional()
2133
+ requires: z2.array(RequiredInputSchema).optional(),
2134
+ supports: z2.array(SupportedOptionSchema).optional(),
2135
+ ratioIgnoredWith: z2.array(z2.enum(["firstFrame", "sourceVideo"])).optional()
1926
2136
  });
1927
- var MediaRefSchema = z.union([
1928
- z.looseObject({ url: z.string(), label: z.string().optional() }),
1929
- z.looseObject({
1930
- bytes: z.instanceof(Uint8Array),
1931
- mime: z.string(),
1932
- label: z.string().optional()
2137
+ var MediaRefSchema = z2.union([
2138
+ z2.looseObject({ url: z2.string(), label: z2.string().optional() }),
2139
+ z2.looseObject({
2140
+ bytes: z2.instanceof(Uint8Array),
2141
+ mime: z2.string(),
2142
+ label: z2.string().optional()
1933
2143
  })
1934
2144
  ]);
1935
- var ImageSafetySettingSchema = z.looseObject({ category: z.string(), threshold: z.string() });
1936
- var UpscaleOptionsSchema = z.looseObject({
1937
- enhanceModel: z.string().optional(),
1938
- subjectDetection: z.string().optional(),
1939
- faceEnhancement: z.boolean().optional(),
1940
- faceEnhancementCreativity: z.number().optional(),
1941
- faceEnhancementStrength: z.number().optional()
2145
+ var ImageSafetySettingSchema = z2.looseObject({ category: z2.string(), threshold: z2.string() });
2146
+ var UpscaleOptionsSchema = z2.looseObject({
2147
+ enhanceModel: z2.string().optional(),
2148
+ subjectDetection: z2.string().optional(),
2149
+ faceEnhancement: z2.boolean().optional(),
2150
+ faceEnhancementCreativity: z2.number().optional(),
2151
+ faceEnhancementStrength: z2.number().optional()
1942
2152
  });
1943
- var DefaultsSchema = z.looseObject({
1944
- references: z.array(MediaRefSchema).optional(),
2153
+ var DefaultsSchema = z2.looseObject({
2154
+ references: z2.array(MediaRefSchema).optional(),
1945
2155
  aspectRatio: AspectRatioSchema.optional(),
1946
2156
  size: ImageSizeSchema.optional(),
1947
- exactSize: z.string().optional(),
2157
+ exactSize: z2.string().optional(),
1948
2158
  quality: ImageQualitySchema.optional(),
1949
- seed: z.number().optional(),
1950
- count: z.number().optional(),
2159
+ seed: z2.number().optional(),
2160
+ count: z2.number().optional(),
1951
2161
  outputFormat: OutputFormatSchema.optional(),
1952
- scaleFactor: z.number().optional(),
1953
- safetySettings: z.array(ImageSafetySettingSchema).optional(),
1954
- temperature: z.number().optional(),
2162
+ scaleFactor: z2.number().optional(),
2163
+ safetySettings: z2.array(ImageSafetySettingSchema).optional(),
2164
+ temperature: z2.number().optional(),
1955
2165
  upscale: UpscaleOptionsSchema.optional(),
1956
2166
  firstFrame: MediaRefSchema.optional(),
1957
2167
  lastFrame: MediaRefSchema.optional(),
1958
2168
  sourceVideo: MediaRefSchema.optional(),
1959
- referenceImages: z.array(MediaRefSchema).optional(),
1960
- referenceVideos: z.array(MediaRefSchema).optional(),
1961
- referenceAudios: z.array(MediaRefSchema).optional(),
2169
+ referenceImages: z2.array(MediaRefSchema).optional(),
2170
+ referenceVideos: z2.array(MediaRefSchema).optional(),
2171
+ referenceAudios: z2.array(MediaRefSchema).optional(),
1962
2172
  resolution: VideoResolutionSchema.optional(),
1963
- durationSeconds: z.number().optional(),
1964
- generateAudio: z.boolean().optional(),
1965
- negativePrompt: z.string().optional(),
1966
- cfgScale: z.number().optional(),
1967
- promptExpansion: z.enum(["fast", "balanced", "quality"]).optional(),
1968
- watermark: z.boolean().optional(),
1969
- safetyChecker: z.boolean().optional(),
1970
- taskType: z.enum(["reference", "edit", "extend"]).optional(),
1971
- mode: z.enum(["standard", "pro"]).optional(),
1972
- instrumental: z.boolean().optional()
2173
+ durationSeconds: z2.number().optional(),
2174
+ generateAudio: z2.boolean().optional(),
2175
+ negativePrompt: z2.string().optional(),
2176
+ cfgScale: z2.number().optional(),
2177
+ promptExpansion: z2.enum(["fast", "balanced", "quality"]).optional(),
2178
+ watermark: z2.boolean().optional(),
2179
+ safetyChecker: z2.boolean().optional(),
2180
+ taskType: z2.enum(["reference", "edit", "extend"]).optional(),
2181
+ mode: z2.enum(["standard", "pro"]).optional(),
2182
+ instrumental: z2.boolean().optional()
1973
2183
  });
1974
- var CatalogDisplaySchema = z.looseObject({
1975
- provider: z.string(),
1976
- description: z.string(),
1977
- logo: z.string().optional(),
1978
- costTier: z.union([z.literal(1), z.literal(2), z.literal(3)]).optional(),
1979
- recommended: z.boolean().optional(),
1980
- sortOrder: z.number().optional(),
1981
- chat: z.looseObject({
2184
+ var CatalogDisplaySchema = z2.looseObject({
2185
+ provider: z2.string(),
2186
+ description: z2.string(),
2187
+ logo: z2.string().optional(),
2188
+ costTier: z2.union([z2.literal(1), z2.literal(2), z2.literal(3)]).optional(),
2189
+ recommended: z2.boolean().optional(),
2190
+ sortOrder: z2.number().optional(),
2191
+ chat: z2.looseObject({
1982
2192
  providerOptions: ProviderOptionsSchema.optional(),
1983
- temperature: z.number().optional()
2193
+ temperature: z2.number().optional()
1984
2194
  }).optional()
1985
2195
  });
1986
- var CatalogCapabilitiesSchema = z.looseObject({
1987
- acceptsSourceVideo: z.boolean().optional(),
1988
- framesExcludeReferences: z.boolean().optional(),
1989
- reasoning: z.boolean().optional(),
1990
- vision: z.boolean().optional(),
1991
- contextWindow: z.number().optional()
2196
+ var CatalogCapabilitiesSchema = z2.looseObject({
2197
+ acceptsSourceVideo: z2.boolean().optional(),
2198
+ framesExcludeReferences: z2.boolean().optional(),
2199
+ reasoning: z2.boolean().optional(),
2200
+ vision: z2.boolean().optional(),
2201
+ contextWindow: z2.number().optional()
1992
2202
  });
1993
- var CatalogModelSchemaBase = z.looseObject({
1994
- id: z.string().min(1),
1995
- aliases: z.array(z.string()).default([]),
1996
- label: z.string().min(1),
2203
+ var CatalogModelSchemaBase = z2.looseObject({
2204
+ id: z2.string().min(1),
2205
+ aliases: z2.array(z2.string()).default([]),
2206
+ label: z2.string().min(1),
1997
2207
  type: ModelTypeSchema,
1998
2208
  adapter: AdapterSchema,
1999
- providerModelId: z.string().min(1),
2209
+ providerModelId: z2.string().min(1),
2000
2210
  lifecycle: LifecycleSchema,
2001
- replacedBy: z.string().optional(),
2211
+ replacedBy: z2.string().optional(),
2002
2212
  exposure: ExposureSchema,
2003
2213
  constraints: ModelConstraintsSchema,
2004
2214
  defaults: DefaultsSchema,
2005
2215
  providerOptions: ProviderOptionsSchema.optional(),
2006
- usage: z.looseObject({
2216
+ usage: z2.looseObject({
2007
2217
  unit: UsageUnitSchema,
2008
- slug: z.string().optional(),
2009
- providerLabel: z.string().optional()
2218
+ slug: z2.string().optional(),
2219
+ providerLabel: z2.string().optional()
2010
2220
  }),
2011
2221
  pricingKind: PricingKindSchema,
2012
2222
  display: CatalogDisplaySchema.optional(),
@@ -2044,44 +2254,29 @@ var CatalogModelSchema = CatalogModelSchemaBase.superRefine(
2044
2254
  message: 'transport must be "direct" or "bedrock"'
2045
2255
  });
2046
2256
  }
2257
+ const inputSpecRaw = row.providerOptions?.["modelkit"]?.["inputSpec"];
2258
+ if (inputSpecRaw !== void 0) {
2259
+ const result = validateInputSpec(inputSpecRaw, row);
2260
+ if (!result.ok) {
2261
+ for (const issue of result.issues) {
2262
+ ctx.addIssue({
2263
+ code: "custom",
2264
+ path: ["providerOptions", "modelkit", "inputSpec", ...issue.path],
2265
+ message: issue.message
2266
+ });
2267
+ }
2268
+ }
2269
+ }
2047
2270
  }
2048
2271
  );
2049
- var CatalogSchema = z.array(CatalogModelSchemaBase).superRefine((rows, ctx) => {
2050
- const seenIds = /* @__PURE__ */ new Map();
2051
- rows.forEach((row, index) => {
2052
- if (seenIds.has(row.id)) {
2053
- ctx.addIssue({
2054
- code: "custom",
2055
- path: [index, "id"],
2056
- message: `Duplicate model id "${row.id}"`
2057
- });
2058
- } else {
2059
- seenIds.set(row.id, index);
2060
- }
2061
- });
2062
- const seenAliases = /* @__PURE__ */ new Map();
2063
- rows.forEach((row, index) => {
2064
- row.aliases.forEach((alias, aliasIndex) => {
2065
- const path = [index, "aliases", aliasIndex];
2066
- if (alias === row.id || seenIds.has(alias)) {
2067
- ctx.addIssue({
2068
- code: "custom",
2069
- path,
2070
- message: `Alias "${alias}" collides with a model id`
2071
- });
2072
- return;
2073
- }
2074
- if (seenAliases.has(alias)) {
2075
- ctx.addIssue({
2076
- code: "custom",
2077
- path,
2078
- message: `Duplicate alias "${alias}"`
2079
- });
2080
- } else {
2081
- seenAliases.set(alias, index);
2082
- }
2272
+ var CatalogSchema = z2.array(CatalogModelSchemaBase).superRefine((rows, ctx) => {
2273
+ for (const issue of collisionIssues(rows)) {
2274
+ ctx.addIssue({
2275
+ code: "custom",
2276
+ path: [issue.index, ...issue.path],
2277
+ message: issue.message
2083
2278
  });
2084
- });
2279
+ }
2085
2280
  });
2086
2281
 
2087
2282
  // src/catalog/live.ts
@@ -2096,26 +2291,102 @@ var liveState = store[KEY] ??= {
2096
2291
  };
2097
2292
  var liveCatalog = liveState.catalog;
2098
2293
 
2294
+ // src/json.ts
2295
+ import { z as z3 } from "zod";
2296
+ var JsonValueSchema = z3.lazy(
2297
+ () => z3.union([
2298
+ z3.string(),
2299
+ z3.number(),
2300
+ z3.boolean(),
2301
+ z3.null(),
2302
+ z3.array(JsonValueSchema),
2303
+ z3.record(z3.string(), JsonValueSchema)
2304
+ ])
2305
+ );
2306
+ var JsonObjectSchema = z3.record(z3.string(), JsonValueSchema);
2307
+ var thrownErrorSchema = z3.object({
2308
+ message: z3.string().optional(),
2309
+ status: z3.number().optional(),
2310
+ statusCode: z3.number().optional(),
2311
+ responseBody: z3.string().optional()
2312
+ });
2313
+
2099
2314
  // src/catalog/load.ts
2100
- import { z as z2 } from "zod";
2315
+ import { z as z4 } from "zod";
2101
2316
  function rowCount() {
2102
2317
  return liveCatalog.list({ lifecycle: ["active", "deprecated", "retired"] }).length;
2103
2318
  }
2104
2319
  function authHeaders(auth) {
2105
2320
  return "bearer" in auth ? { Authorization: `Bearer ${auth.bearer}` } : { "x-api-key": auth.apiKey };
2106
2321
  }
2107
- var EnvelopeSchema = z2.object({
2108
- rows: CatalogSchema,
2109
- revision: z2.string().optional()
2322
+ var EnvelopeSchema = z4.object({
2323
+ rows: z4.array(JsonValueSchema),
2324
+ revision: z4.string().optional()
2110
2325
  });
2111
- function toEnvelope(data) {
2112
- if (data.rows.length === 0) throw new Error("invalid catalog response: no rows");
2113
- const envelope = { rows: data.rows };
2326
+ function dropReason(path, message) {
2327
+ return path.length === 0 ? message : `${path.map((part) => String(part)).join(".")} ${message}`;
2328
+ }
2329
+ function rowId(raw) {
2330
+ const parsed = z4.object({ id: z4.string().min(1) }).loose().safeParse(raw);
2331
+ if (!parsed.success) return "?";
2332
+ return parsed.data.id;
2333
+ }
2334
+ function quarantine(rawRows, logger) {
2335
+ const valid = [];
2336
+ let dropped = 0;
2337
+ for (const raw of rawRows) {
2338
+ const parsed = CatalogModelSchemaBase.safeParse(raw);
2339
+ if (!parsed.success) {
2340
+ const first = parsed.error.issues[0];
2341
+ const reason = first ? dropReason(first.path, first.message) : "invalid row";
2342
+ logger.warn(`[modelkit] catalog row dropped id=${rowId(raw)} reason=${reason}`);
2343
+ dropped += 1;
2344
+ continue;
2345
+ }
2346
+ const row = parsed.data;
2347
+ const inputSpecRaw = row.providerOptions?.["modelkit"]?.["inputSpec"];
2348
+ if (inputSpecRaw !== void 0) {
2349
+ const result = validateInputSpec(inputSpecRaw, row);
2350
+ if (!result.ok) {
2351
+ const first = result.issues[0];
2352
+ const detail = first ? dropReason(first.path, first.message) : "invalid inputSpec";
2353
+ logger.warn(
2354
+ `[modelkit] catalog row ${row.id} has an invalid providerOptions.modelkit.inputSpec: ${detail}; calls fail until it is fixed`
2355
+ );
2356
+ }
2357
+ }
2358
+ valid.push(row);
2359
+ }
2360
+ let kept = valid;
2361
+ for (; ; ) {
2362
+ const issues = collisionIssues(kept);
2363
+ if (issues.length === 0) break;
2364
+ const firstByIndex = /* @__PURE__ */ new Map();
2365
+ for (const issue of issues) {
2366
+ if (!firstByIndex.has(issue.index)) firstByIndex.set(issue.index, issue);
2367
+ }
2368
+ for (const [index, issue] of [...firstByIndex.entries()].sort((a, b) => a[0] - b[0])) {
2369
+ const row = kept[index];
2370
+ if (!row) continue;
2371
+ logger.warn(
2372
+ `[modelkit] catalog row dropped id=${row.id} reason=${String(issue.path[0] ?? "")} ${issue.message}`
2373
+ );
2374
+ }
2375
+ const dropSet = new Set(firstByIndex.keys());
2376
+ kept = kept.filter((_, index) => !dropSet.has(index));
2377
+ dropped += dropSet.size;
2378
+ }
2379
+ return { rows: kept, dropped };
2380
+ }
2381
+ function toEnvelope(data, logger) {
2382
+ const { rows, dropped } = quarantine(data.rows, logger);
2383
+ if (rows.length === 0) throw new Error("invalid catalog response: no rows");
2384
+ const envelope = { rows, dropped };
2114
2385
  if (data.revision !== void 0) envelope.revision = data.revision;
2115
2386
  return envelope;
2116
2387
  }
2117
- function describeError(err) {
2118
- return err instanceof Error ? err.message : String(err);
2388
+ function describeError(cause) {
2389
+ return cause instanceof Error ? cause.message : String(cause);
2119
2390
  }
2120
2391
  function logPrimed(logger, result) {
2121
2392
  const revision = result.revision === void 0 ? "" : ` revision=${result.revision}`;
@@ -2136,9 +2407,9 @@ async function fetchRemote(source, logger) {
2136
2407
  const json = await res.json();
2137
2408
  const parsed = EnvelopeSchema.safeParse(json);
2138
2409
  if (!parsed.success) throw new Error("invalid catalog response: missing rows");
2139
- const { rows, revision: rev } = toEnvelope(parsed.data);
2140
- if (rev === void 0) return { rows, fromCache: false };
2141
- return { rows, revision: rev, fromCache: false };
2410
+ const { rows, revision: rev, dropped } = toEnvelope(parsed.data, logger);
2411
+ if (rev === void 0) return { rows, fromCache: false, dropped };
2412
+ return { rows, revision: rev, fromCache: false, dropped };
2142
2413
  }
2143
2414
  async function writeCache(source, logger, rows, rev) {
2144
2415
  if (!source.cache) return;
@@ -2163,6 +2434,7 @@ async function prime(source, logger) {
2163
2434
  await writeCache(source, logger, fetched.rows, fetched.revision);
2164
2435
  const result = { origin: liveState.origin, rowCount: rowCount() };
2165
2436
  if (fetched.revision !== void 0) result.revision = fetched.revision;
2437
+ if (fetched.dropped > 0) result.droppedRows = fetched.dropped;
2166
2438
  logPrimed(logger, result);
2167
2439
  return result;
2168
2440
  } catch (err) {
@@ -2173,7 +2445,7 @@ async function prime(source, logger) {
2173
2445
  if (!cached) throw new Error("no cached catalog");
2174
2446
  const cachedParsed = EnvelopeSchema.safeParse(JSON.parse(cached));
2175
2447
  if (!cachedParsed.success) throw new Error("invalid catalog response: missing rows");
2176
- const { rows, revision: rev } = toEnvelope(cachedParsed.data);
2448
+ const { rows, revision: rev, dropped } = toEnvelope(cachedParsed.data, logger);
2177
2449
  liveCatalog.replace(rows);
2178
2450
  liveState.origin = "last-good";
2179
2451
  liveState.revision = rev;
@@ -2183,6 +2455,7 @@ async function prime(source, logger) {
2183
2455
  error
2184
2456
  };
2185
2457
  if (rev !== void 0) result.revision = rev;
2458
+ if (dropped > 0) result.droppedRows = dropped;
2186
2459
  logPrimed(logger, result);
2187
2460
  return result;
2188
2461
  } catch (cacheErr) {
@@ -2630,6 +2903,7 @@ export {
2630
2903
  ADAPTER_IDS,
2631
2904
  Catalog,
2632
2905
  CatalogModelSchema,
2906
+ CatalogModelSchemaBase,
2633
2907
  CatalogSchema,
2634
2908
  ExposureSchema,
2635
2909
  IMAGE_MODELS,