"""Endpointy pro přepis audia. - POST /transcribe/dual → paralelní přepis Deepgram + OpenAI Whisper (dva texty) - POST /transcribe/combined → dual + sloučení do jednoho co nejlepšího přepisu (merge) Audio přichází jako proměnná (upload `file` ve form-data). Všechny ostatní parametry jsou v POST těle (form fields). API klíče jsou v X- hlavičkách (viz credentials.py). Výstup je vždy JSON. """ import asyncio from fastapi import APIRouter, Depends, File, Form, UploadFile from pydantic import BaseModel, Field from ..clients import deepgram_client, openai_client from ..config import ( DEFAULT_CHAT_MODEL, DEFAULT_DEEPGRAM_MODEL, DEFAULT_LANGUAGE, DEFAULT_WHISPER_MODEL, ) from ..credentials import Credentials, get_credentials from ..errors import BadRequestError from ..logging_config import get_logger from ..prompts import DEFAULT_COMBINE_PROMPT log = get_logger("audio-transcription.transcribe") router = APIRouter(tags=["transcribe"]) class DualResult(BaseModel): text1: str = Field(..., description="Přepis z Deepgramu.") text2: str = Field(..., description="Přepis z OpenAI Whisper.") deepgram_model: str whisper_model: str language: str class CombinedResult(DualResult): merged: str = Field(..., description="Sloučený, co nejpřesnější výsledný přepis.") chat_model: str async def _read_upload(file: UploadFile) -> tuple[bytes, str, str]: """Načte upload do paměti a vrátí (bytes, filename, content_type).""" audio = await file.read() if not audio: raise BadRequestError("Nahraný soubor je prázdný.") filename = file.filename or "audio.mp3" content_type = file.content_type or "application/octet-stream" return audio, filename, content_type async def _transcribe_dual( creds: Credentials, audio: bytes, filename: str, content_type: str, deepgram_model: str, whisper_model: str, language: str, diarize: bool, smart_format: bool, ) -> tuple[str, str]: """Spustí oba přepisy paralelně; vrátí (deepgram_text, whisper_text).""" deepgram_key = creds.require_deepgram() openai_key = creds.require_openai() text1, text2 = await asyncio.gather( deepgram_client.transcribe( api_key=deepgram_key, audio=audio, content_type=content_type, model=deepgram_model, language=language, diarize=diarize, smart_format=smart_format, ), openai_client.transcribe( api_key=openai_key, audio=audio, filename=filename, content_type=content_type, model=whisper_model, language=language, ), ) return text1, text2 @router.post( "/transcribe/dual", response_model=DualResult, summary="Paralelní přepis Deepgram + OpenAI Whisper", description="Přijme audio soubor a vrátí dva nezávislé přepisy: " "`text1` z Deepgramu a `text2` z OpenAI Whisper.", ) async def transcribe_dual( file: UploadFile = File(..., description="Audio soubor k přepisu (proměnná)."), deepgram_model: str = Form(DEFAULT_DEEPGRAM_MODEL, description="Deepgram model."), whisper_model: str = Form(DEFAULT_WHISPER_MODEL, description="OpenAI přepisový model."), language: str = Form(DEFAULT_LANGUAGE, description="Jazyk audia (ISO kód, např. cs)."), diarize: bool = Form(True, description="Deepgram diarizace (rozlišení mluvčích)."), smart_format: bool = Form(True, description="Deepgram smart formatting."), creds: Credentials = Depends(get_credentials), ) -> DualResult: audio, filename, content_type = await _read_upload(file) text1, text2 = await _transcribe_dual( creds, audio, filename, content_type, deepgram_model, whisper_model, language, diarize, smart_format, ) return DualResult( text1=text1, text2=text2, deepgram_model=deepgram_model, whisper_model=whisper_model, language=language, ) @router.post( "/transcribe/combined", response_model=CombinedResult, summary="Dual přepis + AI sloučení do nejlepšího přepisu", description="Přijme audio soubor, vytvoří přepis z Deepgramu i Whisperu a poté je " "sloučí přes OpenAI Chat podle `combine_prompt` do jediného, co nejpřesnějšího " "českého přepisu. Vrací `text1`, `text2` i výsledný `merged`.", ) async def transcribe_combined( file: UploadFile = File(..., description="Audio soubor k přepisu (proměnná)."), combine_prompt: str = Form( DEFAULT_COMBINE_PROMPT, description="System prompt pro sloučení. Nezadáš-li, použije se výchozí " "'Czech Transcript Merger'.", ), deepgram_model: str = Form(DEFAULT_DEEPGRAM_MODEL, description="Deepgram model."), whisper_model: str = Form(DEFAULT_WHISPER_MODEL, description="OpenAI přepisový model."), chat_model: str = Form(DEFAULT_CHAT_MODEL, description="OpenAI chat model pro sloučení."), language: str = Form(DEFAULT_LANGUAGE, description="Jazyk audia (ISO kód, např. cs)."), diarize: bool = Form(True, description="Deepgram diarizace (rozlišení mluvčích)."), smart_format: bool = Form(True, description="Deepgram smart formatting."), creds: Credentials = Depends(get_credentials), ) -> CombinedResult: audio, filename, content_type = await _read_upload(file) text1, text2 = await _transcribe_dual( creds, audio, filename, content_type, deepgram_model, whisper_model, language, diarize, smart_format, ) prompt = combine_prompt.strip() if combine_prompt and combine_prompt.strip() else DEFAULT_COMBINE_PROMPT merged = await openai_client.merge_transcripts( api_key=creds.require_openai(), model=chat_model, system_prompt=prompt, text_deepgram=text1, text_whisper=text2, ) return CombinedResult( text1=text1, text2=text2, merged=merged, deepgram_model=deepgram_model, whisper_model=whisper_model, chat_model=chat_model, language=language, )