Skip to content

fix: support Qwen-Audio-TTS in DashScope provider - #10495

Open
C10H14N2O5 wants to merge 2 commits into
AstrBotDevs:masterfrom
C10H14N2O5:fix/10494-dashscope-qwen-audio-tts
Open

C10H14N2O5 wants to merge 2 commits into
AstrBotDevs:masterfrom
C10H14N2O5:fix/10494-dashscope-qwen-audio-tts

Conversation

@C10H14N2O5

@C10H14N2O5 C10H14N2O5 commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

TL;DR

  • Route Qwen-Audio-TTS models through the existing SpeechSynthesizer implementation instead of MultiModalConversation.
  • Add an optional, instance-local WebSocket endpoint setting while preserving the SDK default when it is blank.
  • Add Chinese, Japanese, English, and Russian translations, regression coverage, and verification against the real Bailian service.

Background

Fixes #10494.

Configuring qwen-audio-3.0-tts-plus with its cloned voice in the built-in DashScope TTS provider returned HTTP 400 with InvalidParameter: url error, please check url.

The existing model predicate matched any name starting with qwen and containing tts. This also routed Qwen-Audio-TTS through MultiModalConversation, although that series supports the dashscope.audio.tts_v2.SpeechSynthesizer interface. The existing CosyVoice branch already provides the required synthesis implementation.

Reference: Qwen-Audio-TTS Python SDK documentation.

Modifications / 改动点

Model routing

Exclude the qwen-audio- prefix from the existing multimodal model predicate and reuse the existing SpeechSynthesizer branch. Preserve the routing of other Qwen TTS models and CosyVoice.

Model family Synthesis interface
Qwen-Audio-TTS, including qwen-audio-3.0-tts-plus Existing SpeechSynthesizer branch
Existing Qwen3-TTS / Qwen-TTS Existing MultiModalConversation branch
CosyVoice Existing SpeechSynthesizer branch

Voice IDs, WAV output at 24 kHz, timeout handling, and existing error handling are preserved. No provider or synthesis method is added or renamed.

Optional WebSocket endpoint

Add dashscope_tts_websocket_url to the existing configuration template and field metadata. The setting defaults to an empty string. Missing, null, empty, or whitespace-only values use the SDK default endpoint. A configured URL is passed to SpeechSynthesizer(url=...) without modifying the global endpoint or affecting other instances or Qwen3-TTS calls.

Example workspace-specific endpoint:

wss://{WorkspaceId}.cn-beijing.maas.aliyuncs.com/api-ws/v1/inference

Replace {WorkspaceId} with the actual workspace ID. The field uses the existing dynamic configuration form, with its description and hint translated into Chinese, Japanese, English, and Russian.

Regression tests

Add tests/test_dashscope_tts.py with 26 parameterized cases covering:

  • Qwen-Audio Plus, Flash, and uppercase model routing, including cloned voice IDs.
  • Existing Qwen3-TTS / Qwen-TTS request parameters and Base64 or URL audio responses.
  • CosyVoice routing, voices, output format, and timeouts.
  • Default and custom endpoint resolution and isolation between synthesizer instances.
  • Output file content and extension, empty audio, and SDK exceptions.

Tests mock SDK synthesis calls and use temporary output directories. Endpoint isolation tests construct real SDK instances while replacing their synthesis calls to avoid network access.

No new dependencies or backend API routes or request/response schemas are introduced. Existing navigation and page structure are preserved.

  • This is NOT a breaking change.

Screenshots or Test Results / 运行截图或测试结果

Verification steps

  1. Add or edit the built-in DashScope TTS provider in model management.
  2. Configure a Beijing-region API key, qwen-audio-3.0-tts-plus, and its cloned voice ID.
  3. Leave the WebSocket endpoint blank to use the SDK default, or enter the workspace-specific endpoint when needed.
  4. Save and run the model test. Confirm speech synthesis succeeds.
  5. Switch between Chinese, Japanese, English, and Russian and check the new field's description and hint.

The issue reporter successfully tested the real Bailian model on an isolated local AstrBot instance using the patched provider:

Model dashscope_tts test passed, latency 2284 ms

This latency is one observed test result, not a performance benchmark. Automated regression coverage for other models uses mocked synthesis calls rather than live service requests.

