This commit is contained in:
JiriUhlir
2026-07-10 09:46:49 +02:00
parent 429cb84461
commit 7a3f61c125
16 changed files with 762 additions and 16 deletions
View File
+60
View File
@@ -0,0 +1,60 @@
"""Klient pro Deepgram pre-recorded přepis (REST, přes httpx).
Nepoužíváme těžké SDK — voláme REST endpoint přímo, aby nebyl závislý na verzích.
"""
import httpx
from ..config import DEEPGRAM_BASE_URL, UPSTREAM_TIMEOUT_SECONDS
from ..errors import UpstreamError
from ..logging_config import get_logger
log = get_logger("audio-transcription.deepgram")
async def transcribe(
*,
api_key: str,
audio: bytes,
content_type: str,
model: str,
language: str,
diarize: bool,
smart_format: bool,
) -> str:
"""Přepíše audio Deepgramem a vrátí prostý transkript (channels[0].alternatives[0].transcript)."""
params = {
"model": model,
"language": language,
"diarize": "true" if diarize else "false",
"smart_format": "true" if smart_format else "false",
}
headers = {
"Authorization": f"Token {api_key}",
"Content-Type": content_type or "application/octet-stream",
}
url = f"{DEEPGRAM_BASE_URL}/listen"
try:
async with httpx.AsyncClient(timeout=UPSTREAM_TIMEOUT_SECONDS) as client:
resp = await client.post(url, params=params, headers=headers, content=audio)
except httpx.HTTPError as exc:
log.error("Deepgram request failed: %s", exc)
raise UpstreamError(f"Deepgram request se nezdařil: {exc}") from exc
if resp.status_code >= 400:
# Deepgram vrací chybu v JSON; nikdy nelogujeme klíč (ten je jen v hlavičce).
snippet = resp.text[:500]
log.warning("Deepgram returned %s: %s", resp.status_code, snippet)
raise UpstreamError(
f"Deepgram vrátil chybu {resp.status_code}.",
detail=snippet,
)
data = resp.json()
try:
channels = data["results"]["channels"]
alternative = channels[0]["alternatives"][0]
return alternative.get("transcript", "") or ""
except (KeyError, IndexError, TypeError) as exc:
log.error("Unexpected Deepgram response shape: %s", exc)
raise UpstreamError("Neočekávaná struktura odpovědi z Deepgramu.") from exc
+87
View File
@@ -0,0 +1,87 @@
"""Klient pro OpenAI — Whisper přepis + Chat Completions (slučování), přes httpx."""
import httpx
from ..config import OPENAI_BASE_URL, UPSTREAM_TIMEOUT_SECONDS
from ..errors import UpstreamError
from ..logging_config import get_logger
log = get_logger("audio-transcription.openai")
async def transcribe(
*,
api_key: str,
audio: bytes,
filename: str,
content_type: str,
model: str,
language: str | None = None,
) -> str:
"""Přepíše audio přes OpenAI Whisper (/audio/transcriptions) a vrátí text."""
headers = {"Authorization": f"Bearer {api_key}"}
files = {"file": (filename or "audio.mp3", audio, content_type or "application/octet-stream")}
form = {"model": model}
if language:
form["language"] = language
url = f"{OPENAI_BASE_URL}/audio/transcriptions"
try:
async with httpx.AsyncClient(timeout=UPSTREAM_TIMEOUT_SECONDS) as client:
resp = await client.post(url, headers=headers, data=form, files=files)
except httpx.HTTPError as exc:
log.error("OpenAI transcription request failed: %s", exc)
raise UpstreamError(f"OpenAI Whisper request se nezdařil: {exc}") from exc
if resp.status_code >= 400:
snippet = resp.text[:500]
log.warning("OpenAI transcription returned %s: %s", resp.status_code, snippet)
raise UpstreamError(
f"OpenAI Whisper vrátil chybu {resp.status_code}.",
detail=snippet,
)
data = resp.json()
return data.get("text", "") or ""
async def merge_transcripts(
*,
api_key: str,
model: str,
system_prompt: str,
text_deepgram: str,
text_whisper: str,
) -> str:
"""Sloučí dva přepisy do jednoho přes Chat Completions dle system promptu."""
headers = {"Authorization": f"Bearer {api_key}"}
payload = {
"model": model,
"messages": [
{"role": "system", "content": system_prompt},
{"role": "user", "content": f"Text z Deepgram: {text_deepgram}"},
{"role": "user", "content": f"Text z OpenAI Whisper: {text_whisper}"},
],
}
url = f"{OPENAI_BASE_URL}/chat/completions"
try:
async with httpx.AsyncClient(timeout=UPSTREAM_TIMEOUT_SECONDS) as client:
resp = await client.post(url, headers=headers, json=payload)
except httpx.HTTPError as exc:
log.error("OpenAI chat request failed: %s", exc)
raise UpstreamError(f"OpenAI chat (merge) request se nezdařil: {exc}") from exc
if resp.status_code >= 400:
snippet = resp.text[:500]
log.warning("OpenAI chat returned %s: %s", resp.status_code, snippet)
raise UpstreamError(
f"OpenAI chat (merge) vrátil chybu {resp.status_code}.",
detail=snippet,
)
data = resp.json()
try:
return data["choices"][0]["message"]["content"] or ""
except (KeyError, IndexError, TypeError) as exc:
log.error("Unexpected OpenAI chat response shape: %s", exc)
raise UpstreamError("Neočekávaná struktura odpovědi z OpenAI chatu.") from exc