Bugfixes, Verbesserungen und Mixed-Language-Support
Bugfixes: - Abkürzungen (z.B., d.h., Dr., Prof.) werden nicht mehr als Satzenden erkannt (_ABBREV_MASK_RE) - Multilingual-Import: except Exception → except (ImportError, ModuleNotFoundError) - tts_agent: ReAct-Schleife auf max. 10 Iterationen begrenzt, model_dump → explizites Dict - tts_service: audio_device=None fällt auf 'pulse' zurück - JSON-Fehlerbehandlung für --pronunciation-dict mit aussagekräftiger Meldung - PlaybackWorker: Audio-Device wird vor Stream-Start via sd.query_devices() geprüft - mcp_adapter: Fehlerbehandlung für HTTP-Fehler, Timeout erhöht, session_id ergänzt - tts_agent: Health-Check beim Start, --speed/--first-chunk-len Validierung Neue Features: - Gemischtsprachige Texte: [en]...[/en]-Markierungen für per-Segment language_id - strip_markdown(): entfernt Markdown-Formatierung vor der Synthese (--no-strip-markdown) - Emoji-Entfernung in clean_raw_text() via unicodedata - Pause/Resume: request_pause()/request_resume(), POST /pause, POST /resume, MCP-Tools - Neue Einheiten: °C, °F, kWh, kW, W, V, A, J, kPa, bar, m², m³, m/s, rpm - number_to_words_de/en bis Milliarden - DEFAULT_PRONUNCIATION_DE erweitert (GitHub, YouTube, LinkedIn, Wi-Fi, iPhone, ChatGPT, …) - NON_SPELLED_ACRONYMS erweitert (USB, CPU, GPU, API, CEO, HTML, …) - Nummerierte Listen als separate Chunks behandelt - Modell-Warmup via TTS_PRELOAD_LANG Env-Variable - requirements.txt: Upper-Bounds für fastapi und uvicorn Dokumentation: CLAUDE.md, README.md, BEDIENUNGSANLEITUNG.md vollständig aktualisiert Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
d1971049ce
commit
34a34907a8
8 changed files with 778 additions and 114 deletions
|
|
@ -43,6 +43,16 @@ mcp = FastMCP(
|
|||
# Tools
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
def _raise_for_status(r: httpx.Response) -> None:
|
||||
"""Wirft einen klaren Fehler bei HTTP-4xx/5xx statt rohem httpx-Fehler."""
|
||||
try:
|
||||
r.raise_for_status()
|
||||
except httpx.HTTPStatusError as exc:
|
||||
raise RuntimeError(
|
||||
f"TTS-Service antwortet mit HTTP {exc.response.status_code}: {exc.response.text[:200]}"
|
||||
) from exc
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def speak(
|
||||
text: str,
|
||||
|
|
@ -50,6 +60,7 @@ async def speak(
|
|||
voice: str | None = None,
|
||||
interrupt: bool = False,
|
||||
speed: float = 1.0,
|
||||
session_id: str | None = None,
|
||||
) -> dict:
|
||||
"""Text als Sprache ausgeben.
|
||||
|
||||
|
|
@ -57,32 +68,52 @@ async def speak(
|
|||
satzweise und beginnt sofort mit der Wiedergabe.
|
||||
|
||||
Args:
|
||||
text: Auszugebender Text (max. 4000 Zeichen).
|
||||
lang: Sprachcode, z. B. 'de', 'en', 'fr'. Standard: 'de'.
|
||||
voice: Optionaler Pfad zu einer WAV-Referenzdatei (10–30s) für
|
||||
Voice Cloning.
|
||||
interrupt: True = laufende Ausgabe sofort unterbrechen und diesen
|
||||
Text vorgezogen abspielen.
|
||||
speed: Wiedergabegeschwindigkeit (0.5–2.0). Pitch bleibt gleich.
|
||||
text: Auszugebender Text (max. 4000 Zeichen).
|
||||
lang: Sprachcode, z. B. 'de', 'en', 'fr'. Standard: 'de'.
|
||||
voice: Optionaler Pfad zu einer WAV-Referenzdatei (10–30s) für
|
||||
Voice Cloning.
|
||||
interrupt: True = laufende Ausgabe sofort unterbrechen und diesen
|
||||
Text vorgezogen abspielen.
|
||||
speed: Wiedergabegeschwindigkeit (0.5–2.0). Pitch bleibt gleich.
|
||||
session_id: Optionale Session-ID für Job-Tracking im TTS-Service.
|
||||
"""
|
||||
async with httpx.AsyncClient(timeout=15) as client:
|
||||
async with httpx.AsyncClient(timeout=30) as client:
|
||||
r = await client.post(f"{TTS_URL}/speak", json={
|
||||
"text": text,
|
||||
"lang": lang,
|
||||
"voice": voice,
|
||||
"interrupt": interrupt,
|
||||
"speed": speed,
|
||||
"session_id": session_id,
|
||||
})
|
||||
r.raise_for_status()
|
||||
_raise_for_status(r)
|
||||
return r.json()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def stop() -> dict:
|
||||
"""Laufende Sprachausgabe sofort stoppen und Warteschlange leeren."""
|
||||
async with httpx.AsyncClient(timeout=5) as client:
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
r = await client.post(f"{TTS_URL}/stop")
|
||||
r.raise_for_status()
|
||||
_raise_for_status(r)
|
||||
return r.json()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def pause() -> dict:
|
||||
"""Laufende Sprachausgabe pausieren (ohne Datenverlust). Mit resume() fortsetzen."""
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
r = await client.post(f"{TTS_URL}/pause")
|
||||
_raise_for_status(r)
|
||||
return r.json()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def resume() -> dict:
|
||||
"""Pausierte Sprachausgabe fortsetzen."""
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
r = await client.post(f"{TTS_URL}/resume")
|
||||
_raise_for_status(r)
|
||||
return r.json()
|
||||
|
||||
|
||||
|
|
@ -93,18 +124,18 @@ async def get_status() -> dict:
|
|||
Gibt zurück: laufender Job (mit Chunk-Fortschritt), Queue-Länge und
|
||||
die letzten abgeschlossenen Jobs.
|
||||
"""
|
||||
async with httpx.AsyncClient(timeout=5) as client:
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
r = await client.get(f"{TTS_URL}/status")
|
||||
r.raise_for_status()
|
||||
_raise_for_status(r)
|
||||
return r.json()
|
||||
|
||||
|
||||
@mcp.tool()
|
||||
async def list_voices() -> dict:
|
||||
"""Unterstützte Sprachen und Hinweise zu Voice Cloning abfragen."""
|
||||
async with httpx.AsyncClient(timeout=5) as client:
|
||||
async with httpx.AsyncClient(timeout=10) as client:
|
||||
r = await client.get(f"{TTS_URL}/voices")
|
||||
r.raise_for_status()
|
||||
_raise_for_status(r)
|
||||
return r.json()
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue