@shop-os/modelkit 0.2.9 → 0.2.11

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,26 @@ header in the release PR.
9
9
 
10
10
  ## [Unreleased]
11
11
 
12
+ ## [0.2.11] - 2026-09-17
13
+
14
+ ### Added
15
+ - Anthropic on Bedrock transport: InvokeModel via `createBedrockAnthropic`, with the same call surface as the direct route. The transport of a row is its `providerOptions.modelkit.transport` (`"bedrock"` | `"direct"`), then the app's `routing.anthropic`, then `"direct"`. Nothing changes for an app until a row or its routing says `"bedrock"`. The row value moves a model with a catalog edit and no deploy, in either direction, for example back to the Anthropic API during a Bedrock outage.
16
+ - Credential check: when the chosen transport has no credentials in the app and the other one has, the kit uses the other one and warns once, so a catalog edit cannot take an app down. An unknown row transport is ignored with a warning. `CatalogModelSchema` (the write schema) rejects an unknown transport; `CatalogSchema` (the read schema) tolerates it, so a value from a newer kit never makes an older kit reject the catalog.
17
+ - Bedrock model ids are derived: `<routing.bedrockProfile>.anthropic.<providerModelId>`, profile default `"us"`. The dated Haiku 4.5 and Sonnet 4.5 ids are built in. A row overrides with `providerOptions.modelkit.bedrockModelId`, and an already-prefixed `providerModelId` passes through unchanged. A new Claude row needs no kit change.
18
+ - `keys.aws` shape: `region` plus `apiKey` (Bedrock bearer token, wins over SigV4), `accessKeyId`/`secretAccessKey`/`sessionToken`, or `credentialProvider` for task roles and other chains. `hasAwsCredentials` reports whether a tier holds usable credentials, and enterprise-to-pro fallback treats `aws` like every other key. `keysFromEnv` reads `BEDROCK_REGION` (default `us-east-1`, never `AWS_REGION`), `AWS_BEARER_TOKEN_BEDROCK`, `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` plus `AWS_SESSION_TOKEN`, under the bare and `ENTERPRISE_` prefixes. An app that has the AWS key pair for S3 uses that IAM user for Bedrock, so it needs `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream` before a row or the routing moves it to Bedrock.
19
+ - Billing guard: the Bedrock model reports the canonical catalog id with provider `anthropic.messages`, so usage events match the direct route. `response.modelId` is pinned to the same id for `generate` and `stream` calls, because Bedrock echoes its own id for dated models (`anthropic.claude-haiku-4-5-20251001-v1:0`). Call-level `providerOptions.anthropic.cacheControl` is dropped with one warning per handle (Bedrock rejects it); message-level cacheControl and every other Anthropic option pass through.
20
+
21
+ Known Bedrock limits for callers: no `web_search`/`web_fetch` server tools, no Files API, no `anthropic-beta` headers. Image/file URLs are downloaded by the AI SDK in the caller's process and sent as base64 (5 MB per image); the request cap is 20 MB.
22
+
23
+ ## [0.2.10] - 2026-09-16
24
+
25
+ ### Added
26
+ - `VideoRequest.taskType` (`reference` | `edit` | `extend`) for Seedance omni-reference subtasks. `edit` sends `duration: -1` and `ratio: adaptive`; `extend` sends `ratio: adaptive`; both need a source or reference video and fail before submit otherwise. `seedance-2.5` also sends `omni_reference_task_type` so BytePlus validates at submit time instead of failing the task later with `InvalidParameter.TaskTypeConstraint`. The 2.0 rows do not list `taskType`.
27
+ - `ReferenceLimits.imagesWithVideo`: a lower image cap when a reference video is sent. `kling-v3-omni-video` declares 7 / 4 per the Replicate schema.
28
+
29
+ ### Fixed
30
+ - Omni usage bills 10 s when the caller sends no duration; the model renders 10 s by default, not 8 (live probe 2026-09-16). The `duration` response_format field is undocumented but honored, so it stays.
31
+
12
32
  ## [0.2.9] - 2026-09-15
13
33
 
14
34
  ### Changed
package/README.md CHANGED
@@ -127,6 +127,67 @@ Opt into thinking per call, for example
127
127
  The kit keeps its `Warning` type for coercions; the SDK's warning type is `AiWarning`.
128
128
  `languageModel()` remains the low-level handle for Mastra and custom middleware; its `providerOptions` carry no kit-internal keys.
129
129
 
