From 773bd9dffd74388d0f9663564ef76ce77bb040b5 Mon Sep 17 00:00:00 2001 From: LingXuanYin <3546599908@qq.com> Date: Wed, 28 Oct 2026 22:56:02 +0800 Subject: [PATCH 1/2] Revert "feat(qwen): the adapter accepts the retired id qwen-audio-3.0-tts-plus as a hidden alias" This reverts commit 019aa08e63cba3a66fdebc302b1e47599ee665f8. --- src/adapters/audio-speech.ts | 9 +--- test/adapters/audio-speech.test.ts | 85 ------------------------------ 2 files changed, 1 insertion(+), 93 deletions(-) diff --git a/src/adapters/audio-speech.ts b/src/adapters/audio-speech.ts index 9b91334..cca66bd 100644 --- a/src/adapters/audio-speech.ts +++ b/src/adapters/audio-speech.ts @@ -9,13 +9,6 @@ import type { const REQUEST_TIMEOUT_MS = 210_000; const QWEN_MODEL = "qwen-audio-3.1-tts-flash"; -// A retired public id, accepted here and never declared (not in builtins.ts, not in models/). -// Why it stays: Cohub's platform config still declares it with the QWEN_MODEL shape, and Voice Studio calls it through -// that declaration; rejecting it would break Voice Studio the moment Cohub deploys this SDK. The gateway accepts it and -// synthesises with the same model as QWEN_MODEL. The payload carries the declared id unchanged. -// Delete it once Voice Studio sends QWEN_MODEL and the platform declaration for this id is gone. -const QWEN_RETIRED_ALIAS = "qwen-audio-3.0-tts-plus"; -const QWEN_MODELS: ReadonlySet = new Set([QWEN_MODEL, QWEN_RETIRED_ALIAS]); const SCENE3_MODEL = "scene3"; const SCENE3_MAX_REFERENCES = 16; const SCENE3_MAX_INSTRUCTIONS = 300; @@ -146,7 +139,7 @@ function scene3InstructionsLength(instructions: string): number { function validateAudioSpeechRequest(input: ResolvedGenerationRequest): void { const { text, audio } = validateCommonContent(input); - if (QWEN_MODELS.has(input.declaration.model)) { + if (input.declaration.model === QWEN_MODEL) { validateQwen(input, text, audio); return; } diff --git a/test/adapters/audio-speech.test.ts b/test/adapters/audio-speech.test.ts index 00039a4..6f5535b 100644 --- a/test/adapters/audio-speech.test.ts +++ b/test/adapters/audio-speech.test.ts @@ -477,91 +477,6 @@ describe("openai.audioSpeech Qwen instructions and seed", () => { }); }); -describe("openai.audioSpeech Qwen retired alias qwen-audio-3.0-tts-plus", () => { - const FLASH = "qwen-audio-3.1-tts-flash"; - const RETIRED_ALIAS = "qwen-audio-3.0-tts-plus"; - - // Cohub's platform config declares the retired id with the same shape as the flash model; the SDK never declares it. - function platformDeclaration(model: string): GenerationModelDeclaration { - const declaration = getBuiltinGenerationModel(FLASH); - if (!declaration) throw new Error(`${FLASH} declaration is unavailable`); - return { ...structuredClone(declaration), model }; - } - - function platformClient(model: string, fetchMock: typeof fetch = vi.fn() as unknown as typeof fetch) { - return createGenerationClient({ - models: [platformDeclaration(model)], - includeBuiltinModels: false, - apiKey: "key", - fetch: fetchMock, - }); - } - - const designWithDirections = (model: string): GenerateRequest => ({ - model, - content: [{ type: "text", text: QWEN_TEXT }], - meta: { voice_prompt: "沉稳清晰的男性播音员声音" }, - parameters: { instructions: " 低声耳语 ", seed: 7 }, - }); - const cloneWithDirections = (model: string): GenerateRequest => ({ - model, - content: [{ type: "text", text: QWEN_TEXT }, audio(REFERENCE_URL, { role: "reference_audio" })], - parameters: { instructions: "请慢一点" }, - }); - - it.each([ - { name: "design with instructions and seed", request: designWithDirections }, - { name: "clone with instructions", request: cloneWithDirections }, - ])("builds the same payload as the flash model, apart from model: $name", async ({ request }) => { - const sent: string[] = []; - const fetchMock = (async (_url: string | URL | Request, init?: RequestInit) => { - sent.push(String(init?.body)); - return routerSuccess(); - }) as typeof fetch; - - await platformClient(RETIRED_ALIAS, fetchMock).generate(request(RETIRED_ALIAS)); - await platformClient(FLASH, fetchMock).generate(request(FLASH)); - - const [aliasBody, flashBody] = sent.map((body) => JSON.parse(body) as Record); - expect(aliasBody?.model).toBe(RETIRED_ALIAS); - expect(flashBody?.model).toBe(FLASH); - expect({ ...aliasBody, model: FLASH }).toEqual(flashBody); - }); - - it("applies the flash validation to the retired alias", () => { - const client = platformClient(RETIRED_ALIAS); - - expect(() => - client.validate({ - model: RETIRED_ALIAS, - content: [{ type: "text", text: "太短" }], - meta: { voice_prompt: "声音" }, - }), - ).toThrow("requires input of at least 15 Unicode code points"); - expect(() => - client.validate({ - model: RETIRED_ALIAS, - content: [{ type: "text", text: QWEN_TEXT }], - meta: { voice_prompt: "声音", preview_text: "旧字段" }, - }), - ).toThrow("Unknown request.meta field: preview_text"); - }); - - it("does not declare the retired alias", () => { - expect(getBuiltinGenerationModel(RETIRED_ALIAS)).toBeNull(); - expect(createGenerationClient({ apiKey: "key" }).getModel(RETIRED_ALIAS)).toBeNull(); - }); - - it.each([ - "qwen-tts", - "qwen-audio-3.0-tts-flash", - ])("still rejects a platform declaration of %s at the audio speech adapter", (model) => { - const client = platformClient(model); - - expect(() => client.validate(qwenDesignRequest({ model }))).toThrow(`Unsupported audio speech model: ${model}`); - }); -}); - describe("openai.audioSpeech scene3 requests", () => { it("sends a built-in voice request with the text exactly as given and the default drama_value", async () => { const { client, calls } = recordingClient(); From 21e73f949a15b7d4c1cc07028e53574c19dee9cb Mon Sep 17 00:00:00 2001 From: LingXuanYin <3546599908@qq.com> Date: Wed, 28 Oct 2026 22:58:56 +0800 Subject: [PATCH 2/2] chore: bump generation SDK version to 0.2.7; the README names qwen-audio-3.1-tts-flash as the Qwen TTS model --- README.md | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f13087e..5802a98 100644 --- a/README.md +++ b/README.md @@ -402,7 +402,7 @@ await client.generate({ ## Text to speech -`qwen-tts`, `qwen-audio-3.0-tts-plus` and `qwen-audio-3.0-tts-flash` are removed in 0.2.0, and `qwen-audio-3.1-tts-plus`, declared in 0.2.0, is withdrawn in 0.2.1. Migrate to `qwen-audio-3.1-tts-flash`; its input must have at least 15 Unicode code points. +The Qwen TTS model is `qwen-audio-3.1-tts-flash`; its input must have at least 15 Unicode code points. Each TTS request accepts exactly one non-empty text block and returns one URL audio block. Choose the model from the requested voice mode and priority before constructing the request: diff --git a/package.json b/package.json index afa1091..2379075 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@neta-art/generation", - "version": "0.2.6", + "version": "0.2.7", "description": "A lightweight multimodal generation SDK with built-in model presets and adapter-based provider calls.", "keywords": [ "ai",