"""음성 캐스케이드 — OpenAI STT + OpenAI/Higgs TTS 어댑터. MASTERPLAN '음성 필수'(한신대 요구) / DESIGN_CONCEPT §5.2(음성 오브 4상태) / §4.3(립싱크 RMS): STT : OpenAI /v1/audio/transcriptions (gpt-4o-transcribe | whisper-1). 학습자 음성 → 텍스트. 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 re from dataclasses import dataclass, field from pathlib import Path from typing import Any, AsyncIterator, Mapping, Optional import httpx from ..config import settings 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" # 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/