diff --git a/open-sse/config/imageRegistry.ts b/open-sse/config/imageRegistry.ts index 6f722da03f..bc83b37c84 100644 --- a/open-sse/config/imageRegistry.ts +++ b/open-sse/config/imageRegistry.ts @@ -548,6 +548,19 @@ export const IMAGE_PROVIDERS: Record = { ], supportedSizes: ["1024x1024", "1024x1280", "1280x1024"], }, + + // SenseNova (商汤日日新) Text-to-Image on the free Token Plan. OpenAI-compatible + // `/v1/images/generations`, so the generic OpenAI image handler routes it — same + // SenseNova api-key/connection as the chat provider. (9router#2233) + sensenova: { + id: "sensenova", + baseUrl: "https://api.sensenova.cn/v1/images/generations", + authType: "apikey", + authHeader: "bearer", + format: "openai", + models: [{ id: "sensenova-u1-fast", name: "SenseNova U1 Fast" }], + supportedSizes: ["1024x1024"], + }, }; /** diff --git a/open-sse/config/providers/registry/sensenova/index.ts b/open-sse/config/providers/registry/sensenova/index.ts index c3f98d3e79..9407e4ef29 100644 --- a/open-sse/config/providers/registry/sensenova/index.ts +++ b/open-sse/config/providers/registry/sensenova/index.ts @@ -15,6 +15,8 @@ export const sensenovaProvider: RegistryEntry = { { id: "SenseNova-V6.5-Pro", name: "SenseNova V6.5 Pro", contextLength: 131072 }, { id: "SenseNova-V6.5-Turbo", name: "SenseNova V6.5 Turbo", contextLength: 131072 }, { id: "sensenova-6.7-flash-lite", name: "SenseNova 6.7 Flash-Lite" }, + // DeepSeek V4 Flash is served on SenseNova's free Token Plan (9router#2233). + { id: "deepseek-v4-flash", name: "DeepSeek V4 Flash" }, { id: "SenseChat-5", name: "SenseChat 5", contextLength: 131072 }, { id: "SenseChat-5-Cantonese", name: "SenseChat 5 Cantonese", contextLength: 32768 }, { id: "SenseChat-Turbo", name: "SenseChat Turbo", contextLength: 4096 }, diff --git a/tests/unit/sensenova-image-provider-2233.test.ts b/tests/unit/sensenova-image-provider-2233.test.ts new file mode 100644 index 0000000000..6bce716af5 --- /dev/null +++ b/tests/unit/sensenova-image-provider-2233.test.ts @@ -0,0 +1,56 @@ +import test from "node:test"; +import assert from "node:assert/strict"; + +// 9router#2233: complete the SenseNova (商汤日日新) provider for its free Token Plan. +// The chat provider already existed but was missing the `deepseek-v4-flash` chat model +// and — the core ask — the Text-to-Image model `sensenova-u1-fast`, which cannot work +// through the chat path: it must be registered as an IMAGE provider so requests route +// to `/v1/images/generations` via the OpenAI-compatible image handler. + +const { sensenovaProvider } = await import( + "../../open-sse/config/providers/registry/sensenova/index.ts" +); +const { getImageProvider } = await import("../../open-sse/config/imageRegistry.ts"); + +test("SenseNova chat registry includes the deepseek-v4-flash chat model", () => { + const ids = sensenovaProvider.models.map((m) => m.id); + assert.ok( + ids.includes("deepseek-v4-flash"), + `expected deepseek-v4-flash in SenseNova chat models, got: ${ids.join(", ")}` + ); +}); + +test("SenseNova chat registry does NOT carry the image model (image goes to the image registry)", () => { + const ids = sensenovaProvider.models.map((m) => m.id.toLowerCase()); + assert.ok( + !ids.includes("sensenova-u1-fast"), + "the text-to-image model must live in the image registry, not the chat registry" + ); +}); + +test("SenseNova is registered as an OpenAI-compatible image provider", () => { + const cfg = getImageProvider("sensenova"); + assert.ok(cfg, "expected an IMAGE_PROVIDERS entry for sensenova"); + assert.equal(cfg.id, "sensenova"); + assert.equal(cfg.format, "openai", "must use the generic OpenAI-compatible image handler"); + assert.equal(cfg.authType, "apikey"); + assert.equal(cfg.authHeader, "bearer"); + assert.match( + cfg.baseUrl, + /\/v1\/images\/generations$/, + "image baseUrl must target the OpenAI-compatible /v1/images/generations endpoint" + ); +}); + +test("SenseNova image provider exposes the sensenova-u1-fast text-to-image model", () => { + const cfg = getImageProvider("sensenova"); + const ids = (cfg?.models || []).map((m) => m.id); + assert.ok( + ids.includes("sensenova-u1-fast"), + `expected sensenova-u1-fast in SenseNova image models, got: ${ids.join(", ")}` + ); + assert.ok( + Array.isArray(cfg?.supportedSizes) && cfg.supportedSizes.length > 0, + "image provider must declare at least one supported size" + ); +});