@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/dist/index.d.cts CHANGED
@@ -12,7 +12,7 @@ import { z } from 'zod';
12
12
  /** The AI SDK video model spelling: a catalog id or a provider video model instance. */
13
13
  type VideoModel = Parameters<typeof experimental_generateVideo>[0]["model"];
14
14
  type AspectRatio = "1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "3:2" | "2:3" | "4:5" | "5:4" | "21:9" | "9:21" | "97:30" | "adaptive";
15
- type ImageSize = "1K" | "2K" | "4K";
15
+ type ImageSize = "512" | "1K" | "2K" | "4K";
16
16
  type VideoResolution = "360p" | "480p" | "720p" | "768p" | "1080p" | "2k" | "4k";
17
17
  /** "xhigh" and "max" exist only on gpt-image-2.5; gpt-image-2 rejects them at the provider. */
18
18
  type ImageQuality = "auto" | "low" | "medium" | "high" | "xhigh" | "max";
@@ -37,6 +37,8 @@ interface ImageRequest {
37
37
  seed?: number;
38
38
  count?: number;
39
39
  outputFormat?: OutputFormat;
40
+ /** Upscale factor for upscaler rows; adapters map it to the provider's field. */
41
+ scaleFactor?: number;
40
42
  }
41
43
  /**
42
44
  * What a caller passes to generateImage. Same field names as the AI SDK's
@@ -120,7 +122,7 @@ interface MediaPart {
120
122
  cleanup?: () => Promise<void>;
121
123
  }
122
124
  type RequiredInput = "firstFrame" | "sourceVideo" | "references";
123
- type SupportedOption = "seed" | "quality" | "negativePrompt" | "cfgScale" | "generateAudio" | "promptExpansion" | "lastFrame" | "count" | "outputFormat" | "instrumental";
125
+ type SupportedOption = "seed" | "quality" | "negativePrompt" | "cfgScale" | "generateAudio" | "promptExpansion" | "lastFrame" | "count" | "outputFormat" | "scaleFactor" | "instrumental";
124
126
  interface ReferenceLimits {
125
127
  images?: number;
126
128
  videos?: number;
@@ -208,6 +210,8 @@ interface ImageResult {
208
210
  images: ImageBytes[];
209
211
  usage: UsageEvent;
210
212
  raw?: unknown;
213
+ /** The provider URL the images were downloaded from, when the provider hands one back. */
214
+ outputUrl?: string;
211
215
  }
212
216
  /** What generateImage returns: the adapter result plus `image`, SDK accessors and warnings. */
213
217
  interface ImageCallResult extends ImageResult {
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ import { z } from 'zod';
12
12
  /** The AI SDK video model spelling: a catalog id or a provider video model instance. */
13
13
  type VideoModel = Parameters<typeof experimental_generateVideo>[0]["model"];
14
14
  type AspectRatio = "1:1" | "16:9" | "9:16" | "4:3" | "3:4" | "3:2" | "2:3" | "4:5" | "5:4" | "21:9" | "9:21" | "97:30" | "adaptive";
15
- type ImageSize = "1K" | "2K" | "4K";
15
+ type ImageSize = "512" | "1K" | "2K" | "4K";
16
16
  type VideoResolution = "360p" | "480p" | "720p" | "768p" | "1080p" | "2k" | "4k";
17
17
  /** "xhigh" and "max" exist only on gpt-image-2.5; gpt-image-2 rejects them at the provider. */
18
18
  type ImageQuality = "auto" | "low" | "medium" | "high" | "xhigh" | "max";
@@ -37,6 +37,8 @@ interface ImageRequest {
37
37
  seed?: number;
38
38
  count?: number;
39
39
  outputFormat?: OutputFormat;
40
+ /** Upscale factor for upscaler rows; adapters map it to the provider's field. */
41
+ scaleFactor?: number;
40
42
  }
41
43
  /**
42
44
  * What a caller passes to generateImage. Same field names as the AI SDK's
@@ -120,7 +122,7 @@ interface MediaPart {
120
122
  cleanup?: () => Promise<void>;
121
123
  }
122
124
  type RequiredInput = "firstFrame" | "sourceVideo" | "references";
123
- type SupportedOption = "seed" | "quality" | "negativePrompt" | "cfgScale" | "generateAudio" | "promptExpansion" | "lastFrame" | "count" | "outputFormat" | "instrumental";
125
+ type SupportedOption = "seed" | "quality" | "negativePrompt" | "cfgScale" | "generateAudio" | "promptExpansion" | "lastFrame" | "count" | "outputFormat" | "scaleFactor" | "instrumental";
124
126
  interface ReferenceLimits {
125
127
  images?: number;
126
128
  videos?: number;
@@ -208,6 +210,8 @@ interface ImageResult {
208
210
  images: ImageBytes[];
209
211
  usage: UsageEvent;
210
212
  raw?: unknown;
213
+ /** The provider URL the images were downloaded from, when the provider hands one back. */
214
+ outputUrl?: string;
211
215
  }
