voice-agent/lxc-frontend
root 031d52c77a fix(profiles): KeyError bei JSON-Beispielen in Prompt-Templates
Bug: 9 von 17 Profilen (phone-call, brainstorm, one-on-one, postmortem,
schichtuebergabe, pflegedoku, visite, biografie) zeigen in ihren Prompts
JSON-Schema-Beispiele wie {"owner", "task"}. Python's str.format() liest
diese geschweiften Klammern als Platzhalter und wirft KeyError →
HTTP 500 vom Mac-Worker → Job FAILED bei summarize/protocol.

Fix:
- Neuer render(template, **vars) Helper in ollama_client: ersetzt nur
  explizit benannte Platzhalter, lässt alles andere literal stehen.
- summarize/make_protocol nutzen diesen Helper statt .format()
- profiles.validate_all() warnt beim Worker-Start, wenn ein erwarteter
  Platzhalter fehlt — Defekte werden früh sichtbar
- Mac-Worker-Middleware fängt unhandled exceptions und liefert JSON-Body
  mit Type+Message+Traceback-Tail (statt generic "Internal Server Error")
- LXC MacClient liest diesen Error-Body und packt ihn in die HTTPStatusError-
  Message → landet im Job-error-Feld + Diagnose-Panel mit echter Wurzel-Info

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-15 06:55:55 +00:00
..

Voice-Agent — LXC-Frontend

FastAPI + statisches HTML/JS. Läuft im LXC-Container, stellt die Weboberfläche, verwaltet Jobs (SQLite) und ruft den Mac-Worker per HTTP an. Macht selbst keine AI-Verarbeitung.

Lokal starten (Entwicklung)

cd lxc-frontend
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env
# Für lokalen Test ohne Mac kann der Mac-Worker mit WHISPER_ENGINE=mock laufen
uvicorn app.main:app --reload --port 8000

Öffnen: http://localhost:8000

Auf LXC deployen

deploy/install.sh macht alles: Repo klonen, venv anlegen, systemd-Service und Nginx einrichten. Siehe Haupt-README.md.

API

Methode Pfad Zweck
GET / Web-UI
GET /health LXC-Health
GET /api/mac/health Mac-Worker-Erreichbarkeit
GET /api/profiles Verfügbare Verarbeitungs-Profile (Cache 60 s)
POST /api/auth/login / logout / setup Cookie-Session-Auth
GET / PATCH /api/auth/me Eigener User + Standard-Profil
GET / POST / DELETE /api/admin/users[...] User-Verwaltung (admin)
POST /api/jobs multipart: audio, title, profile → Job anlegen
GET /api/jobs Liste aller Jobs (Owner-gefiltert; Admin sieht alle)
GET /api/jobs/{id} Job-Detail (inkl. step_started_at, last_heartbeat_at)
GET /api/jobs/{id}/diag Diagnose-Bündel: Stage-Sekunden + Mac-Worker-Log
POST /api/jobs/{id}/retry Verarbeitung neu anstoßen (Transkript bleibt)
GET /api/jobs/{id}/download/{kind} kindpdf, docx, transcript, summary, protocol

Benutzer- und Passwort-Verwaltung per CLI

Nach dem Deploy ist /usr/local/bin/voice-agent-admin verfügbar — ein Wrapper auf das Python-Modul app.cli. Funktioniert vom Root-Login aus jedem Verzeichnis.

# Alle User auflisten
sudo voice-agent-admin list

# Passwort eines Users zurücksetzen (z. B. Admin-Passwort vergessen)
sudo voice-agent-admin reset admin 'NeuesPasswort123'

# Neuen User anlegen
sudo voice-agent-admin create maria 'StartPasswort123'
sudo voice-agent-admin create chefin 'Geheim12345' --admin

# User zum Admin machen
sudo voice-agent-admin promote maria

# User löschen (geht nicht für den letzten Admin oder den eigenen Account)
sudo voice-agent-admin delete maria

# Passwort prüfen ohne Browser (für Debug)
sudo voice-agent-admin verify admin 'NeuesPasswort123'
# → "OK — Passwort für 'admin' stimmt." oder "NEIN — ..."

Mindest-Passwortlänge: 8 Zeichen. Der letzte Admin kann nicht gelöscht werden.

Initialer Admin

Beim allerersten Web-Aufruf zeigt die UI das Setup-Formular statt des Logins (weil noch kein User existiert). Username + Passwort eingeben → erster Admin wird angelegt + alle bestehenden Jobs werden ihm zugeordnet.

Falls du den Erst-Admin lieber per CLI anlegen willst (z. B. Skript-gesteuert):

sudo voice-agent-admin create admin 'StartPasswort123' --admin

Danach im Browser direkt einloggen.

Datenfluss

Upload → SQLite-Job → BackgroundTask (idempotent)
  → Mac: /api/transcribe (multipart)
  → Mac: /api/preload     (Modell ins RAM ziehen, vor Summarize)
  → Mac: /api/summarize   (JSON)
  → Mac: /api/protocol    (JSON)
  → Mac: /api/export/docx (JSON) → DOCX
  → Mac: /api/export/pdf  (JSON) → PDF
→ Ergebnisse landen in /var/lib/voice-agent/results/{job_id}/

Während laufender Mac-Calls pingt ein Heartbeat alle 3 s last_heartbeat_at. Bei RemoteProtocolError / ConnectError retried der MacClient einmal automatisch mit 3 s Backoff. Jeder Mac-Call trägt einen X-Job-Id-Header — der Mac-Worker sammelt damit pro Job einen Log-Ringbuffer (200 Zeilen, 1 h TTL), den /api/jobs/{id}/diag für die UI bündelt.