This commit is contained in:
Konturai DevOps
2026-05-02 01:54:00 +05:00
parent 00d9b241f0
commit af2e78ce20
4 changed files with 70 additions and 35 deletions
+19 -1
View File
@@ -107,10 +107,28 @@ class FillerAudioLibrary:
segments: Iterable[tuple[str, str | None]], segments: Iterable[tuple[str, str | None]],
) -> bytes: ) -> bytes:
clip = bytearray() clip = bytearray()
for text, language_code in segments: for i, (text, language_code) in enumerate(segments):
LOGGER.info(
"synthesize_segments: starting segment %s language=%s text=%r",
i,
language_code,
text[:60],
)
segment = await self._synthesize_clip(tts, text, language_code=language_code) segment = await self._synthesize_clip(tts, text, language_code=language_code)
if segment: if segment:
clip.extend(segment) clip.extend(segment)
LOGGER.info(
"synthesize_segments: completed segment %s bytes=%s total_bytes=%s",
i,
len(segment),
len(clip),
)
else:
LOGGER.warning(
"synthesize_segments: segment %s produced NO audio; language=%s",
i,
language_code,
)
return bytes(clip) return bytes(clip)
def _load_from_files(self) -> None: def _load_from_files(self) -> None:
+16
View File
@@ -1868,6 +1868,7 @@ class CallSession:
len(segment_text), len(segment_text),
_preview_text(segment_text), _preview_text(segment_text),
) )
audio_segment_chunk_count = 0
async for audio_chunk in self._tts.synthesize_stream( async for audio_chunk in self._tts.synthesize_stream(
one_shot_text_stream(), one_shot_text_stream(),
language_code=language_code, language_code=language_code,
@@ -1875,6 +1876,7 @@ class CallSession:
self._ensure_generation(epoch) self._ensure_generation(epoch)
if not audio_chunk: if not audio_chunk:
continue continue
audio_segment_chunk_count += 1
audio_chunk_count += 1 audio_chunk_count += 1
audio_byte_count += len(audio_chunk) audio_byte_count += len(audio_chunk)
if not first_audio_seen: if not first_audio_seen:
@@ -1887,6 +1889,20 @@ class CallSession:
) )
self._set_state(SessionState.ASSISTANT_SPEAKING, reason="initial greeting started") self._set_state(SessionState.ASSISTANT_SPEAKING, reason="initial greeting started")
await self.transport.send_audio(audio_chunk) await self.transport.send_audio(audio_chunk)
if audio_segment_chunk_count == 0:
LOGGER.warning(
"realtime session %s initial greeting segment produced NO audio: language=%s",
self.session_id,
language_code,
)
else:
LOGGER.info(
"realtime session %s initial greeting segment completed: language=%s chunks=%s",
self.session_id,
language_code,
audio_segment_chunk_count,
)
await self.transport.flush_audio() await self.transport.flush_audio()
self._ensure_generation(epoch) self._ensure_generation(epoch)
LOGGER.info( LOGGER.info(
+8 -12
View File
@@ -103,24 +103,20 @@ def _initial_greeting_segments() -> tuple[tuple[str, str | None], ...]:
language = str(os.getenv("REALTIME_VOICE_INITIAL_GREETING_LANGUAGE_CODE", "")).strip() or None language = str(os.getenv("REALTIME_VOICE_INITIAL_GREETING_LANGUAGE_CODE", "")).strip() or None
return ((raw, language),) return ((raw, language),)
ru_text = str( ru_text = (
os.getenv( str(os.getenv("REALTIME_VOICE_INITIAL_GREETING_RU_TEXT") or "").strip()
"REALTIME_VOICE_INITIAL_GREETING_RU_TEXT", or (
(
"Здравствуйте! Меня зовут Айнур, я эй-ай-ассистент компании ДиджиОпс. " "Здравствуйте! Меня зовут Айнур, я эй-ай-ассистент компании ДиджиОпс. "
"Как я могу к вам обращаться и на каком языке вам удобнее продолжить: на русском или на казахском?" "Как я могу к вам обращаться и на каком языке вам удобнее продолжить: на русском или на казахском?"
),
) )
).strip() )
kk_text = str( kk_text = (
os.getenv( str(os.getenv("REALTIME_VOICE_INITIAL_GREETING_KK_TEXT") or "").strip()
"REALTIME_VOICE_INITIAL_GREETING_KK_TEXT", or (
(
"Сәлеметсіз бе! Менің атым Айнұр, мен ДиджиОпс компаниясының эй-ай ассистентімін. " "Сәлеметсіз бе! Менің атым Айнұр, мен ДиджиОпс компаниясының эй-ай ассистентімін. "
"Сізге қалай жүгінсем болады және қай тілде сөйлескен ыңғайлы: орысша ма, қазақша ма?" "Сізге қалай жүгінсем болады және қай тілде сөйлескен ыңғайлы: орысша ма, қазақша ма?"
),
) )
).strip() )
segments: list[tuple[str, str | None]] = [] segments: list[tuple[str, str | None]] = []
if ru_text: if ru_text:
segments.append((ru_text, "ru")) segments.append((ru_text, "ru"))
+23 -18
View File
@@ -192,7 +192,11 @@ class ElevenLabsTTS(BaseTTS):
if not self._voice_id: if not self._voice_id:
raise RuntimeError("ELEVENLABS_TTS_VOICE_ID is required for ElevenLabs TTS") raise RuntimeError("ELEVENLABS_TTS_VOICE_ID is required for ElevenLabs TTS")
effective_language_code = (str(language_code).strip() if language_code else "") or self._language_code # If language_code is explicitly provided, use it.
# If it's None, use the instance default self._language_code.
# To trigger auto-detection, language_code should be an empty string or the default should be None.
effective_language_code = language_code if language_code is not None else self._language_code
if self._resolved_transport_mode() == "http_stream": if self._resolved_transport_mode() == "http_stream":
async for audio_chunk in self._synthesize_http_stream( async for audio_chunk in self._synthesize_http_stream(
text_stream, text_stream,
@@ -234,7 +238,7 @@ class ElevenLabsTTS(BaseTTS):
self._output_format, self._output_format,
self._provider_sample_rate_hz, self._provider_sample_rate_hz,
self._target_sample_rate_hz, self._target_sample_rate_hz,
language_code, language_code or "auto",
self._auto_mode, self._auto_mode,
) )
try: try:
@@ -297,13 +301,14 @@ class ElevenLabsTTS(BaseTTS):
if audio_chunk: if audio_chunk:
yield audio_chunk yield audio_chunk
if self._is_error_payload(payload): if self._is_error_payload(payload):
error_detail = self._format_error_payload(payload)
LOGGER.error( LOGGER.error(
"ElevenLabs TTS error payload: voice_id=%s model=%s payload=%s", "ElevenLabs TTS error payload: voice_id=%s model=%s error=%s",
self._voice_id, self._voice_id,
self._model_id, self._model_id,
self._format_error_payload(payload), error_detail,
) )
raise RuntimeError(self._format_error_payload(payload)) raise RuntimeError(error_detail)
if bool(payload.get("isFinal")) or bool(payload.get("is_final")): if bool(payload.get("isFinal")) or bool(payload.get("is_final")):
LOGGER.info( LOGGER.info(
"ElevenLabs TTS final payload: voice_id=%s model=%s chunks=%s " "ElevenLabs TTS final payload: voice_id=%s model=%s chunks=%s "
@@ -327,9 +332,9 @@ class ElevenLabsTTS(BaseTTS):
except asyncio.TimeoutError as exc: except asyncio.TimeoutError as exc:
raise RuntimeError("ElevenLabs TTS WebSocket request timed out") from exc raise RuntimeError("ElevenLabs TTS WebSocket request timed out") from exc
except WebSocketException as exc: except WebSocketException as exc:
raise RuntimeError("ElevenLabs TTS WebSocket stream failed") from exc raise RuntimeError(f"ElevenLabs TTS WebSocket stream failed: {exc}") from exc
except OSError as exc: except OSError as exc:
raise RuntimeError("ElevenLabs TTS WebSocket connection failed") from exc raise RuntimeError(f"ElevenLabs TTS WebSocket connection failed: {exc}") from exc
async def _synthesize_http_stream( async def _synthesize_http_stream(
self, self,
@@ -364,7 +369,7 @@ class ElevenLabsTTS(BaseTTS):
self._output_format, self._output_format,
self._provider_sample_rate_hz, self._provider_sample_rate_hz,
self._target_sample_rate_hz, self._target_sample_rate_hz,
language_code, language_code or "auto",
) )
try: try:
@@ -389,9 +394,9 @@ class ElevenLabsTTS(BaseTTS):
async with session.post(url, headers=headers, json=payload) as response: async with session.post(url, headers=headers, json=payload) as response:
if response.status >= 400: if response.status >= 400:
payload_text = await response.text() payload_text = await response.text()
raise RuntimeError( error_msg = f"ElevenLabs TTS HTTP {response.status}: {payload_text[:300]}"
f"ElevenLabs TTS HTTP {response.status}: {payload_text[:300]}" LOGGER.error(error_msg)
) raise RuntimeError(error_msg)
async for audio_chunk in response.content.iter_chunked(4096): async for audio_chunk in response.content.iter_chunked(4096):
if not audio_chunk: if not audio_chunk:
continue continue
@@ -439,7 +444,7 @@ class ElevenLabsTTS(BaseTTS):
except asyncio.TimeoutError as exc: except asyncio.TimeoutError as exc:
raise RuntimeError("ElevenLabs TTS HTTP stream timed out") from exc raise RuntimeError("ElevenLabs TTS HTTP stream timed out") from exc
except aiohttp.ClientError as exc: except aiohttp.ClientError as exc:
raise RuntimeError("ElevenLabs TTS HTTP stream failed") from exc raise RuntimeError(f"ElevenLabs TTS HTTP stream failed: {exc}") from exc
LOGGER.info( LOGGER.info(
"ElevenLabs TTS HTTP stream completed: voice_id=%s model=%s requests=%s chunks=%s " "ElevenLabs TTS HTTP stream completed: voice_id=%s model=%s requests=%s chunks=%s "
@@ -462,9 +467,9 @@ class ElevenLabsTTS(BaseTTS):
"sync_alignment": "false", "sync_alignment": "false",
"apply_text_normalization": self._apply_text_normalization, "apply_text_normalization": self._apply_text_normalization,
} }
effective_language = (str(language_code).strip() if language_code else "") or self._language_code # Use provided language_code or fallback to None for auto-detection
if effective_language: if language_code:
query["language_code"] = effective_language query["language_code"] = language_code
encoded_voice_id = quote(self._voice_id, safe="") encoded_voice_id = quote(self._voice_id, safe="")
return f"{self._ws_base}/v1/text-to-speech/{encoded_voice_id}/stream-input?{urlencode(query)}" return f"{self._ws_base}/v1/text-to-speech/{encoded_voice_id}/stream-input?{urlencode(query)}"
@@ -482,9 +487,9 @@ class ElevenLabsTTS(BaseTTS):
"voice_settings": self._voice_settings, "voice_settings": self._voice_settings,
"apply_text_normalization": self._apply_text_normalization, "apply_text_normalization": self._apply_text_normalization,
} }
effective_language = (str(language_code).strip() if language_code else "") or self._language_code # Use provided language_code or fallback to None for auto-detection
if effective_language: if language_code:
payload["language_code"] = effective_language payload["language_code"] = language_code
return payload return payload
def _resolved_transport_mode(self) -> str: def _resolved_transport_mode(self) -> str: