feat(providers): complete SenseNova free Token Plan — chat + Text-to-Image (port from 9router#2233) (#5679)

Integrated into release/v3.8.43.
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-30 14:56:31 -03:00
committed by GitHub
parent 6b4a80ad10
commit 871dc792d4
3 changed files with 71 additions and 0 deletions

View File

@@ -548,6 +548,19 @@ export const IMAGE_PROVIDERS: Record<string, ImageProviderConfig> = {
],
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"],
},
};
/**

View File

@@ -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 },

View File

@@ -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"
);
});