fix: integrate Azure TTS with shared selector

This commit is contained in:
calesthio
2026-08-13 09:27:32 -07:00
parent a3c45aa3bf
commit 078eb7eecd
3 changed files with 88 additions and 11 deletions

View File

@@ -19,7 +19,7 @@ import os
import time
from pathlib import Path
from typing import Any
from xml.sax.saxutils import escape
from xml.sax.saxutils import escape, quoteattr
from tools.base_tool import (
BaseTool,
@@ -201,23 +201,21 @@ class AzureTTS(BaseTool):
return self.RECOMMENDED_VOICES.get(voice.lower(), voice)
def _build_ssml(self, inputs: dict[str, Any], voice: str) -> str:
locale = inputs.get("locale", "en-US")
rate = inputs.get("rate", "0%")
pitch = inputs.get("pitch", "0%")
locale = str(inputs.get("locale", "en-US"))
rate = str(inputs.get("rate", "0%"))
pitch = str(inputs.get("pitch", "0%"))
style = inputs.get("style")
text = escape(inputs["text"])
inner = f'<prosody rate="{escape(rate)}" pitch="{escape(pitch)}">{text}</prosody>'
inner = f"<prosody rate={quoteattr(rate)} pitch={quoteattr(pitch)}>{text}</prosody>"
if style:
inner = (
f'<mstts:express-as style="{escape(style)}">{inner}</mstts:express-as>'
)
inner = f"<mstts:express-as style={quoteattr(str(style))}>{inner}</mstts:express-as>"
return (
f'<speak version="1.0" '
f'xmlns="http://www.w3.org/2001/10/synthesis" '
f'xmlns:mstts="https://www.w3.org/2001/mstts" '
f'xml:lang="{locale}">'
f'<voice name="{voice}">{inner}</voice></speak>'
f"xml:lang={quoteattr(locale)}>"
f"<voice name={quoteattr(voice)}>{inner}</voice></speak>"
)
def execute(self, inputs: dict[str, Any]) -> ToolResult:

View File

@@ -188,7 +188,7 @@ class TTSSelector(BaseTool):
if tool is None:
return ToolResult(success=False, error="No TTS provider available.")
result = tool.execute(inputs)
result = tool.execute(self._adapt_inputs(tool, inputs))
if result.success:
result.data.setdefault("selected_tool", tool.name)
result.data["selected_provider"] = tool.provider
@@ -202,6 +202,37 @@ class TTSSelector(BaseTool):
]
return result
@staticmethod
def _adapt_inputs(tool: BaseTool, inputs: dict[str, Any]) -> dict[str, Any]:
"""Translate capability-level controls to provider-native inputs."""
adapted = dict(inputs)
if tool.name != "azure_tts":
return adapted
if inputs.get("voice_id") and not inputs.get("voice"):
adapted["voice"] = inputs["voice_id"]
speed = inputs.get("speaking_rate", inputs.get("speed"))
if speed is not None and "rate" not in inputs:
percent = round((float(speed) - 1.0) * 100)
adapted["rate"] = f"{percent:+d}%" if percent else "0%"
pitch = inputs.get("pitch")
if isinstance(pitch, (int, float)):
adapted["pitch"] = f"{pitch:+g}st" if pitch else "0%"
# The selector's numeric style is ElevenLabs-specific. Azure's style
# is a named express-as value such as "calm" or "newscast".
if not isinstance(inputs.get("style"), str):
adapted.pop("style", None)
output_format = str(inputs.get("output_format", ""))
if output_format.startswith("mp3"):
adapted["output_format"] = "mp3"
elif output_format.startswith(("wav", "riff", "pcm")):
adapted["output_format"] = "wav"
return adapted
def _select_best_tool(
self,
inputs: dict[str, Any],