@shop-os/modelkit 0.3.0 → 0.3.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 +12 -0
- package/README.md +3 -3
- package/dist/catalog.cjs +26 -5
- package/dist/catalog.cjs.map +1 -1
- package/dist/catalog.js +26 -5
- package/dist/catalog.js.map +1 -1
- package/dist/index.cjs +136 -123
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +136 -123
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2369,6 +2369,10 @@ var ALL_KEY_SET = /* @__PURE__ */ new Set([
|
|
|
2369
2369
|
...IMAGE_INPUT_SPEC_KEYS,
|
|
2370
2370
|
...AUDIO_INPUT_SPEC_KEYS
|
|
2371
2371
|
]);
|
|
2372
|
+
var TYPES_BY_ADAPTER = /* @__PURE__ */ new Map([
|
|
2373
|
+
["replicate", ["video", "image", "audio"]],
|
|
2374
|
+
["fal", ["image"]]
|
|
2375
|
+
]);
|
|
2372
2376
|
var ALLOWED_KEYS_BY_TYPE = /* @__PURE__ */ new Map([
|
|
2373
2377
|
["video", VIDEO_KEY_SET],
|
|
2374
2378
|
["image", IMAGE_KEY_SET],
|
|
@@ -2407,7 +2411,8 @@ var StaticSchema = import_zod2.z.record(import_zod2.z.string(), ProviderScalarSc
|
|
|
2407
2411
|
var InputSpecSchema = import_zod2.z.strictObject({
|
|
2408
2412
|
fields: FieldsSchema,
|
|
2409
2413
|
values: ValuesSchema.optional(),
|
|
2410
|
-
static: StaticSchema.optional()
|
|
2414
|
+
static: StaticSchema.optional(),
|
|
2415
|
+
editEndpoint: import_zod2.z.string().min(1).optional()
|
|
2411
2416
|
}).superRefine((spec, ctx) => {
|
|
2412
2417
|
const mappedNames = new Set(Object.values(spec.fields));
|
|
2413
2418
|
for (const key of Object.keys(spec.static ?? {})) {
|
|
@@ -2435,18 +2440,21 @@ function validateInputSpec(raw, row) {
|
|
|
2435
2440
|
};
|
|
2436
2441
|
}
|
|
2437
2442
|
const spec = parsed.data;
|
|
2438
|
-
|
|
2443
|
+
const supportedTypes = TYPES_BY_ADAPTER.get(row.adapter);
|
|
2444
|
+
if (supportedTypes === void 0) {
|
|
2439
2445
|
return {
|
|
2440
2446
|
ok: false,
|
|
2441
|
-
issues: [
|
|
2447
|
+
issues: [
|
|
2448
|
+
{ path: [], message: "inputSpec is only supported on replicate and fal rows" }
|
|
2449
|
+
]
|
|
2442
2450
|
};
|
|
2443
2451
|
}
|
|
2444
2452
|
const issues = [];
|
|
2445
2453
|
const allowed = ALLOWED_KEYS_BY_TYPE.get(row.type);
|
|
2446
|
-
if (allowed === void 0) {
|
|
2454
|
+
if (allowed === void 0 || !supportedTypes.includes(row.type)) {
|
|
2447
2455
|
issues.push({
|
|
2448
2456
|
path: [],
|
|
2449
|
-
message: `inputSpec is not supported for "${row.type}" rows`
|
|
2457
|
+
message: row.adapter === "fal" ? `inputSpec on fal rows is only supported for image rows, not "${row.type}"` : `inputSpec is not supported for "${row.type}" rows`
|
|
2450
2458
|
});
|
|
2451
2459
|
} else {
|
|
2452
2460
|
for (const key of Object.keys(spec.fields)) {
|
|
@@ -2466,6 +2474,19 @@ function validateInputSpec(raw, row) {
|
|
|
2466
2474
|
}
|
|
2467
2475
|
}
|
|
2468
2476
|
}
|
|
2477
|
+
if (spec.editEndpoint !== void 0) {
|
|
2478
|
+
if (row.adapter !== "fal") {
|
|
2479
|
+
issues.push({
|
|
2480
|
+
path: ["editEndpoint"],
|
|
2481
|
+
message: "inputSpec.editEndpoint is only supported on fal rows"
|
|
2482
|
+
});
|
|
2483
|
+
} else if (!("references" in spec.fields)) {
|
|
2484
|
+
issues.push({
|
|
2485
|
+
path: ["editEndpoint"],
|
|
2486
|
+
message: 'inputSpec.editEndpoint needs a "references" entry in inputSpec.fields'
|
|
2487
|
+
});
|
|
2488
|
+
}
|
|
2489
|
+
}
|
|
2469
2490
|
const seenNames = /* @__PURE__ */ new Map();
|
|
2470
2491
|
for (const [key, name] of Object.entries(spec.fields)) {
|
|
2471
2492
|
const first = seenNames.get(name);
|
|
@@ -4210,6 +4231,70 @@ async function cancelFalVideoTool(sub, ctx) {
|
|
|
4210
4231
|
throw falError(res.status, await readBody(res), ctx.model.id);
|
|
4211
4232
|
}
|
|
4212
4233
|
|
|
4234
|
+
// src/adapters/input-spec-mapper.ts
|
|
4235
|
+
function canonicalValue(req, key) {
|
|
4236
|
+
return req[key];
|
|
4237
|
+
}
|
|
4238
|
+
function isMediaRef(value) {
|
|
4239
|
+
return value instanceof Object && ("url" in value || "bytes" in value);
|
|
4240
|
+
}
|
|
4241
|
+
function toProviderValue(value, refToUrl2) {
|
|
4242
|
+
if (Array.isArray(value)) {
|
|
4243
|
+
return value.map(refToUrl2);
|
|
4244
|
+
}
|
|
4245
|
+
if (isMediaRef(value)) {
|
|
4246
|
+
return refToUrl2(value);
|
|
4247
|
+
}
|
|
4248
|
+
return value;
|
|
4249
|
+
}
|
|
4250
|
+
function applyInputSpec(spec, req, refToUrl2, opts) {
|
|
4251
|
+
const out = {};
|
|
4252
|
+
for (const [canonical, providerField] of Object.entries(spec.fields)) {
|
|
4253
|
+
if (!isInputSpecKey(canonical)) continue;
|
|
4254
|
+
const value = canonicalValue(req, canonical);
|
|
4255
|
+
if (value === void 0) continue;
|
|
4256
|
+
if (opts?.omit?.(canonical, value)) continue;
|
|
4257
|
+
const serialised = toProviderValue(value, refToUrl2);
|
|
4258
|
+
const mapped = spec.values?.[canonical]?.[String(value)];
|
|
4259
|
+
out[providerField] = mapped ?? serialised;
|
|
4260
|
+
}
|
|
4261
|
+
for (const [key, value] of Object.entries(spec.static ?? {})) {
|
|
4262
|
+
out[key] = value;
|
|
4263
|
+
}
|
|
4264
|
+
return out;
|
|
4265
|
+
}
|
|
4266
|
+
function resolveRowInputSpec(req, ctx) {
|
|
4267
|
+
const raw = ctx.model.providerOptions?.["modelkit"]?.["inputSpec"];
|
|
4268
|
+
if (raw === void 0) return { kind: "absent" };
|
|
4269
|
+
const result = validateInputSpec(raw, ctx.model);
|
|
4270
|
+
if (!result.ok) {
|
|
4271
|
+
ctx.logger.warn(
|
|
4272
|
+
`[modelkit] ${ctx.model.id} has an invalid providerOptions.modelkit.inputSpec; the call fails`
|
|
4273
|
+
);
|
|
4274
|
+
return { kind: "invalid", issues: result.issues };
|
|
4275
|
+
}
|
|
4276
|
+
return {
|
|
4277
|
+
kind: "ok",
|
|
4278
|
+
spec: result.spec,
|
|
4279
|
+
input: applyInputSpec(result.spec, req, refToUrl, {
|
|
4280
|
+
omit: (key, value) => key === "aspectRatio" && value === "adaptive"
|
|
4281
|
+
})
|
|
4282
|
+
};
|
|
4283
|
+
}
|
|
4284
|
+
function describeInputSpecIssue(issue) {
|
|
4285
|
+
if (!issue) return "invalid inputSpec";
|
|
4286
|
+
return issue.path.length === 0 ? issue.message : `${issue.path.map((part) => String(part)).join(".")} ${issue.message}`;
|
|
4287
|
+
}
|
|
4288
|
+
function invalidInputSpecError(provider, ctx, issues) {
|
|
4289
|
+
return new ModelKitError({
|
|
4290
|
+
code: "unsupported_option",
|
|
4291
|
+
message: `${provider} row "${ctx.model.id}" has an invalid providerOptions.modelkit.inputSpec: ${describeInputSpecIssue(issues[0])}`,
|
|
4292
|
+
provider,
|
|
4293
|
+
model: ctx.model.id,
|
|
4294
|
+
retryable: false
|
|
4295
|
+
});
|
|
4296
|
+
}
|
|
4297
|
+
|
|
4213
4298
|
// src/adapters/fal.ts
|
|
4214
4299
|
var MUSE_T2I_ENDPOINT = "meta/muse-image/text-to-image";
|
|
4215
4300
|
var MUSE_EDIT_ENDPOINT = "meta/muse-image/edit";
|
|
@@ -4264,6 +4349,11 @@ function createFalAdapter(deps = defaultDeps) {
|
|
|
4264
4349
|
id: "fal",
|
|
4265
4350
|
generateImage: (req, ctx) => {
|
|
4266
4351
|
throwIfAborted(ctx.signal);
|
|
4352
|
+
const spec = resolveRowInputSpec(req, ctx);
|
|
4353
|
+
if (spec.kind === "invalid") {
|
|
4354
|
+
return Promise.reject(invalidInputSpecError("fal", ctx, spec.issues));
|
|
4355
|
+
}
|
|
4356
|
+
if (spec.kind === "ok") return generateSpecImage(req, ctx, deps, spec);
|
|
4267
4357
|
if (isIdeogram(ctx.model.providerModelId)) return generateIdeogramImage(req, ctx, deps);
|
|
4268
4358
|
return generateMuseImage(req, ctx, deps);
|
|
4269
4359
|
},
|
|
@@ -4443,6 +4533,36 @@ var museImageResultSchema = import_zod12.z.object({
|
|
|
4443
4533
|
})
|
|
4444
4534
|
).optional()
|
|
4445
4535
|
});
|
|
4536
|
+
async function collectFalImages(payload, ctx, req, label) {
|
|
4537
|
+
const parsed = museImageResultSchema.safeParse(payload);
|
|
4538
|
+
const images = parsed.success ? parsed.data.images ?? [] : [];
|
|
4539
|
+
const files = await Promise.all(
|
|
4540
|
+
images.flatMap((img) => {
|
|
4541
|
+
if (img.url === void 0) return [];
|
|
4542
|
+
return [decodeImage(ctx, img.url, img.content_type ?? void 0)];
|
|
4543
|
+
})
|
|
4544
|
+
);
|
|
4545
|
+
if (files.length === 0) {
|
|
4546
|
+
throw new ModelKitError({
|
|
4547
|
+
code: "provider_rejected",
|
|
4548
|
+
message: `${label} returned no image`,
|
|
4549
|
+
provider: "fal",
|
|
4550
|
+
model: ctx.model.id,
|
|
4551
|
+
retryable: false
|
|
4552
|
+
});
|
|
4553
|
+
}
|
|
4554
|
+
return {
|
|
4555
|
+
images: files.map((f) => ({ bytes: f.bytes, mime: f.mime })),
|
|
4556
|
+
usage: imageUsage(ctx, req, files.length),
|
|
4557
|
+
raw: payload
|
|
4558
|
+
};
|
|
4559
|
+
}
|
|
4560
|
+
async function generateSpecImage(req, ctx, deps, spec) {
|
|
4561
|
+
const hasReferences = (req.references ?? []).length > 0;
|
|
4562
|
+
const endpoint = hasReferences && spec.spec.editEndpoint !== void 0 ? spec.spec.editEndpoint : ctx.model.providerModelId;
|
|
4563
|
+
const payload = await runJob(ctx, deps, endpoint, spec.input);
|
|
4564
|
+
return collectFalImages(payload, ctx, req, `fal ${endpoint}`);
|
|
4565
|
+
}
|
|
4446
4566
|
async function generateMuseImage(req, ctx, deps) {
|
|
4447
4567
|
const refs = (req.references ?? []).map(refToUrl);
|
|
4448
4568
|
const isEdit = refs.length > 0;
|
|
@@ -4459,29 +4579,7 @@ async function generateMuseImage(req, ctx, deps) {
|
|
|
4459
4579
|
const custom = id.includes("/") && !id.startsWith("meta/muse-image") ? id : void 0;
|
|
4460
4580
|
const endpoint = custom ?? (isEdit ? MUSE_EDIT_ENDPOINT : MUSE_T2I_ENDPOINT);
|
|
4461
4581
|
const payload = await runJob(ctx, deps, endpoint, input);
|
|
4462
|
-
|
|
4463
|
-
const images = parsed.success ? parsed.data.images ?? [] : [];
|
|
4464
|
-
const firstUrl = images[0]?.url;
|
|
4465
|
-
if (images.length === 0 || firstUrl === void 0) {
|
|
4466
|
-
throw new ModelKitError({
|
|
4467
|
-
code: "provider_rejected",
|
|
4468
|
-
message: "fal Meta Muse returned no image",
|
|
4469
|
-
provider: "fal",
|
|
4470
|
-
model: ctx.model.id,
|
|
4471
|
-
retryable: false
|
|
4472
|
-
});
|
|
4473
|
-
}
|
|
4474
|
-
const files = await Promise.all(
|
|
4475
|
-
images.flatMap((img) => {
|
|
4476
|
-
if (img.url === void 0) return [];
|
|
4477
|
-
return [decodeImage(ctx, img.url, img.content_type ?? void 0)];
|
|
4478
|
-
})
|
|
4479
|
-
);
|
|
4480
|
-
return {
|
|
4481
|
-
images: files.map((f) => ({ bytes: f.bytes, mime: f.mime })),
|
|
4482
|
-
usage: imageUsage(ctx, req, files.length),
|
|
4483
|
-
raw: payload
|
|
4484
|
-
};
|
|
4582
|
+
return collectFalImages(payload, ctx, req, "fal Meta Muse");
|
|
4485
4583
|
}
|
|
4486
4584
|
var IDEOGRAM_MODEL_IDS = /* @__PURE__ */ new Set(["ideogram/v4.5"]);
|
|
4487
4585
|
function isIdeogram(providerModelId) {
|
|
@@ -4526,28 +4624,7 @@ async function generateIdeogramImage(req, ctx, deps) {
|
|
|
4526
4624
|
if (references.length > 0) input.reference_image_urls = references;
|
|
4527
4625
|
}
|
|
4528
4626
|
const payload = await runJob(ctx, deps, endpoint, input);
|
|
4529
|
-
|
|
4530
|
-
const images = parsed.success ? parsed.data.images ?? [] : [];
|
|
4531
|
-
const files = await Promise.all(
|
|
4532
|
-
images.flatMap((img) => {
|
|
4533
|
-
if (img.url === void 0) return [];
|
|
4534
|
-
return [decodeImage(ctx, img.url, img.content_type ?? void 0)];
|
|
4535
|
-
})
|
|
4536
|
-
);
|
|
4537
|
-
if (files.length === 0) {
|
|
4538
|
-
throw new ModelKitError({
|
|
4539
|
-
code: "provider_rejected",
|
|
4540
|
-
message: "fal Ideogram returned no image",
|
|
4541
|
-
provider: "fal",
|
|
4542
|
-
model: ctx.model.id,
|
|
4543
|
-
retryable: false
|
|
4544
|
-
});
|
|
4545
|
-
}
|
|
4546
|
-
return {
|
|
4547
|
-
images: files.map((f) => ({ bytes: f.bytes, mime: f.mime })),
|
|
4548
|
-
usage: imageUsage(ctx, req, files.length),
|
|
4549
|
-
raw: payload
|
|
4550
|
-
};
|
|
4627
|
+
return collectFalImages(payload, ctx, req, "fal Ideogram");
|
|
4551
4628
|
}
|
|
4552
4629
|
var H3_MAX_MODEL_IDS = /* @__PURE__ */ new Set([
|
|
4553
4630
|
"minimax/h3-max",
|
|
@@ -6896,41 +6973,6 @@ function createReplicateHttpClient(opts) {
|
|
|
6896
6973
|
|
|
6897
6974
|
// src/adapters/replicate.ts
|
|
6898
6975
|
var import_zod19 = require("zod");
|
|
6899
|
-
|
|
6900
|
-
// src/adapters/input-spec-mapper.ts
|
|
6901
|
-
function canonicalValue(req, key) {
|
|
6902
|
-
return req[key];
|
|
6903
|
-
}
|
|
6904
|
-
function isMediaRef(value) {
|
|
6905
|
-
return value instanceof Object && ("url" in value || "bytes" in value);
|
|
6906
|
-
}
|
|
6907
|
-
function toProviderValue(value, refToUrl2) {
|
|
6908
|
-
if (Array.isArray(value)) {
|
|
6909
|
-
return value.map(refToUrl2);
|
|
6910
|
-
}
|
|
6911
|
-
if (isMediaRef(value)) {
|
|
6912
|
-
return refToUrl2(value);
|
|
6913
|
-
}
|
|
6914
|
-
return value;
|
|
6915
|
-
}
|
|
6916
|
-
function applyInputSpec(spec, req, refToUrl2, opts) {
|
|
6917
|
-
const out = {};
|
|
6918
|
-
for (const [canonical, providerField] of Object.entries(spec.fields)) {
|
|
6919
|
-
if (!isInputSpecKey(canonical)) continue;
|
|
6920
|
-
const value = canonicalValue(req, canonical);
|
|
6921
|
-
if (value === void 0) continue;
|
|
6922
|
-
if (opts?.omit?.(canonical, value)) continue;
|
|
6923
|
-
const serialised = toProviderValue(value, refToUrl2);
|
|
6924
|
-
const mapped = spec.values?.[canonical]?.[String(value)];
|
|
6925
|
-
out[providerField] = mapped ?? serialised;
|
|
6926
|
-
}
|
|
6927
|
-
for (const [key, value] of Object.entries(spec.static ?? {})) {
|
|
6928
|
-
out[key] = value;
|
|
6929
|
-
}
|
|
6930
|
-
return out;
|
|
6931
|
-
}
|
|
6932
|
-
|
|
6933
|
-
// src/adapters/replicate.ts
|
|
6934
6976
|
var POLL_INTERVAL_MS = 1e3;
|
|
6935
6977
|
var POLL_TIMEOUT_MS = 15 * 6e4;
|
|
6936
6978
|
var defaultReplicateDeps = {
|
|
@@ -7173,38 +7215,6 @@ function replicateNoMapper(ctx, section) {
|
|
|
7173
7215
|
retryable: false
|
|
7174
7216
|
});
|
|
7175
7217
|
}
|
|
7176
|
-
function describeInputSpecIssue(issue) {
|
|
7177
|
-
if (!issue) return "invalid inputSpec";
|
|
7178
|
-
return issue.path.length === 0 ? issue.message : `${issue.path.map((part) => String(part)).join(".")} ${issue.message}`;
|
|
7179
|
-
}
|
|
7180
|
-
function replicateInvalidSpec(ctx, issues) {
|
|
7181
|
-
const detail = describeInputSpecIssue(issues[0]);
|
|
7182
|
-
return new ModelKitError({
|
|
7183
|
-
code: "unsupported_option",
|
|
7184
|
-
message: `replicate row "${ctx.model.id}" has an invalid providerOptions.modelkit.inputSpec: ${detail}`,
|
|
7185
|
-
provider: "replicate",
|
|
7186
|
-
model: ctx.model.id,
|
|
7187
|
-
retryable: false
|
|
7188
|
-
});
|
|
7189
|
-
}
|
|
7190
|
-
function omitAdaptive(key, value) {
|
|
7191
|
-
return key === "aspectRatio" && value === "adaptive";
|
|
7192
|
-
}
|
|
7193
|
-
function replicateInputSpec(req, ctx) {
|
|
7194
|
-
const raw = ctx.model.providerOptions?.["modelkit"]?.["inputSpec"];
|
|
7195
|
-
if (raw === void 0) return { kind: "absent" };
|
|
7196
|
-
const result = validateInputSpec(raw, ctx.model);
|
|
7197
|
-
if (!result.ok) {
|
|
7198
|
-
ctx.logger.warn(
|
|
7199
|
-
`[modelkit] ${ctx.model.id} has an invalid providerOptions.modelkit.inputSpec; the call fails`
|
|
7200
|
-
);
|
|
7201
|
-
return { kind: "invalid", issues: result.issues };
|
|
7202
|
-
}
|
|
7203
|
-
return {
|
|
7204
|
-
kind: "ok",
|
|
7205
|
-
input: applyInputSpec(result.spec, req, refToUrl, { omit: omitAdaptive })
|
|
7206
|
-
};
|
|
7207
|
-
}
|
|
7208
7218
|
var QWEN_IMAGE_RATIOS = /* @__PURE__ */ new Set([
|
|
7209
7219
|
"1:1",
|
|
7210
7220
|
"16:9",
|
|
@@ -7348,8 +7358,9 @@ async function generateReplicateImage(req, ctx, deps) {
|
|
|
7348
7358
|
break;
|
|
7349
7359
|
}
|
|
7350
7360
|
default: {
|
|
7351
|
-
const specResult =
|
|
7352
|
-
if (specResult.kind === "invalid")
|
|
7361
|
+
const specResult = resolveRowInputSpec(req, ctx);
|
|
7362
|
+
if (specResult.kind === "invalid")
|
|
7363
|
+
throw invalidInputSpecError("replicate", ctx, specResult.issues);
|
|
7353
7364
|
if (specResult.kind === "ok") {
|
|
7354
7365
|
input = specResult.input;
|
|
7355
7366
|
break;
|
|
@@ -7429,9 +7440,10 @@ async function generateReplicateVideo(req, ctx, deps) {
|
|
|
7429
7440
|
else if (kind === "kling-omni") input = klingOmniInput(req);
|
|
7430
7441
|
else if (kind === "hailuo") input = hailuoInput(req);
|
|
7431
7442
|
else {
|
|
7432
|
-
const specResult =
|
|
7443
|
+
const specResult = resolveRowInputSpec(req, ctx);
|
|
7433
7444
|
if (specResult.kind === "absent") throw replicateNoMapper(ctx, "video");
|
|
7434
|
-
if (specResult.kind === "invalid")
|
|
7445
|
+
if (specResult.kind === "invalid")
|
|
7446
|
+
throw invalidInputSpecError("replicate", ctx, specResult.issues);
|
|
7435
7447
|
input = specResult.input;
|
|
7436
7448
|
}
|
|
7437
7449
|
const api = client(ctx, deps);
|
|
@@ -7586,9 +7598,10 @@ async function generateReplicateAudio(req, ctx, deps) {
|
|
|
7586
7598
|
else if (kind === "elevenlabs") input = elevenLabsMusicInput(req, ctx);
|
|
7587
7599
|
else if (kind === "lyria") input = lyriaAudioInput(req, ctx);
|
|
7588
7600
|
else {
|
|
7589
|
-
const specResult =
|
|
7601
|
+
const specResult = resolveRowInputSpec(req, ctx);
|
|
7590
7602
|
if (specResult.kind === "absent") throw replicateNoMapper(ctx, "audio");
|
|
7591
|
-
if (specResult.kind === "invalid")
|
|
7603
|
+
if (specResult.kind === "invalid")
|
|
7604
|
+
throw invalidInputSpecError("replicate", ctx, specResult.issues);
|
|
7592
7605
|
input = specResult.input;
|
|
7593
7606
|
}
|
|
7594
7607
|
const { url, predictTime, raw } = await runPrediction(ctx, input, deps);
|