@shop-os/modelkit 0.1.3 → 0.1.4
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/README.md +17 -17
- package/dist/index.cjs +91 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +6 -2
- package/dist/index.d.ts +6 -2
- package/dist/index.js +91 -23
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -89,13 +89,13 @@ cannot express (image `quality`/`outputFormat`, video `sourceVideo`, tiered
|
|
|
89
89
|
`resolution`, ...) is dropped with an `option_ignored` warning, never silently. Use it
|
|
90
90
|
for a model the catalog does not carry yet; the next step is a catalog row.
|
|
91
91
|
|
|
92
|
-
| `ai` function | Here
|
|
93
|
-
| ----------------------------------------------------------------------------------------------- |
|
|
94
|
-
| `generateText`, `streamText` | Same. Catalog `providerOptions` are merged under the caller's; `prepareStep` model switches resolve through the catalog; usage per step.
|
|
95
|
-
| `generateObject`, `streamObject` | Same, including `output: "array" \| "enum" \| "no-schema"`.
|
|
96
|
-
| `generateImage` | Takes `n`, `size: "WxH"`, `aspectRatio`, `seed`, `abortSignal`, `providerOptions` plus the kit's `references`, `size: "2K"`, `quality`, `outputFormat
|
|
97
|
-
| `experimental_generateVideo` | The kit's `generateVideo` returns a job; `duration` is an alias of `durationSeconds`. An `ai` video model instance is wrapped as a settled job. The `ai` function is also re-exported as-is.
|
|
98
|
-
| `experimental_generateSpeech`, `transcribe`, `embed`, `embedMany`, `rerank`, agents, UI streams | Re-exported from `ai` unchanged; the catalog has no rows for them.
|
|
92
|
+
| `ai` function | Here |
|
|
93
|
+
| ----------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
94
|
+
| `generateText`, `streamText` | Same. Catalog `providerOptions` are merged under the caller's; `prepareStep` model switches resolve through the catalog; usage per step. |
|
|
95
|
+
| `generateObject`, `streamObject` | Same, including `output: "array" \| "enum" \| "no-schema"`. |
|
|
96
|
+
| `generateImage` | Takes `n`, `size: "WxH"`, `aspectRatio`, `seed`, `abortSignal`, `providerOptions` plus the kit's `references`, `size: "2K"`, `quality`, `outputFormat`, `scaleFactor` (upscalers). Returns `image` and `images` with `uint8Array`, `base64`, `mediaType` next to `bytes`, `mime`; `outputUrl` when the provider hands back a URL. |
|
|
97
|
+
| `experimental_generateVideo` | The kit's `generateVideo` returns a job; `duration` is an alias of `durationSeconds`. An `ai` video model instance is wrapped as a settled job. The `ai` function is also re-exported as-is. |
|
|
98
|
+
| `experimental_generateSpeech`, `transcribe`, `embed`, `embedMany`, `rerank`, agents, UI streams | Re-exported from `ai` unchanged; the catalog has no rows for them. |
|
|
99
99
|
|
|
100
100
|
An optional second `CallContext` argument selects enterprise keys, adds cancellation
|
|
101
101
|
(combined with `abortSignal`), and sets `onUnsupported`. Package-level functions read
|
|
@@ -117,16 +117,16 @@ The kit keeps its `Warning` type for coercions; the SDK's warning type is `AiWar
|
|
|
117
117
|
|
|
118
118
|
## What the package owns
|
|
119
119
|
|
|
120
|
-
| Concern | Rule
|
|
121
|
-
| ----------- |
|
|
122
|
-
| Ids | `id` is the pro-backend pricing key. Every historical id (`nb-pro`, `veo-3.1-generate-001`, `kling-v2.5-turbo-pro`) is an alias.
|
|
123
|
-
| Options | `resolveOptions` fills catalog defaults, coerces aspect ratio to the nearest supported one, errors on unsupported size or resolution (`onUnsupported: "coerce"` snaps down instead), clamps duration, trims references (audio first, then video), and drops options the model ignores with a warning.
|
|
124
|
-
| Keys | Enterprise keys win only when every key the adapter (and its route, e.g. the cloudflare token for gateway-routed text) needs is set on the enterprise tier.
|
|
125
|
-
| Routing | Cloudflare AI Gateway for text only. Never for Gemini images or Omni. fal passthrough for geo-blocked regions. OpenRouter optional for text.
|
|
126
|
-
| Retry | One classifier. Never on content blocks or 4xx except 429.
|
|
127
|
-
| Errors | `ModelKitError` with `code`, `provider`, `status`, `body`. Gemini `blockReason` and fal `body.detail` are lifted into `body`.
|
|
128
|
-
| Usage | One event per call with the canonical model id. Video usage is emitted on completion. 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. |
|
|
129
|
-
| 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.
|
|
120
|
+
| Concern | Rule |
|
|
121
|
+
| ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
122
|
+
| Ids | `id` is the pro-backend pricing key. Every historical id (`nb-pro`, `veo-3.1-generate-001`, `kling-v2.5-turbo-pro`) is an alias. |
|
|
123
|
+
| Options | Image tiers are `512` (Gemini 3.1 Flash only), `1K`, `2K`, `4K`. `resolveOptions` fills catalog defaults, coerces aspect ratio to the nearest supported one, errors on unsupported size or resolution (`onUnsupported: "coerce"` snaps down instead), clamps duration, trims references (audio first, then video), and drops options the model ignores with a warning. |
|
|
124
|
+
| Keys | Enterprise keys win only when every key the adapter (and its route, e.g. the cloudflare token for gateway-routed text) needs is set on the enterprise tier. |
|
|
125
|
+
| Routing | Cloudflare AI Gateway for text only. Never for Gemini images or Omni. fal passthrough for geo-blocked regions. OpenRouter optional for text. |
|
|
126
|
+
| Retry | One classifier. Never on content blocks or 4xx except 429. |
|
|
127
|
+
| Errors | `ModelKitError` with `code`, `provider`, `status`, `body`. Gemini `blockReason` and fal `body.detail` are lifted into `body`. |
|
|
128
|
+
| Usage | One event per call with the canonical model id. 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. 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. |
|
|
129
|
+
| 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. |
|
|
130
130
|
|
|
131
131
|
## Add a model
|
|
132
132
|
|
package/dist/index.cjs
CHANGED
|
@@ -165,7 +165,18 @@ var Catalog = class _Catalog {
|
|
|
165
165
|
|
|
166
166
|
// src/catalog/image.ts
|
|
167
167
|
var IMAGE_USAGE = { unit: "images", modality: "IMAGE" };
|
|
168
|
-
var GEMINI_RATIOS = [
|
|
168
|
+
var GEMINI_RATIOS = [
|
|
169
|
+
"1:1",
|
|
170
|
+
"2:3",
|
|
171
|
+
"3:2",
|
|
172
|
+
"16:9",
|
|
173
|
+
"9:16",
|
|
174
|
+
"4:3",
|
|
175
|
+
"3:4",
|
|
176
|
+
"21:9",
|
|
177
|
+
"4:5",
|
|
178
|
+
"5:4"
|
|
179
|
+
];
|
|
169
180
|
function geminiImage(id, label, aliases, sizes) {
|
|
170
181
|
const model = {
|
|
171
182
|
id,
|
|
@@ -188,7 +199,8 @@ var IMAGE_MODELS = [
|
|
|
188
199
|
"gemini-3.1-flash-image-preview",
|
|
189
200
|
"Nano Banana Flash",
|
|
190
201
|
["nb-2"],
|
|
191
|
-
|
|
202
|
+
// Flash 3.1 is the only Gemini image model with the 512 tier.
|
|
203
|
+
["512", "1K", "2K", "4K"]
|
|
192
204
|
),
|
|
193
205
|
geminiImage("gemini-3.1-flash-lite-image", "Nano Banana Lite", ["nb-lite"], ["1K"]),
|
|
194
206
|
// trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
|
|
@@ -533,8 +545,12 @@ var OTHER_MODELS = [
|
|
|
533
545
|
adapter: "replicate",
|
|
534
546
|
providerModelId: "topazlabs/image-upscale",
|
|
535
547
|
lifecycle: "active",
|
|
536
|
-
constraints: {
|
|
537
|
-
|
|
548
|
+
constraints: {
|
|
549
|
+
references: { images: 1 },
|
|
550
|
+
requires: ["references"],
|
|
551
|
+
supports: ["scaleFactor", "outputFormat"]
|
|
552
|
+
},
|
|
553
|
+
defaults: { scaleFactor: 2, outputFormat: "png" },
|
|
538
554
|
usage: { unit: "compute_seconds", modality: "IMAGE" },
|
|
539
555
|
pricingKind: "flat"
|
|
540
556
|
},
|
|
@@ -590,7 +606,7 @@ var TEXT_MODELS = [
|
|
|
590
606
|
text("gpt-5.6-sol", "GPT-5.6 Sol", "gpt-5.6-sol"),
|
|
591
607
|
// Library key "claude-sonnet-4.5" maps to the SDK id "claude-sonnet-4-5".
|
|
592
608
|
text("claude-sonnet-4-5", "Claude Sonnet 4.5", "claude-sonnet-4-5", {
|
|
593
|
-
aliases: ["claude-sonnet-4.5"]
|
|
609
|
+
aliases: ["claude-sonnet-4.5", "claude-sonnet-4-5-20250929"]
|
|
594
610
|
}),
|
|
595
611
|
// Anthropic retired the dated 4.0 ids; callers that still send them get 4.6.
|
|
596
612
|
text("claude-sonnet-4-6", "Claude Sonnet 4.6", "claude-sonnet-4-6", {
|
|
@@ -606,7 +622,7 @@ var TEXT_MODELS = [
|
|
|
606
622
|
aliases: ["claude-opus-4.8", "claude-opus-4", "claude-opus-4.0", "claude-opus-4-20250514"]
|
|
607
623
|
}),
|
|
608
624
|
text("claude-haiku-4-5", "Claude Haiku 4.5", "claude-haiku-4-5", {
|
|
609
|
-
aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001"]
|
|
625
|
+
aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001", "claude-haiku-4"]
|
|
610
626
|
}),
|
|
611
627
|
text("claude-opus-5", "Claude Opus 5", "claude-opus-5", {
|
|
612
628
|
aliases: ["claude-opus-5.0"]
|
|
@@ -1089,7 +1105,7 @@ var MODELS = [
|
|
|
1089
1105
|
];
|
|
1090
1106
|
|
|
1091
1107
|
// src/resolve-options.ts
|
|
1092
|
-
var SIZE_ORDER = ["1K", "2K", "4K"];
|
|
1108
|
+
var SIZE_ORDER = ["512", "1K", "2K", "4K"];
|
|
1093
1109
|
var RESOLUTION_ORDER = ["360p", "480p", "720p", "768p", "1080p", "2k", "4k"];
|
|
1094
1110
|
function ratioValue(r) {
|
|
1095
1111
|
if (r === "adaptive") return 1;
|
|
@@ -1221,7 +1237,13 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
|
|
|
1221
1237
|
model: model.id
|
|
1222
1238
|
});
|
|
1223
1239
|
}
|
|
1224
|
-
dropUnsupported(merged, model, warnings, [
|
|
1240
|
+
dropUnsupported(merged, model, warnings, [
|
|
1241
|
+
"seed",
|
|
1242
|
+
"quality",
|
|
1243
|
+
"count",
|
|
1244
|
+
"outputFormat",
|
|
1245
|
+
"scaleFactor"
|
|
1246
|
+
]);
|
|
1225
1247
|
return { request: merged, warnings };
|
|
1226
1248
|
}
|
|
1227
1249
|
function resolveVideoOptions(model, request, onUnsupported = "error") {
|
|
@@ -1995,6 +2017,7 @@ function createModelKit(config, adapters) {
|
|
|
1995
2017
|
providerOptions,
|
|
1996
2018
|
references,
|
|
1997
2019
|
exactSize: exactSizeField,
|
|
2020
|
+
scaleFactor,
|
|
1998
2021
|
quality: _quality,
|
|
1999
2022
|
outputFormat: _outputFormat
|
|
2000
2023
|
} = req;
|
|
@@ -2020,6 +2043,8 @@ function createModelKit(config, adapters) {
|
|
|
2020
2043
|
if (_quality !== void 0) pushIgnored(warnings, "quality", _quality, model.modelId);
|
|
2021
2044
|
if (_outputFormat !== void 0)
|
|
2022
2045
|
pushIgnored(warnings, "outputFormat", _outputFormat, model.modelId);
|
|
2046
|
+
if (scaleFactor !== void 0)
|
|
2047
|
+
pushIgnored(warnings, "scaleFactor", scaleFactor, model.modelId);
|
|
2023
2048
|
const abortSignal = combineSignals(req.abortSignal, call.signal);
|
|
2024
2049
|
if (abortSignal) options.abortSignal = abortSignal;
|
|
2025
2050
|
const result = await (0, import_ai2.generateImage)(options);
|
|
@@ -4077,6 +4102,10 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
|
|
|
4077
4102
|
imageQuality: "auto"
|
|
4078
4103
|
};
|
|
4079
4104
|
if (req.size !== void 0) usage2.imageSize = req.size;
|
|
4105
|
+
if (result.usage?.inputTokens !== void 0)
|
|
4106
|
+
usage2.inputTokens = result.usage.inputTokens;
|
|
4107
|
+
if (result.usage?.outputTokens !== void 0)
|
|
4108
|
+
usage2.outputTokens = result.usage.outputTokens;
|
|
4080
4109
|
return { images, usage: usage2, raw: result };
|
|
4081
4110
|
}
|
|
4082
4111
|
};
|
|
@@ -4608,7 +4637,7 @@ function aspectRatioToSize(ratio, tier) {
|
|
|
4608
4637
|
};
|
|
4609
4638
|
const MIN_PIXELS = 655360;
|
|
4610
4639
|
const MAX_PIXELS = 8294400;
|
|
4611
|
-
const maxLong = TIER_LONG_EDGE[tier];
|
|
4640
|
+
const maxLong = TIER_LONG_EDGE[tier] ?? 1024;
|
|
4612
4641
|
const RATIO_MAP = /* @__PURE__ */ new Map([
|
|
4613
4642
|
["1:1", [1, 1]],
|
|
4614
4643
|
["16:9", [16, 9]],
|
|
@@ -5242,6 +5271,9 @@ function isUpscaler(providerModelId) {
|
|
|
5242
5271
|
const id = isLower(providerModelId);
|
|
5243
5272
|
return id.includes("upscal") || id.includes("crystal") || id.includes("topaz");
|
|
5244
5273
|
}
|
|
5274
|
+
function isTopazUpscaler(providerModelId) {
|
|
5275
|
+
return isLower(providerModelId).includes("topazlabs/image-upscale");
|
|
5276
|
+
}
|
|
5245
5277
|
function isQwen(providerModelId) {
|
|
5246
5278
|
return isLower(providerModelId).includes("qwen");
|
|
5247
5279
|
}
|
|
@@ -5291,8 +5323,36 @@ function wanImageSize(aspectRatio, size) {
|
|
|
5291
5323
|
}
|
|
5292
5324
|
async function generateReplicateImage(req, ctx, deps) {
|
|
5293
5325
|
const id = ctx.model.providerModelId;
|
|
5294
|
-
|
|
5295
|
-
if (
|
|
5326
|
+
let input;
|
|
5327
|
+
if (isTopazUpscaler(id)) {
|
|
5328
|
+
const first = (req.references ?? [])[0];
|
|
5329
|
+
if (!first) {
|
|
5330
|
+
throw new ModelKitError({
|
|
5331
|
+
code: "missing_input",
|
|
5332
|
+
message: `${ctx.model.id} requires a reference image to upscale`,
|
|
5333
|
+
provider: "replicate",
|
|
5334
|
+
model: ctx.model.id,
|
|
5335
|
+
retryable: false
|
|
5336
|
+
});
|
|
5337
|
+
}
|
|
5338
|
+
let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
|
|
5339
|
+
if (format !== "png" && format !== "jpg") {
|
|
5340
|
+
ctx.warnings.push({
|
|
5341
|
+
code: "option_ignored",
|
|
5342
|
+
field: "outputFormat",
|
|
5343
|
+
from: format,
|
|
5344
|
+
message: "topazlabs/image-upscale supports only png and jpg; using png"
|
|
5345
|
+
});
|
|
5346
|
+
format = "png";
|
|
5347
|
+
}
|
|
5348
|
+
input = {
|
|
5349
|
+
image: refToUrl3(first),
|
|
5350
|
+
enhance_model: "Standard V2",
|
|
5351
|
+
output_format: format,
|
|
5352
|
+
upscale_factor: `${req.scaleFactor ?? 2}x`,
|
|
5353
|
+
face_enhancement: true
|
|
5354
|
+
};
|
|
5355
|
+
} else if (isUpscaler(id)) {
|
|
5296
5356
|
const refs = req.references ?? [];
|
|
5297
5357
|
const first = refs[0];
|
|
5298
5358
|
if (!first) {
|
|
@@ -5304,24 +5364,29 @@ async function generateReplicateImage(req, ctx, deps) {
|
|
|
5304
5364
|
retryable: false
|
|
5305
5365
|
});
|
|
5306
5366
|
}
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5367
|
+
const imageInput = { prompt: req.prompt };
|
|
5368
|
+
imageInput.image = refToUrl3(first);
|
|
5369
|
+
imageInput.creativity = 0;
|
|
5370
|
+
imageInput.scale_factor = 2;
|
|
5371
|
+
imageInput.output_format = "png";
|
|
5372
|
+
input = imageInput;
|
|
5311
5373
|
} else if (isWanImage(id)) {
|
|
5312
5374
|
const refs = (req.references ?? []).map(refToUrl3);
|
|
5313
|
-
|
|
5314
|
-
|
|
5375
|
+
const imageInput = { prompt: req.prompt };
|
|
5376
|
+
if (refs.length > 0) imageInput.images = refs;
|
|
5377
|
+
imageInput.size = wanImageSize(req.aspectRatio, req.size);
|
|
5378
|
+
input = imageInput;
|
|
5315
5379
|
} else if (isQwen(id)) {
|
|
5316
5380
|
const refs = (req.references ?? []).map(refToUrl3);
|
|
5317
5381
|
const firstUrl = refs[0];
|
|
5382
|
+
const imageInput = { prompt: req.prompt };
|
|
5318
5383
|
if (firstUrl !== void 0) {
|
|
5319
|
-
|
|
5320
|
-
if (!isQwenEditPlus(id))
|
|
5384
|
+
imageInput.image = firstUrl;
|
|
5385
|
+
if (!isQwenEditPlus(id)) imageInput.match_input_image = true;
|
|
5321
5386
|
}
|
|
5322
5387
|
const ratios = isQwenEditPlus(id) ? QWEN_EDIT_RATIOS : QWEN_IMAGE_RATIOS;
|
|
5323
5388
|
if (req.aspectRatio !== void 0) {
|
|
5324
|
-
if (ratios.has(req.aspectRatio))
|
|
5389
|
+
if (ratios.has(req.aspectRatio)) imageInput.aspect_ratio = req.aspectRatio;
|
|
5325
5390
|
else
|
|
5326
5391
|
ctx.warnings.push({
|
|
5327
5392
|
code: "option_ignored",
|
|
@@ -5331,18 +5396,21 @@ async function generateReplicateImage(req, ctx, deps) {
|
|
|
5331
5396
|
});
|
|
5332
5397
|
}
|
|
5333
5398
|
if (isQwenEditPlus(id) && req.outputFormat !== void 0) {
|
|
5334
|
-
|
|
5399
|
+
imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
|
|
5335
5400
|
}
|
|
5401
|
+
input = imageInput;
|
|
5336
5402
|
} else {
|
|
5337
5403
|
const refs = req.references ?? [];
|
|
5338
5404
|
const first = refs[0];
|
|
5339
|
-
|
|
5405
|
+
const imageInput = { prompt: req.prompt };
|
|
5406
|
+
if (first) imageInput.image = refToUrl3(first);
|
|
5407
|
+
input = imageInput;
|
|
5340
5408
|
}
|
|
5341
5409
|
const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
|
|
5342
5410
|
const file = await download(ctx, url);
|
|
5343
5411
|
const usage2 = imageUsage2(ctx, req);
|
|
5344
5412
|
usage2.quantity = predictTime;
|
|
5345
|
-
return { images: [{ bytes: file.bytes, mime: file.mime }], usage: usage2, raw };
|
|
5413
|
+
return { images: [{ bytes: file.bytes, mime: file.mime }], usage: usage2, raw, outputUrl: url };
|
|
5346
5414
|
}
|
|
5347
5415
|
function wanSize(aspectRatio) {
|
|
5348
5416
|
if (!aspectRatio || aspectRatio === "adaptive" || aspectRatio === "1:1") return "2K";
|