130
+ ## Anthropic on Bedrock
131
+
132
+ Anthropic catalog rows can go through AWS Bedrock InvokeModel instead of the
133
+ Anthropic API. The call surface is the same, and usage events and
134
+ `response.modelId` still carry the catalog id.
135
+
136
+ The transport of a row is decided in this order:
137
+
138
+ 1. The row: `providerOptions.modelkit.transport` = `"bedrock"` or `"direct"`. An
139
+ operator edits it in pro-admin and every app follows at its next catalog
140
+ refresh, with no deploy. It works in both directions, so it is also the
141
+ switch back during a Bedrock outage.
142
+ 2. The app: `routing.anthropic`.
143
+ 3. The default: `"direct"`, the Anthropic API.
144
+
145
+ A catalog edit can never take an app down. When the chosen transport has no
146
+ credentials in the app and the other one has, the kit uses the other one and
147
+ logs one warning. Keep both `ANTHROPIC_API_KEY` and the AWS credentials set so
148
+ the switch has somewhere to go.
149
+
150
+ Credentials come from `keys.aws` (`region` plus an `apiKey`, a static pair, or
151
+ a `credentialProvider`), or from the environment through `keysFromEnv`
152
+ (`BEDROCK_REGION`, default `us-east-1`; `AWS_BEARER_TOKEN_BEDROCK`, or
153
+ `AWS_ACCESS_KEY_ID`/`AWS_SECRET_ACCESS_KEY` plus `AWS_SESSION_TOKEN`).
154
+ `keysFromEnv` picks up the AWS key pair an app already has for S3, so that IAM
155
+ user needs `bedrock:InvokeModel` and `bedrock:InvokeModelWithResponseStream`
156
+ before a row or the routing moves it to Bedrock. Bedrock rows never go through
157
+ the Cloudflare gateway.
158
+
159
+ The Bedrock model id is `<routing.bedrockProfile>.anthropic.<providerModelId>`
160
+ (profile default `us`). The older dated models are built in. A model that
161
+ breaks the pattern sets `providerOptions.modelkit.bedrockModelId` on its row.
162
+ See the CHANGELOG entry for the caller-visible Bedrock limits.
163
+
164
+ ```ts
165
+ import { defaultProvider } from "@aws-sdk/credential-provider-node";
166
+ import { createDefaultModelKit, keysFromEnv } from "@shop-os/modelkit";
167
+
168
+ // Task roles and other credential chains. The Anthropic key stays as the fallback.
169
+ const kit = createDefaultModelKit({
170
+ keys: {
171
+ pro: {
172
+ ...keysFromEnv(process.env),
173
+ aws: { region: "us-east-1", credentialProvider: defaultProvider() },
174
+ },
175
+ },
176
+ routing: { anthropic: "bedrock" },
177
+ });
178
+
179
+ // Static keys or a bearer token from the environment:
180
+ // BEDROCK_REGION=us-east-1 plus AWS_BEARER_TOKEN_BEDROCK, or
181
+ // AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (+ AWS_SESSION_TOKEN).
182
+ const envKit = createDefaultModelKit({
183
+ keys: { pro: keysFromEnv(process.env) },
184
+ routing: { anthropic: "bedrock" },
185
+ });
186
+
187
+ // No routing: the Anthropic API, until a catalog row sets transport "bedrock".
188
+ const defaultKit = createDefaultModelKit({ keys: { pro: keysFromEnv(process.env) } });
189
+ ```
190
+
130
191
  ## What the package owns
131
192
 
132
193
  | Concern | Rule |
package/dist/catalog.cjs CHANGED
@@ -1520,6 +1520,7 @@ var VIDEO_MODELS = [
1520
1520
  },
