mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-27 17:44:45 +03:00
Compare commits
1 Commits
docs/dedup
...
fix/11296-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a443cb71be |
1
changelog.d/fixes/11296-kie-market-model-id-sweep.md
Normal file
1
changelog.d/fixes/11296-kie-market-model-id-sweep.md
Normal file
@@ -0,0 +1 @@
|
||||
- **fix(kie):** correct 12 more KIE Market catalog ids that were sent to `createTask` unchanged but diverge from KIE's documented upstream `model` values — GPT Image 2 T2I/I2I (drops the `gpt/` prefix), GPT Image 1.5 T2I/I2I (`gpt-image/` namespace), Seedream 5.0 Lite T2I/I2I (drops the `.0`), all 4 Flux 2 variants (`flux-2/` namespace, generic variant renamed `flex`), and Wan 2.7 Image / Image Pro (dash instead of dot) — each verified individually against the literal example request published on docs.kie.ai. `#11326`'s "everything else already matches" claim was wrong a second time (#11296); `z-image/4.0-*`/`z-image/4.5-*` and `flux/kontext` remain open, documented as unresolved in `KIE_MARKET_UPSTREAM_MODEL_IDS`'s comment pending further verification.
|
||||
@@ -92,19 +92,65 @@ interface KieImageOptions {
|
||||
}
|
||||
|
||||
// KIE Market catalog ids are namespaced for OmniRoute's catalog
|
||||
// (`google-imagen/<model>`), but the KIE Market createTask API expects
|
||||
// (`<vendor>/<model>`), but the KIE Market createTask API expects
|
||||
// vendor-specific upstream ids that do not follow a single consistent
|
||||
// pattern (confirmed against docs.kie.ai/market/google/* — see #11225,
|
||||
// #11296): nano-banana-2 and nano-banana-pro drop the vendor namespace
|
||||
// entirely, while nano-banana and nano-banana-edit use a `google/` prefix
|
||||
// instead of `google-imagen/`. Every other KIE Market namespace (seedream,
|
||||
// flux, ideogram, qwen, wan, grok-imagine, gpt) already matches its real
|
||||
// upstream id byte-for-byte, so this map stays scoped to google-imagen.
|
||||
// pattern. Every entry below was confirmed individually against the literal
|
||||
// example request JSON published on docs.kie.ai (never inferred by pattern —
|
||||
// see #11326's false "everything else already matches" claim and #11296's
|
||||
// follow-up correction):
|
||||
// - google-imagen: nano-banana-2 and nano-banana-pro drop the vendor
|
||||
// namespace entirely; nano-banana and nano-banana-edit use a `google/`
|
||||
// prefix instead of `google-imagen/` (docs.kie.ai/market/google/*).
|
||||
// - gpt: gpt-image-2-* drops the `gpt/` namespace entirely
|
||||
// (docs.kie.ai/market/gpt/gpt-image-2-*); gpt-image-1.5-* uses a
|
||||
// `gpt-image/` namespace instead of `gpt/gpt-image-1.5-`, and keeps the
|
||||
// dot in "1.5" (docs.kie.ai/market/gpt-image/1-5-*).
|
||||
// - seedream: 5.0-lite-* drops the ".0" — real id is `5-lite-*`
|
||||
// (docs.kie.ai/market/seedream/5-lite-text-to-image); seedream 4.5 (T2I
|
||||
// and edit) already matches byte-for-byte.
|
||||
// - flux: `flux/2-*` uses a `flux-2/` namespace (dash, not slash); the
|
||||
// generic (non-"pro") variant is named `flex` upstream, not `2`
|
||||
// (docs.kie.ai/market/flux2/pro-*, .../flex-*).
|
||||
// - wan: `wan/2.7-*` keeps the dot in our catalog, but KIE's documented
|
||||
// enum uses a dash — real id is `wan/2-7-*`
|
||||
// (docs.kie.ai/market/wan/2-7-image[-pro]).
|
||||
// - ideogram (v3-text-to-image, v3-edit, v3-remix), qwen, qwen2, and
|
||||
// grok-imagine already match byte-for-byte
|
||||
// (docs.kie.ai/market/{ideogram,qwen,qwen2,grok-imagine}/*).
|
||||
// ideogram/v3-reframe has no dedicated docs.kie.ai page as of this sweep
|
||||
// (its 3 siblings above are all direct id matches, so it is assumed
|
||||
// correct by pattern, not independently confirmed).
|
||||
// Two catalog entries remain UNRESOLVED after this sweep and are
|
||||
// deliberately left untouched pending a follow-up (see #11296 discussion):
|
||||
// - z-image/4.0-text-to-image and z-image/4.5-text-to-image: the only
|
||||
// documented Z-Image Market page (docs.kie.ai/market/z-image/z-image)
|
||||
// shows a single fixed `model` enum value `"z-image"` with no
|
||||
// version-specific id or "version" input field found — unclear whether
|
||||
// both catalog ids should collapse to the same upstream call.
|
||||
// - flux/kontext: no `docs.kie.ai/market/flux2/kontext` (or similar)
|
||||
// Market page exists; Flux Kontext is documented under the separate
|
||||
// `/flux-kontext-api/*` docs tree with its own endpoint
|
||||
// (`POST /api/v1/flux/kontext/generate`, models `flux-kontext-pro`/
|
||||
// `flux-kontext-max`), not the Market `createTask` flow this map feeds.
|
||||
// This entry may be miscatalogued as `isMarket: true` and need a
|
||||
// dedicated reroute rather than an id rewrite.
|
||||
export const KIE_MARKET_UPSTREAM_MODEL_IDS: ReadonlyMap<string, string> = new Map([
|
||||
["google-imagen/nano-banana", "google/nano-banana"],
|
||||
["google-imagen/nano-banana-2", "nano-banana-2"],
|
||||
["google-imagen/nano-banana-pro", "nano-banana-pro"],
|
||||
["google-imagen/nano-banana-edit", "google/nano-banana-edit"],
|
||||
["gpt/gpt-image-2-text-to-image", "gpt-image-2-text-to-image"],
|
||||
["gpt/gpt-image-2-image-to-image", "gpt-image-2-image-to-image"],
|
||||
["gpt/gpt-image-1.5-text-to-image", "gpt-image/1.5-text-to-image"],
|
||||
["gpt/gpt-image-1.5-image-to-image", "gpt-image/1.5-image-to-image"],
|
||||
["seedream/5.0-lite-text-to-image", "seedream/5-lite-text-to-image"],
|
||||
["seedream/5.0-lite-image-to-image", "seedream/5-lite-image-to-image"],
|
||||
["flux/2-pro-text-to-image", "flux-2/pro-text-to-image"],
|
||||
["flux/2-pro-image-to-image", "flux-2/pro-image-to-image"],
|
||||
["flux/2-text-to-image", "flux-2/flex-text-to-image"],
|
||||
["flux/2-image-to-image", "flux-2/flex-image-to-image"],
|
||||
["wan/2.7-image", "wan/2-7-image"],
|
||||
["wan/2.7-image-pro", "wan/2-7-image-pro"],
|
||||
]);
|
||||
|
||||
export function resolveKieMarketUpstreamModelId(publicModelId: string): string {
|
||||
|
||||
88
tests/unit/kie-market-upstream-id-11296-probe.test.ts
Normal file
88
tests/unit/kie-market-upstream-id-11296-probe.test.ts
Normal file
@@ -0,0 +1,88 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { mkdtempSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
|
||||
process.env.DATA_DIR = mkdtempSync(join(tmpdir(), "omniroute-kie-11296-probe-"));
|
||||
|
||||
const { handleImageGeneration } = await import(
|
||||
"../../open-sse/handlers/imageGeneration.ts"
|
||||
);
|
||||
|
||||
interface CapturedCreate {
|
||||
url: string;
|
||||
body: Record<string, unknown>;
|
||||
}
|
||||
|
||||
async function captureCreateTaskModel(publicModel: string): Promise<string> {
|
||||
const originalFetch = globalThis.fetch;
|
||||
let captured: CapturedCreate | undefined;
|
||||
|
||||
globalThis.fetch = (async (url: unknown, options: { body?: unknown } = {}) => {
|
||||
const stringUrl = String(url);
|
||||
|
||||
if (stringUrl === "https://api.kie.ai/api/v1/jobs/createTask") {
|
||||
captured = {
|
||||
url: stringUrl,
|
||||
body: JSON.parse(String(options.body ?? "{}")) as Record<string, unknown>,
|
||||
};
|
||||
return new Response(JSON.stringify({ code: 200, data: { taskId: "kie-probe-task-1" } }), {
|
||||
status: 200,
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
if (stringUrl.startsWith("https://api.kie.ai/api/v1/jobs/recordInfo")) {
|
||||
return new Response(
|
||||
JSON.stringify({
|
||||
code: 200,
|
||||
data: {
|
||||
state: "success",
|
||||
resultJson: JSON.stringify({ resultUrls: ["https://example.com/kie-probe.png"] }),
|
||||
},
|
||||
}),
|
||||
{ status: 200, headers: { "content-type": "application/json" } }
|
||||
);
|
||||
}
|
||||
|
||||
throw new Error(`Unexpected URL: ${stringUrl}`);
|
||||
}) as typeof globalThis.fetch;
|
||||
|
||||
try {
|
||||
await handleImageGeneration({
|
||||
body: { model: publicModel, prompt: "probe prompt", size: "1024x1024", n: 1 },
|
||||
credentials: { apiKey: "test-kie-key" },
|
||||
log: null,
|
||||
});
|
||||
assert.ok(captured, "expected a createTask request to be captured");
|
||||
return String(captured.body.model);
|
||||
} finally {
|
||||
globalThis.fetch = originalFetch;
|
||||
}
|
||||
}
|
||||
|
||||
test("#11296: GPT Image 2 T2I sends KIE's real upstream id (no namespace prefix)", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/gpt/gpt-image-2-text-to-image");
|
||||
assert.equal(sentModel, "gpt-image-2-text-to-image");
|
||||
});
|
||||
|
||||
test("#11296: GPT Image 2 I2I sends KIE's real upstream id (no namespace prefix)", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/gpt/gpt-image-2-image-to-image");
|
||||
assert.equal(sentModel, "gpt-image-2-image-to-image");
|
||||
});
|
||||
|
||||
test("#11296: GPT Image 1.5 T2I sends KIE's real 'gpt-image/' namespace", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/gpt/gpt-image-1.5-text-to-image");
|
||||
assert.equal(sentModel, "gpt-image/1.5-text-to-image");
|
||||
});
|
||||
|
||||
test("#11296: Seedream 5.0 Lite T2I sends KIE's real id without the '.0'", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/seedream/5.0-lite-text-to-image");
|
||||
assert.equal(sentModel, "seedream/5-lite-text-to-image");
|
||||
});
|
||||
|
||||
test("#11296: Flux 2 Pro T2I sends KIE's real 'flux-2/' namespace (dash, not slash)", async () => {
|
||||
const sentModel = await captureCreateTaskModel("kie/flux/2-pro-text-to-image");
|
||||
assert.equal(sentModel, "flux-2/pro-text-to-image");
|
||||
});
|
||||
@@ -103,13 +103,21 @@ function resolveLiveKieMarketCatalog() {
|
||||
}));
|
||||
}
|
||||
|
||||
test("KIE Market resolver changes exactly the 4 google-imagen ids in the live market catalog", () => {
|
||||
test("KIE Market resolver changes exactly the documented mismatched ids in the live market catalog", () => {
|
||||
const roundTrips = resolveLiveKieMarketCatalog();
|
||||
const changed = roundTrips.filter(({ publicModelId, upstreamModelId }) => {
|
||||
return upstreamModelId !== publicModelId;
|
||||
});
|
||||
|
||||
assert.deepEqual(changed, [
|
||||
{
|
||||
publicModelId: "seedream/5.0-lite-text-to-image",
|
||||
upstreamModelId: "seedream/5-lite-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "seedream/5.0-lite-image-to-image",
|
||||
upstreamModelId: "seedream/5-lite-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "google-imagen/nano-banana-2",
|
||||
upstreamModelId: "nano-banana-2",
|
||||
@@ -126,19 +134,71 @@ test("KIE Market resolver changes exactly the 4 google-imagen ids in the live ma
|
||||
publicModelId: "google-imagen/nano-banana-edit",
|
||||
upstreamModelId: "google/nano-banana-edit",
|
||||
},
|
||||
{
|
||||
publicModelId: "flux/2-pro-image-to-image",
|
||||
upstreamModelId: "flux-2/pro-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "flux/2-pro-text-to-image",
|
||||
upstreamModelId: "flux-2/pro-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "flux/2-image-to-image",
|
||||
upstreamModelId: "flux-2/flex-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "flux/2-text-to-image",
|
||||
upstreamModelId: "flux-2/flex-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "gpt/gpt-image-1.5-text-to-image",
|
||||
upstreamModelId: "gpt-image/1.5-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "gpt/gpt-image-1.5-image-to-image",
|
||||
upstreamModelId: "gpt-image/1.5-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "gpt/gpt-image-2-text-to-image",
|
||||
upstreamModelId: "gpt-image-2-text-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "gpt/gpt-image-2-image-to-image",
|
||||
upstreamModelId: "gpt-image-2-image-to-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "wan/2.7-image",
|
||||
upstreamModelId: "wan/2-7-image",
|
||||
},
|
||||
{
|
||||
publicModelId: "wan/2.7-image-pro",
|
||||
upstreamModelId: "wan/2-7-image-pro",
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
const REWRITTEN_GOOGLE_IMAGEN_MARKET_IDS = new Set([
|
||||
const REWRITTEN_MARKET_IDS = new Set([
|
||||
"google-imagen/nano-banana",
|
||||
"google-imagen/nano-banana-2",
|
||||
"google-imagen/nano-banana-pro",
|
||||
"google-imagen/nano-banana-edit",
|
||||
"gpt/gpt-image-2-text-to-image",
|
||||
"gpt/gpt-image-2-image-to-image",
|
||||
"gpt/gpt-image-1.5-text-to-image",
|
||||
"gpt/gpt-image-1.5-image-to-image",
|
||||
"seedream/5.0-lite-text-to-image",
|
||||
"seedream/5.0-lite-image-to-image",
|
||||
"flux/2-pro-text-to-image",
|
||||
"flux/2-pro-image-to-image",
|
||||
"flux/2-text-to-image",
|
||||
"flux/2-image-to-image",
|
||||
"wan/2.7-image",
|
||||
"wan/2.7-image-pro",
|
||||
]);
|
||||
|
||||
test("KIE Market resolver preserves every other live market catalog id byte-identically", () => {
|
||||
for (const { publicModelId, upstreamModelId } of resolveLiveKieMarketCatalog()) {
|
||||
if (!REWRITTEN_GOOGLE_IMAGEN_MARKET_IDS.has(publicModelId)) {
|
||||
if (!REWRITTEN_MARKET_IDS.has(publicModelId)) {
|
||||
assert.equal(
|
||||
upstreamModelId,
|
||||
publicModelId,
|
||||
@@ -148,8 +208,8 @@ test("KIE Market resolver preserves every other live market catalog id byte-iden
|
||||
}
|
||||
});
|
||||
|
||||
test("KIE Market resolver keeps exactly the explicit google-imagen upstream id mappings (#11296)", () => {
|
||||
assert.equal(KIE_MARKET_UPSTREAM_MODEL_IDS.size, 4);
|
||||
test("KIE Market resolver keeps exactly the explicit upstream id mappings (#11296)", () => {
|
||||
assert.equal(KIE_MARKET_UPSTREAM_MODEL_IDS.size, 16);
|
||||
});
|
||||
|
||||
test("KIE Market resolver passes an unknown namespaced id through byte-identically", () => {
|
||||
@@ -209,6 +269,78 @@ test("KIE Market createTask sends the KIE upstream id for Nano Banana Edit (#112
|
||||
);
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the unprefixed upstream id for GPT Image 2 T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/gpt/gpt-image-2-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "gpt-image-2-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the unprefixed upstream id for GPT Image 2 I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/gpt/gpt-image-2-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "gpt-image-2-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'gpt-image/' namespace for GPT Image 1.5 T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/gpt/gpt-image-1.5-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "gpt-image/1.5-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'gpt-image/' namespace for GPT Image 1.5 I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/gpt/gpt-image-1.5-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "gpt-image/1.5-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask drops the '.0' for Seedream 5.0 Lite T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/seedream/5.0-lite-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "seedream/5-lite-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask drops the '.0' for Seedream 5.0 Lite I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/seedream/5.0-lite-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "seedream/5-lite-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'flux-2/' namespace for Flux 2 Pro T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/flux/2-pro-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "flux-2/pro-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'flux-2/' namespace for Flux 2 Pro I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/flux/2-pro-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "flux-2/pro-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'flux-2/flex-' name for Flux 2 T2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/flux/2-text-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "flux-2/flex-text-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the 'flux-2/flex-' name for Flux 2 I2I (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/flux/2-image-to-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "flux-2/flex-image-to-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the dash-separated id for Wan 2.7 Image (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/wan/2.7-image");
|
||||
|
||||
assert.equal(captured.create.body.model, "wan/2-7-image");
|
||||
});
|
||||
|
||||
test("KIE Market createTask sends the dash-separated id for Wan 2.7 Image Pro (#11296)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/wan/2.7-image-pro");
|
||||
|
||||
assert.equal(captured.create.body.model, "wan/2-7-image-pro");
|
||||
});
|
||||
|
||||
test("KIE Market createTask leaves genuinely namespaced upstream ids untouched (#11225 control)", async () => {
|
||||
const captured = await runKieMarketGeneration("kie/seedream/4.5-text-to-image");
|
||||
|
||||
|
||||
Reference in New Issue
Block a user