음성 재생과 운영 배포 정리
This commit is contained in:
parent
8ed185ce6c
commit
ac7db95542
1020 changed files with 46863 additions and 2175 deletions
|
|
@ -21,11 +21,12 @@ from __future__ import annotations
|
|||
import re
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import AsyncIterator, Optional
|
||||
from typing import Any, AsyncIterator, Mapping, Optional
|
||||
|
||||
import httpx
|
||||
|
||||
from ..config import settings
|
||||
from ..paths import repo_root, repo_path
|
||||
|
||||
# ════════════════════════════════════════════════════════════════════════════
|
||||
# OpenAI 음성 엔드포인트/모델 상수
|
||||
|
|
@ -50,10 +51,9 @@ TTS_RESPONSE_FORMAT = "mp3"
|
|||
# End-of-turn readiness default for cascaded STT providers.
|
||||
EOT_SILENCE_THRESHOLD_MS = 1200
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parents[4]
|
||||
POC_SAMPLE_TTS_PRESET = "soft-young-fem"
|
||||
POC_SAMPLE_TTS_DEFAULT_DIR = (
|
||||
_REPO_ROOT / "docs" / "voice-art" / "p1-seoyeon-higgs-v3-20260627"
|
||||
repo_path("docs", "voice-art", "p1-seoyeon-higgs-v3-20260627")
|
||||
)
|
||||
POC_SAMPLE_TTS_CHUNK_SIZE = 4096
|
||||
_POC_SAMPLE_TTS_DEFAULT_SAMPLE = "p1_seoyeon_01_depressed_slow"
|
||||
|
|
@ -190,6 +190,51 @@ def resolve_voice(
|
|||
)
|
||||
|
||||
|
||||
def resolve_voice_from_map(
|
||||
*,
|
||||
provider: str,
|
||||
voice_id: str,
|
||||
base_params: Mapping[str, Any] | None,
|
||||
persona_code: Optional[str] = None,
|
||||
) -> VoicePreset | None:
|
||||
"""DB persona_voice_map row -> live OpenAI VoicePreset.
|
||||
|
||||
provider-agnostic rows are allowed in the catalog, but this service only
|
||||
knows how to send OpenAI TTS. Unsupported providers return None so callers
|
||||
can fall back to the existing preset resolver.
|
||||
"""
|
||||
if provider.strip().lower() != "openai":
|
||||
return None
|
||||
|
||||
fallback = resolve_voice(persona_code=persona_code)
|
||||
params = dict(base_params or {})
|
||||
preset = _clean_optional_text(params.get("preset")) or fallback.preset
|
||||
mapped_voice = _clean_optional_text(params.get("openai_voice"))
|
||||
voice_id_value = _clean_optional_text(voice_id)
|
||||
if not mapped_voice and voice_id_value in _OPENAI_VOICES:
|
||||
mapped_voice = voice_id_value
|
||||
if not mapped_voice:
|
||||
mapped_voice = PRESET_TO_OPENAI_VOICE.get(preset, fallback.openai_voice)
|
||||
if mapped_voice not in _OPENAI_VOICES:
|
||||
mapped_voice = fallback.openai_voice
|
||||
if mapped_voice not in _OPENAI_VOICES:
|
||||
mapped_voice = DEFAULT_OPENAI_VOICE
|
||||
|
||||
rate = PRESET_RATE.get(preset, fallback.rate)
|
||||
if "rate" in params:
|
||||
try:
|
||||
rate = float(params["rate"])
|
||||
except (TypeError, ValueError):
|
||||
rate = fallback.rate
|
||||
|
||||
return VoicePreset(
|
||||
preset=preset,
|
||||
openai_voice=mapped_voice,
|
||||
rate=rate,
|
||||
instructions=_clean_optional_text(params.get("instructions")),
|
||||
)
|
||||
|
||||
|
||||
# 비언어 지문 패턴: (…)·(…)·[…]·【…】. 내담자 발화의 무대지시(고개 끄덕/한숨/침묵 등).
|
||||
_STAGE_DIRECTION_RE = re.compile(r"[\((\[【][^\))\]】]*[\))\]】]")
|
||||
|
||||
|
|
@ -291,7 +336,7 @@ class VoiceService:
|
|||
)
|
||||
sample_dir = Path(sample_dir_value)
|
||||
if not sample_dir.is_absolute():
|
||||
sample_dir = _REPO_ROOT / sample_dir
|
||||
sample_dir = repo_root() / sample_dir
|
||||
self._poc_sample_tts_dir = sample_dir
|
||||
self._client: Optional[httpx.AsyncClient] = None
|
||||
|
||||
|
|
@ -507,6 +552,13 @@ def _nonnegative_int(value: object) -> int:
|
|||
return 0
|
||||
|
||||
|
||||
def _clean_optional_text(value: object) -> str | None:
|
||||
if value is None:
|
||||
return None
|
||||
text = str(value).strip()
|
||||
return text or None
|
||||
|
||||
|
||||
# 앱 전역 싱글톤 (main lifespan 이 startup/shutdown — Foundation 이 관리하거나
|
||||
# 라우트가 lazy 사용). engine_client 패턴과 동일.
|
||||
voice_service = VoiceService()
|
||||
|
|
@ -521,6 +573,7 @@ __all__ = [
|
|||
"VoiceService",
|
||||
"voice_service",
|
||||
"resolve_voice",
|
||||
"resolve_voice_from_map",
|
||||
"build_tts_payload",
|
||||
"assess_end_of_turn",
|
||||
"EOT_SILENCE_THRESHOLD_MS",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue