@shop-os/modelkit 0.4.5 → 0.4.7

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,22 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.4.7] - Unreleased
13
+
14
+ ### Fixed
15
+ - Video usage events report the resolution the provider renders, not only the one the caller asked for. Pricing looks a video up by `(model, resolution)` and takes the cheapest row when the event has none, so these clips were under-billed:
16
+ - Seedance (ark) reports the resolution BytePlus returns in the poll, else the one it sends (`720p` when the request has none). A resumed job whose request lost the submitted resolution bills what was rendered.
17
+ - Gemini Omni 1.1 reports the resolution it sends, else Google's documented `720p` default. Omni 1.0 always reports `720p`: it rejects `resolution` and renders 720p, so a requested value never reached the provider.
18
+ - Kling V3 Omni (replicate) reports the resolution of the mode it sends: `standard` is `720p`, `pro` is `1080p`, `4k` is `4k`. The row defaults to `pro`, so a request with no resolution renders and now bills 1080p.
19
+ - Kling V3 (`kwaivgi/kling-v3-video`, replicate) uses the same tiers. It now also sends the mode that matches a requested resolution; before, it ignored the resolution and sent `standard`.
20
+ - Veo reports Google's documented `720p` default when the request has none.
21
+ - MiniMax H3 (`2k`), H3 Max (`768p`) and Wan 3.0 (`1080p`) on fal, and Hailuo 02 (`768p`) on replicate, report the default their payload sends. The payload and the usage event now read one helper per model, so the two cannot drift.
22
+
23
+ ## [0.4.6] - Unreleased
24
+
25
+ ### Added
26
+ - `ModelKit.resumeVideo(req, jobId)` and `Adapter.resumeVideo` (ark/Seedance only): reconnect to a video job `generateVideo` submitted earlier, by its `jobId`, without submitting a new job. Other adapters throw `unsupported_option`.
27
+
12
28
  ## [0.4.5] - Unreleased
13
29
 
14
30
  ### Fixed
package/README.md CHANGED
@@ -51,6 +51,10 @@ await kit.generateText({
51
51
  await clip.cleanup?.();
52
52
  ```
53
53
 
54
+ A video job that outlives its caller can be rejoined with `resumeVideo`: pass the same
55
+ request and the `jobId` from `generateVideo` and await `job.result()` as usual. Only the
56
+ ark adapter (Seedance) supports it today; every other adapter throws `unsupported_option`.
57
+
54
58
  ### Video tools
55
59
 
56
60
  `video-tool` rows (Topaz upscale, frame interpolation, SDR to HDR on fal) take a clip and
@@ -268,7 +272,7 @@ const defaultKit = createDefaultModelKit({ keys: { pro: keysFromEnv(process.env)
268
272
  | 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. |
269
273
  | 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. |
270
274
  | 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. |
271
- | 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. |
275
+ | 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 and carries the resolution the provider renders (the default it falls back to when the request has none), because pricing is keyed by it; 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. |
272
276
  | 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. |
273
277
 
274
278
  ## Add a model
package/dist/catalog.cjs CHANGED
@@ -37,6 +37,7 @@ __export(catalog_entry_exports, {
37
37
  TEXT_MODELS: () => TEXT_MODELS,
38
38
  VIDEO_MODELS: () => VIDEO_MODELS,
39
39
  VIDEO_TOOL_MODELS: () => VIDEO_TOOL_MODELS,
40
+ VideoResolutionSchema: () => VideoResolutionSchema,
40
41
  billedAudio: () => billedAudio,
41
42
  compareCatalogOrder: () => compareCatalogOrder,
42
43
  isListed: () => isListed,