# Architektur ## Überblick VoiceLog ist eine Single-File-FastAPI-Anwendung (`app.py`, ~2000 Zeilen). Es gibt keine Templates, kein separates Frontend-Build-System und keine weiteren Module. HTML wird serverseitig als Python-f-Strings erzeugt; Bootstrap 5.3 (CDN) übernimmt das Layout. ``` Browser │ ▼ FastAPI (app.py, Port 8094) ├── SQLite (DB_PATH, Standard: /data/ui.db) ├── ThreadPoolExecutor (2 Workers) │ ├── Upload-Job → Transcription-API (API_BASE) │ └── Analyse-Job → Ollama (OLLAMA_BASE_URL) └── Dateisystem (JOB_DIR, Standard: /data/jobs/) ``` Traefik sitzt als Reverse-Proxy davor und terminiert TLS (`https://voicelog.aquantico.lan`). --- ## Datenmodell (SQLite) ### `projects` | Spalte | Typ | Beschreibung | |---|---|---| | `id` | INTEGER PK | | | `name` | TEXT UNIQUE | Projektname | | `created_at` | TEXT | ISO-8601-UTC | ### `prompts` | Spalte | Typ | Beschreibung | |---|---|---| | `id` | INTEGER PK | | | `name` | TEXT UNIQUE | Anzeigename | | `prompt` | TEXT | Prompttext für das LLM | | `created_at` | TEXT | | | `updated_at` | TEXT | | | `llm_use_default` | INTEGER | 1 = globale KI-Einstellungen nutzen (Standard) | | `llm_model` | TEXT | Überschreibt globales Modell wenn `llm_use_default=0` | | `llm_think` | TEXT | `"true"` / `"false"` | | `llm_num_ctx` | TEXT | Kontextgröße oder `"auto"` | | `llm_num_predict` | INTEGER | Max. Ausgabe-Tokens | | `llm_repeat_penalty` | REAL | | | `llm_repeat_last_n` | INTEGER | | ### `documents` | Spalte | Typ | Beschreibung | |---|---|---| | `id` | INTEGER PK | | | `project_id` | INTEGER FK | → projects | | `kind` | TEXT | `transcript` oder `analysis` | | `title` | TEXT | | | `content_md` | TEXT | Inhalt (Markdown) | | `source_document_id` | INTEGER FK | Bei `analysis`: Quell-Transkript | | `prompt_id` | INTEGER FK | Bei `analysis`: verwendeter Prompt | | `raw_json` | TEXT | Rohantwort der externen API als JSON | | `created_at` | TEXT | | ### `jobs` | Spalte | Typ | Beschreibung | |---|---|---| | `id` | INTEGER PK | | | `kind` | TEXT | `upload` oder `analysis` | | `status` | TEXT | `queued` → `running` → `done` / `error` / `cancelled` | | `project_id` | INTEGER | | | `document_id` | INTEGER | Quell-Dokument (bei `analysis`) | | `prompt_id` | INTEGER | | | `title` | TEXT | Anzeigename | | `file_path` | TEXT | Temporärer Pfad der Audiodatei (bei `upload`) | | `error` | TEXT | Fehlermeldung bei Status `error` | | `result_document_id` | INTEGER | Erzeugtes Dokument nach `done` | | `user_prompt` | TEXT | Zusatzinformation vom Nutzer | | `llm_prompt` | TEXT | Vollständiger LLM-Prompt inkl. `[num_ctx=…]`-Header | | `llm_response` | TEXT | Rohausgabe des LLM | | `llm_thinking` | TEXT | Thinking-Chain-of-Thought-Ausgabe | | `created_at` | TEXT | | | `started_at` | TEXT | | | `finished_at` | TEXT | | ### `settings` Einfache Key-Value-Tabelle für persistente Laufzeiteinstellungen. | Spalte | Typ | |---|---| | `key` | TEXT PK | | `value` | TEXT | Bekannte Keys: `ollama_model`, `ollama_think`, `ollama_num_ctx`, `ollama_num_predict`, `ollama_repeat_penalty`, `ollama_repeat_last_n`. --- ## Hintergrundverarbeitung Jobs werden über `enqueue_job()` in die DB geschrieben und sofort an einen `ThreadPoolExecutor(max_workers=2)` übergeben. ### Upload-Job (`_process_upload_job`) 1. Liest temporäre Audiodatei aus `JOB_DIR` 2. Sendet sie als `multipart/form-data` an `{API_BASE}/transcribe-diarize` 3. Speichert `formatted_text` aus der Antwort als `transcript`-Dokument 4. Löscht die temporäre Datei ### Analyse-Job (`_process_analysis_job`) 1. Lädt Dokument und Prompt aus der DB 2. Prüft `llm_use_default` am Prompt: globale oder promptspezifische KI-Einstellungen 3. Berechnet `num_ctx` dynamisch über `_estimate_num_ctx()` (außer bei manuellem Override) 4. Streamt Anfrage an `{OLLAMA_BASE_URL}/api/generate` 5. Puffert `thinking`- und `response`-Chunks in `_JOB_STREAMS` (für Live-Anzeige) 6. Speichert Ergebnis als `analysis`-Dokument Beide Worker prüfen nach jedem externen Call, ob der Job zwischenzeitlich auf `cancelled` gesetzt wurde. --- ## Datenbankmigrationen Werden inline in `init_db()` mit `ALTER TABLE … ADD COLUMN` in `try/except`-Blöcken ausgeführt. Die DB wird beim Start automatisch angelegt und migriert. --- ## PWA Die App liefert `/manifest.webmanifest`, `/icon.svg` und `/sw.js` direkt aus Route-Handlern aus und ist damit als Progressive Web App installierbar.