fix: register bundled TTS providers and route overrides correctly (#62846) (thanks @stainlu)

* fix(microsoft,elevenlabs): add enabledByDefault so speech providers register at runtime

* fix(tts): route generic directive tokens to the explicitly declared provider

Addresses the P2 Codex review on #62846 that flagged auto-enabling
ElevenLabs as a product regression for MiniMax users. Both providers
claim the generic `speed` token, and parseTtsDirectives walked
providers in autoSelectOrder with first-match-wins, so inputs like
`[[tts:provider=minimax speed=1.2]]` silently routed speed to
providerOverrides.elevenlabs once elevenlabs participated in every
parse pass.

The parser now pre-scans for `provider=` (honoring legacy last-wins
semantics) and routes generic tokens with the declared provider tried
first, falling back to autoSelectOrder when it doesn't handle the key.
Token order inside the directive no longer matters: `speed=1.2` before
or after `provider=minimax` both resolve to MiniMax.

Adds a regression test suite covering the exact ElevenLabs/MiniMax
speed collision plus fallback, mixed-token, last-wins, and
allowProvider-disabled cases. parseTtsDirectives had no prior test
coverage.

* fix(tts): prefer active provider for generic directives

* fix: register bundled TTS providers safely (#62846) (thanks @stainlu)

* fix: use exported TTS SDK seam (#62846) (thanks @stainlu)

---------

Co-authored-by: Ayaan Zaidi <hi@obviy.us>
This commit is contained in:
stain lu
2026-04-16 17:56:38 +08:00
committed by GitHub
parent ecfaf64526
commit 6ea3cddf0d
7 changed files with 210 additions and 21 deletions

View File

@@ -1036,12 +1036,14 @@ export async function maybeApplyTtsToPayload(params: {
return params.payload;
}
const config = resolveTtsConfig(params.cfg);
const activeProvider = getTtsProvider(config, prefsPath);
const reply = resolveSendableOutboundReplyParts(params.payload);
const text = reply.text;
const directives = parseTtsDirectives(text, config.modelOverrides, {
cfg: params.cfg,
providerConfigs: config.providerConfigs,
preferredProviderId: activeProvider,
});
if (directives.warnings.length > 0) {
logVerbose(`TTS: ignored directive overrides (${directives.warnings.join("; ")})`);
@@ -1049,9 +1051,8 @@ export async function maybeApplyTtsToPayload(params: {
if (isVerbose()) {
const effectiveProvider = directives.overrides?.provider
? (canonicalizeSpeechProviderId(directives.overrides.provider, params.cfg) ??
getTtsProvider(config, prefsPath))
: getTtsProvider(config, prefsPath);
? (canonicalizeSpeechProviderId(directives.overrides.provider, params.cfg) ?? activeProvider)
: activeProvider;
logVerbose(
`TTS: auto mode enabled (${autoMode}), channel=${params.channel}, selected provider=${effectiveProvider}, config.provider=${config.provider}, config.providerSource=${config.providerSource}`,
);