@shop-os/modelkit 0.2.2 → 0.2.3

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/CHANGELOG.md CHANGED
@@ -9,6 +9,14 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.2.3] - 2026-09-15
13
+
14
+ ### Changed
15
+ - `display.sortOrder` sorts descending: a higher number shows first, so a new model takes the next number up. No published row set it before.
16
+
17
+ ### Fixed
18
+ - `primeCatalog` treats a remote response with zero rows as a failure and keeps the last-good or snapshot rows, so a pro-backend whose catalog table is not seeded yet cannot empty every consumer's pickers.
19
+
12
20
  ## [0.2.2] - 2026-09-15
13
21
 
14
22
  Option audit against provider docs, limited to options a product surface sets.
package/dist/catalog.cjs CHANGED
@@ -164,6 +164,12 @@ var GEMINI_RATIOS = [
164
164
  "adaptive"
165
165
  ];
166
166
  function geminiImage(id, label, aliases, sizes, display, exposure, references) {
167
+ const constraints = {
168
+ aspectRatios: [...GEMINI_RATIOS],
169
+ sizes,
170
+ supports: ["safetySettings", "temperature"]
171
+ };
172
+ if (references !== void 0) constraints.references = references;
167
173
  const model = {
168
174
  id,
169
175
  aliases,
@@ -173,12 +179,7 @@ function geminiImage(id, label, aliases, sizes, display, exposure, references) {
173
179
  providerModelId: id,
174
180
  lifecycle: "active",
175
181
  exposure,
176
- constraints: {
177
- aspectRatios: [...GEMINI_RATIOS],
178
- sizes,
179
- supports: ["safetySettings", "temperature"],
180
- ...references ? { references } : {}
181
- },
182
+ constraints,
182
183
  defaults: { size: "1K", aspectRatio: "1:1" },
183
184
  usage: { ...IMAGE_USAGE, slug: "gemini" },
184
185
  pricingKind: "image_size",
@@ -305,6 +306,7 @@ var IMAGE_MODELS = [
305
306
  pricingKind: "image_size",
306
307
  display: {
307
308
  provider: "openai",
309
+ sortOrder: 20,
308
310
  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."
309
311
  }
310
312
  },
@@ -339,6 +341,7 @@ var IMAGE_MODELS = [
339
341
  pricingKind: "image_size",
340
342
  display: {
341
343
  provider: "openai",
344
+ sortOrder: 10,
342
345
  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."
343
346
  }
344
347
  },
@@ -2079,23 +2082,22 @@ var liveState = store[KEY] ??= {
2079
2082
  var liveCatalog = liveState.catalog;
2080
2083
 
2081
2084
  // src/catalog/load.ts
2085
+ var import_zod2 = require("zod");
2082
2086
  function rowCount() {
2083
2087
  return liveCatalog.list({ lifecycle: ["active", "deprecated", "retired"] }).length;
2084
2088
  }
2085
2089
  function authHeaders(auth) {
2086
2090
  return "bearer" in auth ? { Authorization: `Bearer ${auth.bearer}` } : { "x-api-key": auth.apiKey };
2087
2091
  }
2088
- function describeError(err) {
2089
- return err instanceof Error ? err.message : String(err);
2090
- }
2091
- function parseEnvelope(json) {
2092
- if (typeof json !== "object" || json === null || !("rows" in json)) {
2093
- throw new Error("invalid catalog response: missing rows");
2094
- }
2095
- const rows = CatalogSchema.parse(json.rows);
2096
- const rev = json.revision;
2097
- if (typeof rev === "string") return { rows, revision: rev };
2098
- return { rows };
2092
+ var EnvelopeSchema = import_zod2.z.object({
2093
+ rows: CatalogSchema,
2094
+ revision: import_zod2.z.string().optional()
2095
+ });
2096
+ function toEnvelope(data) {
2097
+ if (data.rows.length === 0) throw new Error("invalid catalog response: no rows");
2098
+ const envelope = { rows: data.rows };
2099
+ if (data.revision !== void 0) envelope.revision = data.revision;
2100
+ return envelope;
2099
2101
  }
2100
2102
  function logPrimed(logger, result) {
2101
2103
  const { origin: loggedOrigin, rowCount: loggedCount, revision: loggedRevision } = result;
@@ -2116,7 +2118,9 @@ async function fetchRemote(source, logger) {
2116
2118
  if (res.status === 304) return { kept: true };
2117
2119
  if (!res.ok) throw new Error(`catalog fetch failed with status ${res.status}`);
2118
2120
  const json = await res.json();
2119
- const { rows, revision: rev } = parseEnvelope(json);
2121
+ const parsed = EnvelopeSchema.safeParse(json);
2122
+ if (!parsed.success) throw new Error("invalid catalog response: missing rows");
2123
+ const { rows, revision: rev } = toEnvelope(parsed.data);
2120
2124
  if (rev === void 0) return { rows, fromCache: false };
2121
2125
  return { rows, revision: rev, fromCache: false };
2122
2126
  }
@@ -2147,11 +2151,13 @@ async function prime(source, logger) {
2147
2151
  return result;
2148
2152
  } catch (err) {
2149
2153
  logger.warn("[modelkit] catalog remote failed, trying cache", err);
2150
- const error = describeError(err);
2154
+ const error = err instanceof Error ? err.message : String(err);
2151
2155
  try {
2152
2156
  const cached = await source.cache?.get();
2153
2157
  if (!cached) throw new Error("no cached catalog");
2154
- const { rows, revision: rev } = parseEnvelope(JSON.parse(cached));
2158
+ const cachedParsed = EnvelopeSchema.safeParse(JSON.parse(cached));
2159
+ if (!cachedParsed.success) throw new Error("invalid catalog response: missing rows");
2160
+ const { rows, revision: rev } = toEnvelope(cachedParsed.data);
2155
2161
  liveCatalog.replace(rows);
2156
2162
  liveState.origin = "last-good";
2157
2163
  liveState.revision = rev;
@@ -2191,19 +2197,18 @@ async function refresh(source, logger) {
2191
2197
  }
2192
2198
  }
2193
2199
  function primeCatalog(source) {
2194
- const memo = liveState.memo;
2195
- if (memo) return memo;
2200
+ if (liveState.memo) return liveState.memo;
2196
2201
  const logger = source.logger ?? console;
2197
- liveState.memo = prime(source, logger).then((result) => {
2202
+ const pending = prime(source, logger).then((result) => {
2198
2203
  const ttlMs = source.ttlMs ?? 3e5;
2199
2204
  if (ttlMs > 0 && liveState.timer === void 0) {
2200
2205
  liveState.timer = setInterval(() => void refresh(source, logger), ttlMs);
2201
- const maybeUnref = liveState.timer;
2202
- maybeUnref.unref?.();
2206
+ liveState.timer.unref?.();
2203
2207
  }
2204
2208
  return result;
2205
2209
  });
2206
- return liveState.memo;
2210
+ liveState.memo = pending;
2211
+ return pending;
2207
2212
  }
2208
2213
  function resetLiveCatalog() {
2209
2214
  if (liveState.timer !== void 0) {