1521
1521
  {
1522
1522
  // generateAudio defaults to false here (trigger-workflows); Space Builder defaults it to true.
1523
+ // omni_reference_task_type edit/extend: adaptive ratio, edit sends duration -1.
1523
1524
  id: "seedance-2.5",
1524
1525
  aliases: ["seedance-2-5", "dreamina-seedance-2-5-260628"],
1525
1526
  label: "Seedance 2.5",
@@ -1534,7 +1535,7 @@ var VIDEO_MODELS = [
1534
1535
  durations: { min: 4, max: 30 },
1535
1536
  references: { images: 30, videos: 10, audios: 10 },
1536
1537
  ratioIgnoredWith: ["firstFrame", "sourceVideo"],
1537
- supports: ["generateAudio", "seed", "lastFrame", "watermark"]
1538
+ supports: ["generateAudio", "seed", "lastFrame", "watermark", "taskType"]
1538
1539
  },
1539
1540
  defaults: {
1540
1541
  durationSeconds: 5,
@@ -1643,6 +1644,9 @@ var VIDEO_MODELS = [
1643
1644
  },
1644
1645
  {
1645
1646
  // mode standard/pro selects 720p/1080p; the endpoint takes no separate resolution.
1647
+ // Replicate: "Max 7 [images] without video, 4 with video"; 4K does not support
1648
+ // reference_video. No `videos` limit yet: the adapter sends no reference video,
1649
+ // so `imagesWithVideo` stays dormant until one is wired through.
1646
1650
  id: "kling-v3-omni-video",
1647
1651
  aliases: ["kwaivgi/kling-v3-omni-video", "kling-v3-omni"],
1648
1652
  label: "Kling V3 Omni",
@@ -1655,7 +1659,7 @@ var VIDEO_MODELS = [
1655
1659
  aspectRatios: [...KLING_RATIOS],
1656
1660
  resolutions: ["720p", "1080p"],
1657
1661
  durations: { min: 3, max: 15 },
1658
- references: { images: 7 },
1662
+ references: { images: 7, imagesWithVideo: 4 },
1659
1663
  supports: ["generateAudio", "lastFrame"]
1660
1664
  },
1661
1665
  defaults: { durationSeconds: 5, aspectRatio: "16:9", generateAudio: false, mode: "pro" },
@@ -1931,6 +1935,7 @@ var SupportedOptionSchema = import_zod.z.enum([
1931
1935
  "upscale",
1932
1936
  "watermark",
1933
1937
  "safetyChecker",
1938
+ "taskType",
1934
1939
  "instrumental"
1935
1940
  ]);
1936
1941
  var UsageUnitSchema = import_zod.z.enum([
@@ -1953,6 +1958,7 @@ var ProviderOptionValueSchema = import_zod.z.lazy(
1953
1958
  var ProviderOptionsSchema = import_zod.z.record(import_zod.z.string(), import_zod.z.record(import_zod.z.string(), ProviderOptionValueSchema));
1954
1959
  var ReferenceLimitsSchema = import_zod.z.looseObject({
1955
1960
  images: import_zod.z.number().optional(),
1961
+ imagesWithVideo: import_zod.z.number().optional(),
1956
1962
  videos: import_zod.z.number().optional(),
1957
1963
  audios: import_zod.z.number().optional(),
1958
1964
  total: import_zod.z.number().optional(),
@@ -2014,6 +2020,7 @@ var DefaultsSchema = import_zod.z.looseObject({
2014
2020
  promptExpansion: import_zod.z.enum(["fast", "balanced", "quality"]).optional(),
2015
2021
  watermark: import_zod.z.boolean().optional(),
2016
2022
  safetyChecker: import_zod.z.boolean().optional(),
2023
+ taskType: import_zod.z.enum(["reference", "edit", "extend"]).optional(),
2017
2024
  mode: import_zod.z.enum(["standard", "pro"]).optional(),
2018
2025
  instrumental: import_zod.z.boolean().optional()
2019
2026
  });
@@ -2080,7 +2087,18 @@ var CatalogModelSchemaBase = import_zod.z.looseObject({
2080
2087
  });
2081
2088
  }
2082
2089
  });
2083
- var CatalogModelSchema = CatalogModelSchemaBase;
2090
+ var CatalogModelSchema = CatalogModelSchemaBase.superRefine(
2091
+ (row, ctx) => {
2092
+ const transport = row.providerOptions?.["modelkit"]?.["transport"];
2093
+ if (transport !== void 0 && transport !== "direct" && transport !== "bedrock") {
2094
+ ctx.addIssue({
2095
+ code: "custom",
2096
+ path: ["providerOptions", "modelkit", "transport"],
2097
+ message: 'transport must be "direct" or "bedrock"'
2098
+ });
2099
+ }
2100
+ }
2101
+ );
2084
2102
  var CatalogSchema = import_zod.z.array(CatalogModelSchemaBase).superRefine((rows, ctx) => {
2085
2103
  const seenIds = /* @__PURE__ */ new Map();
2086
2104
  rows.forEach((row, index) => {
@@ -2550,6 +2568,9 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
2550
2568
  model,
2551
2569
  warnings
2552
2570
  );
2571
+ if (limits.imagesWithVideo !== void 0 && (limits.videos ?? 0) > 0 && videos.length > 0 && images.length > limits.imagesWithVideo) {
2572
+ images = trim("referenceImages", images, limits.imagesWithVideo, model, warnings);
2573
+ }
2553
2574
  if (limits.total !== void 0) {
2554
2575
  let over = images.length + videos.length + audios.length - limits.total;
2555
2576
  if (over > 0) {
@@ -2583,7 +2604,8 @@ function resolveVideoOptions(model, request, onUnsupported = "error") {
2583
2604
  "promptExpansion",
2584
2605
  "lastFrame",
2585
2606
  "watermark",
2586
- "safetyChecker"
2607
+ "safetyChecker",
2608
+ "taskType"
2587
2609
  ]);
2588
2610
  return { request: merged, warnings };
2589
2611
  }