Successful model test GIF

QQ20261011-015135

Chinese localization

QQ20261011-015231

Japanese localization

QQ20261011-015301

English localization

QQ20261011-015245

Russian localization

QQ20261011-015252

Automated validation

From the repository root:

.venv\Scripts\python.exe -m pytest tests/test_dashscope_tts.py tests/test_dashscope_embedding_source.py tests/test_bailian_rerank_source.py -q --basetemp data/issue-10494-pytest

Result: 55 passed, including 26 new TTS cases and 29 existing related tests. Two dependency deprecation warnings were reported. The repository-local temporary directory avoids a sandbox temporary-directory permission issue.

From dashboard/:

node scripts/subset-mdi-font.mjs
node node_modules/vue-tsc/bin/vue-tsc.js --noEmit
node node_modules/vite/bin/vite.js build

Result: passed, including TypeScript checks and the production build. These execute the build script's steps directly because the local pnpm runtime encountered a sandbox symlink permission issue.

From the repository root:

.venv\Scripts\ruff.exe format .
.venv\Scripts\ruff.exe check .
git diff --check

Results: 515 files left unchanged, All checks passed, and no whitespace errors.

Runtime smoke checks passed for the dashboard page, login, provider configuration schema, and the rebuilt frontend entry bundle. All four language bundles contain the new translations and were served with HTTP 200.


Checklist / 检查清单

Summary by Sourcery

Support Qwen-Audio-TTS synthesis in the DashScope provider while preserving existing model behavior and configuration defaults.

New Features:

  • Add an optional instance-local WebSocket endpoint setting for DashScope SpeechSynthesizer-based TTS models, with localized configuration metadata.

Bug Fixes:

  • Route Qwen-Audio-TTS models through the SpeechSynthesizer path so cloned-voice synthesis works correctly.

Enhancements:

  • Ensure per-provider API keys override conflicting custom or global authorization settings without affecting other synthesizer instances.

Tests:

  • Add regression coverage for model routing, endpoint resolution and isolation, authorization handling, audio output, and synthesis failures.

@C10H14N2O5
C10H14N2O5 marked this pull request as ready for review October 10, 2026 17:58

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 2 issues

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="astrbot/core/provider/sources/dashscope_tts.py" line_range="177" />
<code_context>
+        return (
+            "tts" in model_lower
+            and model_lower.startswith("qwen")
+            and not model_lower.startswith("qwen-audio-")
+        )
</code_context>
<issue_to_address>
**Requests use the wrong account**

When multiple enabled providers have different API keys and Qwen-Audio synthesis is requested from one after both are initialized, `SpeechSynthesizer` omits the instance's `chosen_api_key`, so it uses the globally overwritten `dashscope.api_key` and authenticates with the wrong account, failing or consuming that account's quota.

Pass the provider instance's `chosen_api_key` when constructing `SpeechSynthesizer`.
</issue_to_address>

### Comment 2
<location path="astrbot/core/config/default.py" line_range="1886" />
<code_context>
                         "api_key": "",
                         "model": "cosyvoice-v1",
                         "dashscope_tts_voice": "loongstella",
+                        "dashscope_tts_websocket_url": "",
                         "timeout": "20",
                     },
</code_context>
<issue_to_address>
**Existing providers hide endpoint setting**

When a deployment upgrades with an existing DashScope TTS provider source that predates this field, `selectProviderSource` passes the stored source through `ensureProviderSourceDefaults`, which does not add this new key, and `advancedSourceConfig` only exposes keys already present on that source. Existing DashScope TTS providers therefore cannot configure the custom endpoint in the dashboard.

When selecting an existing provider source, populate missing fields from its provider template without overwriting stored values.
</issue_to_address>

Sourcery assessment

Approval pending. 2 findings to address first.

Blocking findings: astrbot/core/provider/sources/dashscope_tts.py:177, astrbot/core/config/default.py:1886


Sourcery is free for open source - if you like our reviews please consider sharing them ✨

Comment thread astrbot/core/provider/sources/dashscope_tts.py
Comment thread astrbot/core/config/default.py

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] DashScope TTS 无法调用 qwen-audio-3.0-tts-plus 模型,返回 InvalidParameter: url error

1 participant