212
216
  /** What generateImage returns: the adapter result plus `image`, SDK accessors and warnings. */
213
217
  interface ImageCallResult extends ImageResult {
package/dist/index.js CHANGED
@@ -83,7 +83,18 @@ var Catalog = class _Catalog {
83
83
 
84
84
  // src/catalog/image.ts
85
85
  var IMAGE_USAGE = { unit: "images", modality: "IMAGE" };
86
- var GEMINI_RATIOS = ["1:1", "16:9", "9:16", "4:3", "3:4", "21:9", "4:5", "5:4"];
86
+ var GEMINI_RATIOS = [
87
+ "1:1",
88
+ "2:3",
89
+ "3:2",
90
+ "16:9",
91
+ "9:16",
92
+ "4:3",
93
+ "3:4",
94
+ "21:9",
95
+ "4:5",
96
+ "5:4"
97
+ ];
87
98
  function geminiImage(id, label, aliases, sizes) {
88
99
  const model = {
89
100
  id,
@@ -106,7 +117,8 @@ var IMAGE_MODELS = [
106
117
  "gemini-3.1-flash-image-preview",
107
118
  "Nano Banana Flash",
108
119
  ["nb-2"],
109
- ["1K", "2K", "4K"]
120
+ // Flash 3.1 is the only Gemini image model with the 512 tier.
121
+ ["512", "1K", "2K", "4K"]
110
122
  ),
111
123
  geminiImage("gemini-3.1-flash-lite-image", "Nano Banana Lite", ["nb-lite"], ["1K"]),
112
124
  // trigger-workflows validates 1K only; pricing seeds 2K/4K rows so allow all three.
@@ -451,8 +463,12 @@ var OTHER_MODELS = [
451
463
  adapter: "replicate",
452
464
  providerModelId: "topazlabs/image-upscale",
453
465
  lifecycle: "active",
454
- constraints: {},
455
- defaults: {},
466
+ constraints: {
467
+ references: { images: 1 },
468
+ requires: ["references"],
469
+ supports: ["scaleFactor", "outputFormat"]
470
+ },
471
+ defaults: { scaleFactor: 2, outputFormat: "png" },
456
472
  usage: { unit: "compute_seconds", modality: "IMAGE" },
457
473
  pricingKind: "flat"
458
474
  },
@@ -508,7 +524,7 @@ var TEXT_MODELS = [
508
524
  text("gpt-5.6-sol", "GPT-5.6 Sol", "gpt-5.6-sol"),
509
525
  // Library key "claude-sonnet-4.5" maps to the SDK id "claude-sonnet-4-5".
510
526
  text("claude-sonnet-4-5", "Claude Sonnet 4.5", "claude-sonnet-4-5", {
511
- aliases: ["claude-sonnet-4.5"]
527
+ aliases: ["claude-sonnet-4.5", "claude-sonnet-4-5-20250929"]
512
528
  }),
513
529
  // Anthropic retired the dated 4.0 ids; callers that still send them get 4.6.
514
530
  text("claude-sonnet-4-6", "Claude Sonnet 4.6", "claude-sonnet-4-6", {
@@ -524,7 +540,7 @@ var TEXT_MODELS = [
524
540
  aliases: ["claude-opus-4.8", "claude-opus-4", "claude-opus-4.0", "claude-opus-4-20250514"]
525
541
  }),
526
542
  text("claude-haiku-4-5", "Claude Haiku 4.5", "claude-haiku-4-5", {
527
- aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001"]
543
+ aliases: ["claude-haiku-4.5", "claude-haiku-4-5-20251001", "claude-haiku-4"]
528
544
  }),
529
545
  text("claude-opus-5", "Claude Opus 5", "claude-opus-5", {
530
546
  aliases: ["claude-opus-5.0"]
@@ -1007,7 +1023,7 @@ var MODELS = [
1007
1023
  ];
1008
1024
 
1009
1025
  // src/resolve-options.ts
1010
- var SIZE_ORDER = ["1K", "2K", "4K"];
1026
+ var SIZE_ORDER = ["512", "1K", "2K", "4K"];
1011
1027
  var RESOLUTION_ORDER = ["360p", "480p", "720p", "768p", "1080p", "2k", "4k"];
1012
1028
  function ratioValue(r) {
1013
1029
  if (r === "adaptive") return 1;
@@ -1139,7 +1155,13 @@ function resolveImageOptions(model, request, onUnsupported = "error") {
1139
1155
  model: model.id
1140
1156
  });
1141
1157
  }
1142
- dropUnsupported(merged, model, warnings, ["seed", "quality", "count", "outputFormat"]);
1158
+ dropUnsupported(merged, model, warnings, [
1159
+ "seed",
1160
+ "quality",
1161
+ "count",
1162
+ "outputFormat",
1163
+ "scaleFactor"
1164
+ ]);
1143
1165
  return { request: merged, warnings };
1144
1166
  }
1145
1167
  function resolveVideoOptions(model, request, onUnsupported = "error") {
@@ -1920,6 +1942,7 @@ function createModelKit(config, adapters) {
1920
1942
  providerOptions,
1921
1943
  references,
1922
1944
  exactSize: exactSizeField,
1945
+ scaleFactor,
1923
1946
  quality: _quality,
1924
1947
  outputFormat: _outputFormat
1925
1948
  } = req;
@@ -1945,6 +1968,8 @@ function createModelKit(config, adapters) {
1945
1968
  if (_quality !== void 0) pushIgnored(warnings, "quality", _quality, model.modelId);
1946
1969
  if (_outputFormat !== void 0)
1947
1970
  pushIgnored(warnings, "outputFormat", _outputFormat, model.modelId);
1971
+ if (scaleFactor !== void 0)
1972
+ pushIgnored(warnings, "scaleFactor", scaleFactor, model.modelId);
1948
1973
  const abortSignal = combineSignals(req.abortSignal, call.signal);
1949
1974
  if (abortSignal) options.abortSignal = abortSignal;
1950
1975
  const result = await aiGenerateImage(options);
@@ -4004,6 +4029,10 @@ function createGoogleImageAdapter(deps = defaultGoogleImageDeps) {
4004
4029
  imageQuality: "auto"
4005
4030
  };
4006
4031
  if (req.size !== void 0) usage2.imageSize = req.size;
4032
+ if (result.usage?.inputTokens !== void 0)
4033
+ usage2.inputTokens = result.usage.inputTokens;
4034
+ if (result.usage?.outputTokens !== void 0)
4035
+ usage2.outputTokens = result.usage.outputTokens;
4007
4036
  return { images, usage: usage2, raw: result };
4008
4037
  }
4009
4038
  };
@@ -4535,7 +4564,7 @@ function aspectRatioToSize(ratio, tier) {
4535
4564
  };
4536
4565
  const MIN_PIXELS = 655360;
4537
4566
  const MAX_PIXELS = 8294400;
4538
- const maxLong = TIER_LONG_EDGE[tier];
4567
+ const maxLong = TIER_LONG_EDGE[tier] ?? 1024;
4539
4568
  const RATIO_MAP = /* @__PURE__ */ new Map([
4540
4569
  ["1:1", [1, 1]],
4541
4570
  ["16:9", [16, 9]],
@@ -5169,6 +5198,9 @@ function isUpscaler(providerModelId) {
5169
5198
  const id = isLower(providerModelId);
5170
5199
  return id.includes("upscal") || id.includes("crystal") || id.includes("topaz");
5171
5200
  }
5201
+ function isTopazUpscaler(providerModelId) {
5202
+ return isLower(providerModelId).includes("topazlabs/image-upscale");
5203
+ }
5172
5204
  function isQwen(providerModelId) {
5173
5205
  return isLower(providerModelId).includes("qwen");
5174
5206
  }
@@ -5218,8 +5250,36 @@ function wanImageSize(aspectRatio, size) {
5218
5250
  }
5219
5251
  async function generateReplicateImage(req, ctx, deps) {
5220
5252
  const id = ctx.model.providerModelId;
5221
- const input = { prompt: req.prompt };
5222
- if (isUpscaler(id)) {
5253
+ let input;
5254
+ if (isTopazUpscaler(id)) {
5255
+ const first = (req.references ?? [])[0];
5256
+ if (!first) {
5257
+ throw new ModelKitError({
5258
+ code: "missing_input",
5259
+ message: `${ctx.model.id} requires a reference image to upscale`,
5260
+ provider: "replicate",
5261
+ model: ctx.model.id,
5262
+ retryable: false
5263
+ });
5264
+ }
5265
+ let format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat ?? "png";
5266
+ if (format !== "png" && format !== "jpg") {
5267
+ ctx.warnings.push({
5268
+ code: "option_ignored",
5269
+ field: "outputFormat",
5270
+ from: format,
5271
+ message: "topazlabs/image-upscale supports only png and jpg; using png"
5272
+ });
5273
+ format = "png";
5274
+ }
5275
+ input = {
5276
+ image: refToUrl3(first),
5277
+ enhance_model: "Standard V2",
5278
+ output_format: format,
5279
+ upscale_factor: `${req.scaleFactor ?? 2}x`,
5280
+ face_enhancement: true
5281
+ };
5282
+ } else if (isUpscaler(id)) {
5223
5283
  const refs = req.references ?? [];
5224
5284
  const first = refs[0];
5225
5285
  if (!first) {
@@ -5231,24 +5291,29 @@ async function generateReplicateImage(req, ctx, deps) {
5231
5291
  retryable: false
5232
5292
  });
5233
5293
  }
5234
- input.image = refToUrl3(first);
5235
- input.creativity = 0;
5236
- input.scale_factor = 2;
5237
- input.output_format = "png";
5294
+ const imageInput = { prompt: req.prompt };
5295
+ imageInput.image = refToUrl3(first);
5296
+ imageInput.creativity = 0;
5297
+ imageInput.scale_factor = 2;
5298
+ imageInput.output_format = "png";
5299
+ input = imageInput;
5238
5300
  } else if (isWanImage(id)) {
5239
5301
  const refs = (req.references ?? []).map(refToUrl3);
5240
- if (refs.length > 0) input.images = refs;
5241
- input.size = wanImageSize(req.aspectRatio, req.size);
5302
+ const imageInput = { prompt: req.prompt };
5303
+ if (refs.length > 0) imageInput.images = refs;
5304
+ imageInput.size = wanImageSize(req.aspectRatio, req.size);
5305
+ input = imageInput;
5242
5306
  } else if (isQwen(id)) {
5243
5307
  const refs = (req.references ?? []).map(refToUrl3);
5244
5308
  const firstUrl = refs[0];
5309
+ const imageInput = { prompt: req.prompt };
5245
5310
  if (firstUrl !== void 0) {
5246
- input.image = firstUrl;
5247
- if (!isQwenEditPlus(id)) input.match_input_image = true;
5311
+ imageInput.image = firstUrl;
5312
+ if (!isQwenEditPlus(id)) imageInput.match_input_image = true;
5248
5313
  }
5249
5314
  const ratios = isQwenEditPlus(id) ? QWEN_EDIT_RATIOS : QWEN_IMAGE_RATIOS;
5250
5315
  if (req.aspectRatio !== void 0) {
5251
- if (ratios.has(req.aspectRatio)) input.aspect_ratio = req.aspectRatio;
5316
+ if (ratios.has(req.aspectRatio)) imageInput.aspect_ratio = req.aspectRatio;
5252
5317
  else
5253
5318
  ctx.warnings.push({
5254
5319
  code: "option_ignored",
@@ -5258,18 +5323,21 @@ async function generateReplicateImage(req, ctx, deps) {
5258
5323
  });
5259
5324
  }
5260
5325
  if (isQwenEditPlus(id) && req.outputFormat !== void 0) {
5261
- input.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
5326
+ imageInput.output_format = req.outputFormat === "jpeg" ? "jpg" : req.outputFormat;
5262
5327
  }
5328
+ input = imageInput;
5263
5329
  } else {
5264
5330
  const refs = req.references ?? [];
5265
5331
  const first = refs[0];
5266
- if (first) input.image = refToUrl3(first);
5332
+ const imageInput = { prompt: req.prompt };
5333
+ if (first) imageInput.image = refToUrl3(first);
5334
+ input = imageInput;
5267
5335
  }
5268
5336
  const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
5269
5337
  const file = await download(ctx, url);
5270
5338
  const usage2 = imageUsage2(ctx, req);
5271
5339
  usage2.quantity = predictTime;
5272
- return { images: [{ bytes: file.bytes, mime: file.mime }], usage: usage2, raw };
5340
+ return { images: [{ bytes: file.bytes, mime: file.mime }], usage: usage2, raw, outputUrl: url };
5273
5341
  }
5274
5342
  function wanSize(aspectRatio) {
5275
5343
  if (!aspectRatio || aspectRatio === "adaptive" || aspectRatio === "1:1") return "2K";