"""음성 캐스케이드 — Deepgram/OpenAI STT + OpenAI/Higgs TTS 어댑터. MASTERPLAN '음성 필수'(한신대 요구) / DESIGN_CONCEPT §5.2(음성 오브 4상태) / §4.3(립싱크 RMS): STT : Deepgram 실시간 WebSocket 또는 OpenAI 배치 전사. 학습자 음성 → 텍스트. TTS : OpenAI /v1/audio/speech 또는 로컬 Higgs v3. 내담자 텍스트 → 음성. 설계 원칙(이 모듈의 경계): - 순수 어댑터: httpx 로 OpenAI 음성 엔드포인트만 호출한다. 상담 로직(orchestrator)·상태머신은 호출부(routes/voice.py)가 조립한다. 여기는 "오디오↔텍스트" 변환 + voice preset 매핑만. - API 키 없으면 명확히 degraded: is_available()=False, 호출 시 VoiceUnavailable. 절대 크래시·무한대기 금지(라우트가 503/close 로 변환). - 립싱크 힌트: TTS 오디오 청크를 흘리며 RMS(진폭) 힌트를 같이 산출(설계 §4.3 — viseme 정밀 매칭 안 함, RMS 1채널). PCM 디코딩 의존성 없이 바이트 에너지 근사로 임시 RMS 추정. 페르소나 voice preset(persona/*.json voice.preset, 설계 §4.6) → OpenAI voice 매핑은 PRESET_TO_OPENAI_VOICE 테이블이 흡수. 새 preset 추가는 이 테이블만 손대면 된다. """ from __future__ import annotations import asyncio import json import re import time from collections.abc import Awaitable, Callable from dataclasses import dataclass, field from pathlib import Path from typing import Any, AsyncIterator, Mapping, Optional from urllib.parse import urlencode import httpx from websockets.asyncio.client import connect as websocket_connect from ..config import settings from .voice_runtime import voice_runtime_metrics from ..paths import repo_root, repo_path # ════════════════════════════════════════════════════════════════════════════ # OpenAI 음성 엔드포인트/모델 상수 # ════════════════════════════════════════════════════════════════════════════ OPENAI_BASE_URL = "https://api.openai.com/v1" STT_ENDPOINT = "/audio/transcriptions" TTS_ENDPOINT = "/audio/speech" HIGGS_TTS_ENDPOINT = "/tts" MELOTTS_TTS_ENDPOINT = "/tts" MELOTTS_TTS_MODEL = "melotts-korean" DEEPGRAM_STT_URL = "wss://api.deepgram.com/v1/listen" DEEPGRAM_STT_MODEL = "nova-3" # STT 모델: gpt-4o-transcribe(고품질) — 미가용 폴백은 whisper-1. STT_MODEL = "gpt-4o-transcribe" STT_MODEL_FALLBACK = "whisper-1" # TTS 모델: gpt-4o-mini-tts(저지연·표현력) — 폴백 tts-1. TTS_MODEL = "gpt-4o-mini-tts" TTS_MODEL_FALLBACK = "tts-1" HIGGS_TTS_MODEL = "higgs-audio-v3-tts-4b" # 전사 언어 힌트(상담은 한국어). OpenAI 는 ISO-639-1. STT_LANGUAGE = "ko" # TTS 출력 포맷: 브라우저 MediaSource/