import logging import secrets from pathlib import Path from fastapi import FastAPI, File, Form, HTTPException, UploadFile from fastapi.responses import Response from pydantic import BaseModel from app.core import ollama_client from app.core.config import settings from app.core.docx_export import build_docx from app.core.whisper_engine import engine as whisper logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(name)s: %(message)s") log = logging.getLogger("mac-worker") app = FastAPI(title="Voice-Agent Mac-Worker") class SummarizeIn(BaseModel): transcript: str title: str = "" class ProtocolIn(BaseModel): transcript: str summary: dict title: str = "" @app.get("/health") async def health(): info = { "status": "ok", "whisper_engine": settings.whisper_engine, "whisper_model": settings.whisper_model, "ollama_model": settings.ollama_model, "ollama_reachable": False, } try: await ollama_client.health() info["ollama_reachable"] = True except Exception as e: # noqa: BLE001 info["ollama_error"] = str(e) return info @app.post("/api/transcribe") async def transcribe(audio: UploadFile = File(...), language: str = Form("de")): if not audio.filename: raise HTTPException(400, "Dateiname fehlt") suffix = Path(audio.filename).suffix or ".bin" tmp = settings.work_dir / (secrets.token_hex(8) + suffix) try: with tmp.open("wb") as f: while chunk := await audio.read(1024 * 1024): f.write(chunk) log.info("Transcribing %s (%.1f MB) lang=%s", audio.filename, tmp.stat().st_size / 1024 / 1024, language) result = whisper.transcribe(tmp, language=language) return result finally: tmp.unlink(missing_ok=True) @app.post("/api/summarize") async def summarize(payload: SummarizeIn): if not payload.transcript.strip(): raise HTTPException(400, "Leerer Transkript") return await ollama_client.summarize(payload.transcript, title=payload.title) @app.post("/api/protocol") async def protocol(payload: ProtocolIn): if not payload.transcript.strip(): raise HTTPException(400, "Leerer Transkript") return await ollama_client.make_protocol(payload.transcript, payload.summary, title=payload.title) @app.post("/api/export/docx") async def export_docx(protocol: dict): data = build_docx(protocol) return Response( content=data, media_type="application/vnd.openxmlformats-officedocument.wordprocessingml.document", headers={"Content-Disposition": 'attachment; filename="protocol.docx"'}, )