fix: add summary fallback and stabilize recording sink

This commit is contained in:
2026-06-10 12:01:13 +00:00
parent 53889da8f6
commit 5e51535693
4 changed files with 153 additions and 10 deletions
+66 -8
View File
@@ -31,6 +31,7 @@ DIRECT_OPENAI_TRANSCRIPTION_MODELS = [
TRANSCRIPTION_CHUNK_SECONDS = 600
MAX_TRANSCRIPTION_FILE_BYTES = 25 * 1024 * 1024
SUMMARY_TRANSCRIPT_CHAR_LIMIT = 12000
DIRECT_OPENAI_SUMMARY_MODEL = "gpt-4o-mini"
def _api_key() -> str:
@@ -362,7 +363,18 @@ async def transcribe(audio_path: str) -> str:
pass
async def _chat_completion(prompt: str, *, timeout: int = 180) -> str:
def _extract_chat_content(data: Any, *, provider: str) -> str:
try:
content = data["choices"][0]["message"]["content"]
except (KeyError, IndexError, TypeError) as exc:
raise RuntimeError(f"Unexpected {provider} summarization response shape") from exc
if not isinstance(content, str) or not content.strip():
raise RuntimeError(f"{provider} summarization returned empty content")
return content.strip()
async def _chat_completion_openrouter(prompt: str, *, timeout: int = 180) -> str:
body = {
"model": "@preset/cheap-deepseek-v4-flash",
"messages": [{"role": "user", "content": prompt}],
@@ -386,14 +398,60 @@ async def _chat_completion(prompt: str, *, timeout: int = 180) -> str:
raise RuntimeError(f"OpenRouter summarization failed: {detail}") from exc
data = resp.json()
try:
content = data["choices"][0]["message"]["content"]
except (KeyError, IndexError, TypeError) as exc:
raise RuntimeError("Unexpected OpenRouter summarization response shape") from exc
return _extract_chat_content(data, provider="OpenRouter")
if not isinstance(content, str) or not content.strip():
raise RuntimeError("OpenRouter summarization returned empty content")
return content.strip()
async def _chat_completion_openai(prompt: str, *, timeout: int = 180) -> str:
api_key = _openai_api_key()
if not api_key:
raise RuntimeError("OPENAI_API_KEY not set")
body = {
"model": DIRECT_OPENAI_SUMMARY_MODEL,
"messages": [{"role": "user", "content": prompt}],
"max_tokens": 1500,
}
headers = {
"Authorization": f"Bearer {api_key}",
"Content-Type": "application/json",
}
async with httpx.AsyncClient(timeout=timeout) as client:
resp = await client.post(
f"{OPENAI_BASE}/chat/completions",
headers=headers,
json=body,
)
try:
resp.raise_for_status()
except httpx.HTTPStatusError as exc:
detail = summarize_error(_safe_json(resp), fallback=resp.text)
request_id = resp.headers.get("x-request-id")
suffix = f"; request_id={request_id}" if request_id else ""
logger.error("Direct OpenAI summarization failed: %s%s", detail, suffix)
raise RuntimeError(f"Direct OpenAI summarization failed: {detail}{suffix}") from exc
data = resp.json()
return _extract_chat_content(data, provider="Direct OpenAI")
async def _chat_completion(prompt: str, *, timeout: int = 180) -> str:
failures: list[str] = []
try:
return await _chat_completion_openrouter(prompt, timeout=timeout)
except RuntimeError as exc:
failures.append(str(exc))
logger.warning("OpenRouter summarization failed; checking direct OpenAI fallback: %s", exc)
if _openai_api_key():
try:
return await _chat_completion_openai(prompt, timeout=timeout)
except RuntimeError as exc:
failures.append(str(exc))
else:
failures.append("direct OpenAI summarization fallback skipped: OPENAI_API_KEY not set")
raise RuntimeError("Summarization failed across providers: " + " | ".join(failures))
def _summary_prompt(transcript: str) -> str: