import json
import os
import sqlite3
from datetime import datetime
from typing import Optional
import requests
from fastapi import FastAPI, File, Form, HTTPException, UploadFile
from fastapi.responses import HTMLResponse
API_BASE = os.getenv("API_BASE", "http://gx10.aquantico.lan:8093").rstrip("/")
OLLAMA_BASE_URL = os.getenv("OLLAMA_BASE_URL", "http://gx10.aquantico.lan:11434").rstrip("/")
OLLAMA_MODEL = os.getenv("OLLAMA_MODEL", "qwen3.5:9b")
DB_PATH = os.getenv("DB_PATH", "/data/ui.db")
app = FastAPI(title="Diarization UI + LLM")
def db():
conn = sqlite3.connect(DB_PATH)
conn.row_factory = sqlite3.Row
return conn
def init_db():
os.makedirs(os.path.dirname(DB_PATH), exist_ok=True)
with db() as c:
c.execute(
"""
CREATE TABLE IF NOT EXISTS transcripts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
created_at TEXT NOT NULL,
filename TEXT,
formatted_text TEXT NOT NULL,
raw_json TEXT NOT NULL
)
"""
)
c.execute(
"""
CREATE TABLE IF NOT EXISTS analyses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
transcript_id INTEGER NOT NULL,
created_at TEXT NOT NULL,
prompt TEXT NOT NULL,
answer TEXT NOT NULL,
FOREIGN KEY(transcript_id) REFERENCES transcripts(id)
)
"""
)
@app.on_event("startup")
def startup():
init_db()
@app.get("/healthz")
def healthz():
return {
"ok": True,
"api_base": API_BASE,
"ollama_base_url": OLLAMA_BASE_URL,
"ollama_model": OLLAMA_MODEL,
"db_path": DB_PATH,
}
@app.get("/", response_class=HTMLResponse)
def index():
return """
Diarization UI
Upload -> Transcribe + Diarize -> speichern -> LLM Analyse
Analyse
Gespeicherte Transkripte
"""
@app.post("/process")
async def process(file: UploadFile = File(...)):
data = await file.read()
if not data:
raise HTTPException(400, "empty file")
files = {"file": (file.filename or "audio.bin", data, file.content_type or "application/octet-stream")}
try:
r = requests.post(f"{API_BASE}/transcribe-diarize", files=files, timeout=1800)
except Exception as e:
raise HTTPException(502, f"API unreachable: {e}")
if r.status_code >= 400:
raise HTTPException(r.status_code, r.text)
payload = r.json()
formatted = payload.get("formatted_text", "")
with db() as c:
cur = c.execute(
"INSERT INTO transcripts(created_at, filename, formatted_text, raw_json) VALUES (?,?,?,?)",
(datetime.utcnow().isoformat(), file.filename, formatted, json.dumps(payload, ensure_ascii=False)),
)
transcript_id = cur.lastrowid
return {"ok": True, "transcript_id": transcript_id, **payload}
@app.get("/transcripts")
def transcripts(limit: int = 20):
with db() as c:
rows = c.execute(
"SELECT id, created_at, filename, formatted_text FROM transcripts ORDER BY id DESC LIMIT ?",
(limit,),
).fetchall()
return {"items": [dict(r) for r in rows]}
@app.post("/analyze")
def analyze(transcript_id: int = Form(...), prompt: str = Form(...)):
with db() as c:
row = c.execute("SELECT formatted_text FROM transcripts WHERE id=?", (transcript_id,)).fetchone()
if not row:
raise HTTPException(404, "transcript not found")
transcript_text = row[0]
llm_prompt = (
"Du bist ein Meeting-Analyst. Arbeite auf Deutsch.\n"
"Erzeuge präzise Ausgabe für den folgenden Auftrag.\n\n"
f"AUFTRAG:\n{prompt}\n\n"
f"TRANSKRIPT:\n{transcript_text}\n"
)
body = {
"model": OLLAMA_MODEL,
"prompt": llm_prompt,
"stream": False,
}
try:
r = requests.post(f"{OLLAMA_BASE_URL}/api/generate", json=body, timeout=600)
except Exception as e:
raise HTTPException(502, f"Ollama unreachable: {e}")
if r.status_code >= 400:
raise HTTPException(r.status_code, r.text)
j = r.json()
answer = j.get("response", "")
with db() as c:
cur = c.execute(
"INSERT INTO analyses(transcript_id, created_at, prompt, answer) VALUES (?,?,?,?)",
(transcript_id, datetime.utcnow().isoformat(), prompt, answer),
)
analysis_id = cur.lastrowid
return {"ok": True, "analysis_id": analysis_id, "answer": answer}