@shop-os/modelkit 0.3.8 → 0.4.1

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,31 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.4.1] - Unreleased
13
+
14
+ ### Changed
15
+ - `veo-3.1`, `veo-3.1-fast`, `veo-3.1-lite` and `gemini-omni-flash` are `deprecated`, replaced by `gemini-omni-1.1-flash`, with no exposure. Google shuts down their provider models (`veo-3.1-generate-preview`, `veo-3.1-fast-generate-preview`, `veo-3.1-lite-generate-preview`, `gemini-omni-flash-preview`) on 2026-10-22. The rows still resolve, so stored ids keep working until then.
16
+ - `gemini-3-pro-image-preview` sends `gemini-3-pro-image` and `gemini-3.1-flash-image-preview` sends `gemini-3.1-flash-image`: Google listed the preview endpoints for shutdown on 2026-06-25, and the GA ids cost the same. The catalog ids are unchanged, since they are pricing keys and stored model ids.
17
+ - `higgsfield-dop-preview` is priced `flat`: Higgsfield bills DoP per generation, a fixed-length clip with no duration knob.
18
+
19
+ ### Live rows
20
+ pro-backend's `apply-model-shutdowns` script (PR #1486) makes the same changes to the live `model_catalog` rows per environment, including clearing the deprecated rows' exposure.
21
+
22
+ ## [0.4.0] - Unreleased
23
+
24
+ ### Added
25
+ - `ModelKitError` code `provider_billing`: the provider account is out of credit or quota. HTTP 402 always maps to it; so does any 4xx whose structured `error.code` / `error.type` (or top-level `code` / `type`) is a billing code such as OpenAI's `insufficient_quota`; free-text matches ("Exhausted balance", "Insufficient credit") count only on 403 and 429, never on a 400/422 whose validation detail may echo the request. It is never retried; before, a 402 was a generic `provider_rejected` and OpenAI's quota 429 was retried as `rate_limited`. Text calls are unchanged (they surface the `ai` SDK's error).
26
+ - `UsageEvent.latencyMs`: the call's wall time from dispatch to the provider's final answer, on every image, video, audio and video-tool event and on single-step text calls (`generateText`/`streamText` with one step, `generateObject`, `streamObject`). A video includes its polling. A multi-step text call (a tool loop) emits its events with **no** `latencyMs`: its wall time includes tool execution and every step, and stamping it on each step's event would inflate per-model p95s and trip the absolute duration alert. `submitVideoTool` stamps `submittedAt` on the submission and `videoToolResult` measures from it; a stored submission without it emits no latency.
27
+
28
+ ### Breaking
29
+ - `ModelKitErrorCode` gains `provider_billing`, so an exhaustive `Record<ModelKitErrorCode, ...>` in a consumer (cowork's `BY_KIT_CODE`) needs an entry, and code that matched a 402 as `provider_rejected` sees the new code. Hence the minor bump: consumers on `^0.3` keep 0.3.x until they move.
30
+
31
+ ### Known gaps
32
+ - Three adapters retry a raw `status === 429` themselves before `classifyHttpError` sees the response: `adapters/higgsfield.ts` (status poll), `adapters/ark.ts` (task poll) and `adapters/google-files.ts` (Files API file-state poll). A quota-exhausted 429 (`insufficient_quota`-style) on those paths is still retried as a rate limit instead of surfacing as `provider_billing`. Follow-up: route those checks through the classifier.
33
+
34
+ ### Consumer follow-up (separate PR, AGENTS.md section 3)
35
+ Map `usage.latencyMs` to the usage-client `latency_ms` field in each app's `onUsage` sink, map `provider_billing` in error categorizers, and log it with the standard `{ err, code: "provider_billing" }` shape on ECS request paths (the Phase 2 CloudWatch metric filter matches on `code`).
36
+
12
37
  ## [0.3.8] - Unreleased
13
38
 
14
39
  ### Added
package/README.md CHANGED
@@ -132,7 +132,7 @@ An optional second `CallContext` argument selects enterprise keys, adds cancella
132
132
  (combined with `abortSignal`), sets media `maxRetries`, and sets `onUnsupported`. Package-level functions read
133
133
  keys from the environment; a configured kit adds routing, catalog overrides, custom
134
134
  fetch, provider endpoint overrides (`endpoints.ark` for the Ark base url), logging, and
135
- the `onUsage` sink.
135
+ the `onUsage` sink. Usage events carry `latencyMs`, the call's wall time from dispatch to the provider's final answer (a video's polling included); a video tool measures from the `submittedAt` the kit stamps on the submission. A multi-step text call (tool loop) carries none, since its wall time is not model latency.
136
136
 
137
137
  ### Durable media execution
138
138
 
@@ -266,7 +266,7 @@ const defaultKit = createDefaultModelKit({ keys: { pro: keysFromEnv(process.env)
266
266
  | Keys | Enterprise keys win only when every key the adapter (and its route, e.g. the cloudflare token for a vendor that really goes through the gateway) needs is set on the enterprise tier; a fallback to pro keys is logged once per call. |
267
267
  | Routing | Cloudflare AI Gateway for text only. Never for Gemini images or Omni. fal passthrough for geo-blocked regions; the fal direct route submits to the queue and polls, so `job.result({ timeoutMs, signal })` is live. |
268
268
  | Retry | One classifier, one policy. Never on content blocks, aborts, or 4xx except 429; SDKs with their own retry (Replicate) run under a single kit attempt; a 429 on a status poll keeps polling unless `CallContext.maxRetries` is `0`. See durable media execution above. |
269
- | Errors | `ModelKitError` with `code`, `provider`, `status`, `body`. Gemini `blockReason` and fal `body.detail` are lifted into `body`. A throw with no HTTP status (fetch failed, reset, DNS) is a retryable `network` error on every adapter. |
269
+ | Errors | `ModelKitError` with `code`, `provider`, `status`, `body`. Gemini `blockReason` and fal `body.detail` are lifted into `body`. A throw with no HTTP status (fetch failed, reset, DNS) is a retryable `network` error on every adapter. An out-of-credit provider account is `provider_billing` and is never retried: HTTP 402, a 4xx whose structured error code is a billing code (`insufficient_quota`, ...), or a 403/429 whose text names the exhausted account ("Exhausted balance", "Insufficient credit"). Text calls go through `ai` and surface the SDK's own error instead. |
270
270
  | Usage | One event per call with the canonical model id and the row's `usage.unit` (adapters supply the quantity in that unit). Gemini image events carry the token counts too (`inputTokens`/`outputTokens`); Google's raw `usageMetadata` rides on `raw.providerMetadata.google`. Video usage is emitted on completion; Seedance keeps `usage`, `duration`, `framespersecond` and `resolution` from the poll on `raw`. Audio: Lyria 3 returns no duration, so it emits `unit: "images", quantity: 1` (token counts ride along as `inputTokens`/`outputTokens`); Replicate audio (Lyria 2, MMAudio) emits `unit: "compute_seconds"` with the prediction time. |
271
271
  | Media parts | `prepareMediaPart` turns a `MediaRef` into an AI SDK file part. Google inlines bytes up to 20 MB and uploads larger payloads through the Files API; call the returned `cleanup` after the request. Other providers get the reference inline. |
272
272
 
package/dist/catalog.cjs CHANGED
@@ -275,35 +275,43 @@ var IMAGE_MODELS = [
275
275
  GEMINI_PROMPT_MAX_CHARS,
276
276
  NANO_BANANA_2_1_RATIOS
277
277
  ),
278
- geminiImage(
279
- "gemini-3-pro-image-preview",
280
- "Nano Banana Pro",
281
- ["nb-pro"],
282
- ["1K", "2K", "4K"],
283
- {
284
- provider: "google",
285
- description: "Create stunning, production-quality visuals with exceptional realism, detail, and typography. Best for campaigns, e-commerce, product photography, and hero creatives.",
286
- logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
287
- },
288
- ["cowork", "space_builder"],
289
- { images: 14 },
290
- GEMINI_PROMPT_MAX_CHARS
291
- ),
292
- geminiImage(
293
- "gemini-3.1-flash-image-preview",
294
- "Nano Banana 2",
295
- ["nb-2"],
296
- // Flash 3.1 is the only Gemini image model with the 512 tier.
297
- ["512", "1K", "2K", "4K"],
298
- {
299
- provider: "google",
300
- description: "Generate high-quality images at speed. Perfect for brainstorming, concept boards, concept testing, creative exploration, and large batch workflows.",
301
- logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
302
- },
303
- ["cowork", "space_builder"],
304
- { images: 14 },
305
- GEMINI_PROMPT_MAX_CHARS
306
- ),
278
+ // Google retired the -preview endpoints for the GA ids; the catalog ids stay because they
279
+ // are pricing keys and stored model ids.
280
+ {
281
+ ...geminiImage(
282
+ "gemini-3-pro-image-preview",
283
+ "Nano Banana Pro",
284
+ ["nb-pro"],
285
+ ["1K", "2K", "4K"],
286
+ {
287
+ provider: "google",
288
+ description: "Create stunning, production-quality visuals with exceptional realism, detail, and typography. Best for campaigns, e-commerce, product photography, and hero creatives.",
289
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
290
+ },
291
+ ["cowork", "space_builder"],
292
+ { images: 14 },
293
+ GEMINI_PROMPT_MAX_CHARS
294
+ ),
295
+ providerModelId: "gemini-3-pro-image"
296
+ },
297
+ {
298
+ ...geminiImage(
299
+ "gemini-3.1-flash-image-preview",
300
+ "Nano Banana 2",
301
+ ["nb-2"],
302
+ // Flash 3.1 is the only Gemini image model with the 512 tier.
303
+ ["512", "1K", "2K", "4K"],
304
+ {
305
+ provider: "google",
306
+ description: "Generate high-quality images at speed. Perfect for brainstorming, concept boards, concept testing, creative exploration, and large batch workflows.",
307
+ logo: "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png"
308
+ },
309
+ ["cowork", "space_builder"],
310
+ { images: 14 },
311
+ GEMINI_PROMPT_MAX_CHARS
312
+ ),
313
+ providerModelId: "gemini-3.1-flash-image"
314
+ },
307
315
  geminiImage(
308
316
  "gemini-3.1-flash-lite-image",
309
317
  "Nano Banana Lite",
@@ -1437,6 +1445,7 @@ var OMNI_PROMPT = { referenceSyntax: "caption", maxChars: 5e4 };
1437
1445
  var BYTEDANCE_LOGO = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/3bba5d81-4e57-4e71-911b-cdfa05acca2c_compressed.png";
1438
1446
  var GOOGLE_LOGO2 = "https://d31vvfpk2ppdyq.cloudfront.net/assets/image/192485d2-a76a-4657-9c06-930b2c70badd_compressed.png";
1439
1447
  var VIDEO_MODELS = [
1448
+ // Google shuts down the Veo 3.1 previews and Omni Flash 1.0 on 2026-10-22.
1440
1449
  {
1441
1450
  id: "veo-3.1",
1442
1451
  aliases: [
@@ -1449,8 +1458,9 @@ var VIDEO_MODELS = [
1449
1458
  type: "video",
1450
1459
  adapter: "google-genai",
1451
1460
  providerModelId: "veo-3.1-generate-preview",
1452
- lifecycle: "active",
1453
- exposure: ["cowork", "space_builder"],
1461
+ lifecycle: "deprecated",
1462
+ replacedBy: "gemini-omni-1.1-flash",
1463
+ exposure: [],
1454
1464
  constraints: {
1455
1465
  aspectRatios: [...OMNI_RATIOS],
1456
1466
  resolutions: ["720p", "1080p", "4k"],
@@ -1485,7 +1495,8 @@ var VIDEO_MODELS = [
1485
1495
  type: "video",
1486
1496
  adapter: "google-genai",
1487
1497
  providerModelId: "veo-3.1-fast-generate-preview",
1488
- lifecycle: "active",
1498
+ lifecycle: "deprecated",
1499
+ replacedBy: "gemini-omni-1.1-flash",
1489
1500
  exposure: [],
1490
1501
  constraints: {
1491
1502
  aspectRatios: [...OMNI_RATIOS],
@@ -1515,7 +1526,8 @@ var VIDEO_MODELS = [
1515
1526
  type: "video",
1516
1527
  adapter: "google-genai",
1517
1528
  providerModelId: "veo-3.1-lite-generate-preview",
1518
- lifecycle: "active",
1529
+ lifecycle: "deprecated",
1530
+ replacedBy: "gemini-omni-1.1-flash",
1519
1531
  exposure: [],
1520
1532
  constraints: {
1521
1533
  aspectRatios: [...OMNI_RATIOS],
@@ -1546,8 +1558,9 @@ var VIDEO_MODELS = [
1546
1558
  type: "video",
1547
1559
  adapter: "google-genai",
1548
1560
  providerModelId: "gemini-omni-flash-preview",
1549
- lifecycle: "active",
1550
- exposure: ["cowork", "space_builder"],
1561
+ lifecycle: "deprecated",
1562
+ replacedBy: "gemini-omni-1.1-flash",
1563
+ exposure: [],
1551
1564
  constraints: {
1552
1565
  // 1.0 rejects `resolution`; it renders 720p. No defaults: the adapter
1553
1566
  // sends ratio and duration only when the caller set them, so a
@@ -2102,7 +2115,8 @@ var VIDEO_MODELS = [
2102
2115
  },
2103
2116
  defaults: {},
2104
2117
  usage: { ...VIDEO_USAGE },
2105
- pricingKind: "video_per_second",
2118
+ // Higgsfield bills DoP per generation: a fixed-length clip with no duration knob.
2119
+ pricingKind: "flat",
2106
2120
  display: {
2107
2121
  provider: "higgsfield",
2108
2122
  description: "Higgsfield DOP image-to-video model; animates a required first-frame image from a text prompt with an optional last-frame image and motion controls."