20 Commits

Author SHA1 Message Date
root
7679efa7e4 feat(settings): eigene Unterseite für User-Einstellungen + Passwort-Wechsel
Header: Profil- und Modell-Dropdowns rausgenommen, dafür ein
"⚙ Einstellungen"-Link. Klick öffnet eine eigene Settings-View
(toggled die main-View aus) mit drei Sektionen:

- Standard-Profil (sofort speichernd)
- Standard-LLM-Modell (sofort speichernd, leer = Worker-Default)
- Passwort ändern (aktuelles + neues + Bestätigung)

Neuer Endpoint POST /api/auth/me/password — verlangt zwingend das
aktuelle Passwort (verhindert Übernahme via offene Browser-Session).
Admin-Reset über /api/admin/users/<id>/password bleibt unberührt.

Layout: Settings-View ist eine Card mit max-width 640px, jede
Sektion durch Trennlinie abgegrenzt. Inline-Status-Meldungen
("Standard-Profil ist jetzt: …") direkt unter den Controls.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:40:28 +00:00
root
f03d46afb5 ui: Upload-Form & Jobs-Liste aufgeräumt
Upload-Form:
- Title-Label nicht mehr mit "(optional)" suffix — Hinweis wandert in
  den Placeholder. Labels haben jetzt einheitliches Spalten-Layout
  (flex-column + align-items: end), Inputs sind bündig
- "Standard: X" Hinweis unter dem Profil entfernt (Dropdown ist
  selbst-erklärend, das Header-Dropdown zeigt's eh schon)

Jobs-Liste:
- Wenn kein Titel gesetzt ist, wird der Dateiname nur 1× als Titel
  gezeigt (vorher: groß als Titel + nochmal klein darunter)
- Profile/Modell-Tags in eigene flex-Zeile statt margin-left-Hack
- Dateiname-Zeile mit Ellipsis bei zu langen Namen
- Tags 1px kleiner, weniger Padding
- Datumsformat: heute → nur HH:MM, sonst dd.mm HH:MM (statt langem
  dd.mm.yyyy HH:MM:SS) — col-updated von 120px auf 78px geschrumpft
- col-status von 110px auf 170px → Aktions-Buttons stapeln weniger

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 20:34:54 +00:00
root
c6164d946c feat(model): wählbares LLM-Modell pro User & pro Job
Mac-Worker:
- /api/summarize, /api/protocol, /api/preload akzeptieren optionales
  model-Feld → überschreibt den .env-Default pro Call, kein Restart nötig
- /api/models listet die lokal verfügbaren Ollama-Modelle + Default

LXC:
- User.default_model + Job.model (Migration für SQLite)
- /api/auth/me liefert default_model, PATCH speichert ihn sofort
- /api/mac/models proxiet die Liste an die UI
- Pipeline reicht job.model an Mac-Worker durch; leer = Worker-Default
- create_job & reprocess akzeptieren model-Override
- Snapshot-Meta nutzt job.model (genauer als /health-Snapshot)

UI:
- Header-Dropdown "Modell:" neben "Profil:" — sofort-speichert User-Default
- Jeder Job zeigt sein Modell als Tag (lila); leer bedeutet Worker-Default
- Reprocess öffnet einen Modal-Picker (statt simplem confirm), Auswahl
  aller verfügbaren Modelle inkl. "Worker-Default"

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 16:48:11 +00:00
root
dbf7bca6c7 feat(jobs): "Mit anderem Modell verarbeiten" + Verlauf pro Job
Neuer Endpoint POST /api/jobs/{id}/reprocess: erlaubt einen LLM-Lauf
auch auf done/failed-Jobs, sichert Summary/Protokoll/DOCX/PDF in
results/<id>/runs/<ts>-<modell>/ als Snapshot, leert die DB-Pfade,
startet die Pipeline mit dem aktuell auf dem Mac konfigurierten
Ollama-Modell. Transkript bleibt erhalten.

GET /api/jobs/{id}/runs liefert die Snapshot-Liste,
GET /api/jobs/{id}/runs/{run_id}/{file} den Einzeldownload.

UI: zwei neue Buttons auf done/failed-Jobs — "↻ Mit anderem Modell"
und "Verlauf ▾". Reprocess-Dialog zeigt das aktuell konfigurierte
Mac-Modell an, bevor er startet, damit kein Versehen passiert.
Verlauf-Panel listet pro Lauf Modellname + Zeitstempel + Download-
Links für die Snapshot-Dateien.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-16 07:58:23 +00:00
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
root
f408a555f8 feat: kontinuierliche LXC↔Mac-Diagnose + Auto-Retry
Diagnose-Sichtbarkeit (Plan A):
- Job.step_started_at + last_heartbeat_at, Heartbeat-Task pingt alle 3 s
  während laufender Mac-Calls
- Mac-Worker hält per X-Job-Id Header einen Log-Ringbuffer pro Job
  (200 Zeilen, 1 h TTL); GET /api/jobs/{id}/log liefert ihn aus
- LXC-Endpoint GET /api/jobs/{id}/diag bündelt Job-Status, Stage-Sekunden,
  Heartbeat-Alter und Mac-Worker-Log
- Frontend: Live-Timing im Status-Pill ("läuft seit Xs · letzter Ping vor Ys"),
  klappbares Diagnose-Panel mit Mac-Log für FAILED + langlaufende Jobs

Robustheit (Plan B):
- MacClient: einmaliger Retry mit 3 s Backoff bei RemoteProtocolError /
  ConnectError / ReadError für summarize/protocol/export
- Mac-Worker /api/preload heizt Ollama vor (keep_alive 30 m, num_predict 1);
  Pipeline ruft Preload vor summarize, damit Modell-Reload nicht mitten
  in der Generierung passiert (Hauptursache "Server disconnected")

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 09:12:27 +00:00
root
354e4c57eb feat: PDF-Export zusätzlich zu DOCX
Mac-Worker:
- pdf_export.py mit reportlab (pure Python, kein externes Tool)
- Nutzt dieselbe profile.docx-Konfig wie der DOCX-Renderer →
  strukturell identisch, nur andere Optik
- POST /api/export/pdf
- requirements: reportlab==4.2.5

LXC:
- Job.pdf_path neu (Migration: ALTER TABLE job ADD COLUMN)
- Pipeline-Step nach DOCX: PDF wird ebenfalls erzeugt
  (Progress: 90 DOCX → 95 PDF → 100 done)
- /api/jobs/{id}/download/pdf
- /api/jobs/{id}/retry löscht PDF mit, damit es neu erzeugt wird
- Frontend: zusätzlicher Download-Button "PDF" (vorne im Array)
- Tabelle: Download-Spalte 320px, 3-spaltiges Grid (5 Buttons → 3+2)
- Cards (Mobile) bleiben 2-spaltig (5 Buttons → 3 Zeilen)
2026-05-14 04:48:47 +00:00
root
220b7ccbc3 feat: Retry-Funktion + idempotente Pipeline (Transkript bleibt erhalten)
Backend:
- POST /api/jobs/{id}/retry — löscht summary/protocol/docx, queued den Job
  neu und startet Pipeline. Transkript bleibt erhalten (teurer Schritt).
- Pipeline überspringt Steps, deren Output-Datei schon existiert →
  retry rennt direkt in summarize ohne erneute Transkription.

Frontend:
- Kleiner gelber '↻ Erneut versuchen'-Button in jeder Job-Zeile/-Karte
  wenn Status != done
- Confirm-Dialog erklärt was passiert
- Event-Delegation, funktioniert für Tabelle (Desktop) und Cards (Mobile)
2026-05-14 04:11:13 +00:00
root
24ac3ac058 ui: echte Mobile-First-Variante — Recording-Hero, Job-Cards, Admin-Klappe
Mobile-Layout ab <700px komplett umgebaut, nicht mehr nur Desktop-Verkleinerung:

- Header: nur Logo + User + Profil-Mini-Dropdown + Logout
- Upload-Card: großer roter Aufnahme-Button als Hero-Element
  - Profil und Titel darunter, kompakt
  - Datei-Upload in <details>-Klappe (auf Desktop offen, Mobile zu)
- Jobs: Card-Liste statt Tabelle (Tabelle nur Desktop, Cards nur Mobile)
- Admin: H2 als Toggle, Body kollabiert (auf Desktop immer offen)
- Inputs auf Mobile mit font-size:16px → kein iOS-Zoom-on-Focus
- Recording-Pane: Timer 64px, Buttons full-width
- safe-area-inset für iPhone-Notch + Bottom-Bar

Ohne JS-Mode-Switch — alles via CSS Media Query +
.desktop-only / .mobile-only Helfer-Klassen. JS rendert beide
Job-Repräsentationen, CSS schaltet die richtige sichtbar.
2026-05-14 04:00:33 +00:00
root
11ab8b57bf feat: PWA + Mikrofon-Aufnahme + HTTPS via selbst-signiertem Cert
Frontend-Aufnahme (MediaRecorder):
- Aufnahme-Button im Upload-Form, große Touch-Targets
- Start / Pause / Fortsetzen / Stop mit Live-Timer
- Format-Auto-Detect (Opus/WebM, Safari nimmt M4A)
- Wake Lock damit iOS nicht ins Sleep geht
- Aufnahme-Datei landet als File-Objekt im Upload-Flow
- Beim Submit: aufgenommene Datei bevorzugt vor File-Input

PWA:
- manifest.webmanifest, icon.svg + PNG-Icons (192/512)
- apple-mobile-web-app-capable + theme-color
- "Zum Home-Bildschirm" hinzufügen verwandelt Seite in App

Mobile-Layout:
- Single-Column ab 700px, größere Buttons
- Header stapelt sich vertikal
- Tabelle scrollt horizontal statt zu zerquetschen
- Safe-Area-Insets für iPhone-Notch

HTTPS (Pflicht für Mikrofon-Zugriff):
- install.sh erzeugt selbst-signiertes Cert (4096 Bit, 825 Tage, mit
  SAN aus Hostname + LAN-IP)
- Nginx: HTTP → HTTPS Redirect, Cert unter /ssl/voice-agent.crt
  ungesichert abrufbar (für bequemen iOS-Profil-Import)

Backend:
- ALLOWED_EXTS um 'webm' erweitert
2026-05-14 03:43:51 +00:00
root
482b344787 ui: Standard-Profil kompakt in die User-Bar oben statt eigener Karte
- Mini-Select neben dem User-Namen, speichert sofort bei change
- "Meine Einstellungen"-Karte entfernt
- Selector erscheint nur, wenn ≥2 Profile verfügbar (sonst nichts zu wählen)
2026-05-14 03:23:36 +00:00
root
3a9881d36f feat: Profile-System für unterschiedliche Use-Cases (Sitzungsprotokoll, später Arzt-Diktat etc.)
Mac-Worker:
- profiles/meeting.yaml — extrahiert die bisherigen Prompts + DOCX-Layout
- core/profiles.py — YAML-Loader mit Cache und Fallback
- ollama_client.summarize/make_protocol nehmen profile_name
- docx_export: generischer Renderer aus profile.docx (meta + sections mit
  Typ text/list/tasks/transcript)
- /api/profiles listet verfügbare Profile
- pyyaml als Dependency

LXC:
- Job.profile + User.default_profile (Migration: ALTER TABLE)
- /api/profiles proxy mit 60s-Cache und Fallback
- Upload-Form akzeptiert profile (Server-Default: user.default_profile)
- Pipeline gibt Profile bei summarize/protocol/docx an Mac weiter
- PATCH /api/auth/me — User kann Standard-Profil ändern

Frontend:
- Profile-Dropdown im Upload (nur sichtbar wenn ≥2 Profile)
- Settings-Karte "Mein Standard-Profil" (nur wenn ≥2 Profile)
- Job-Zeile zeigt grünes Profile-Tag (nur wenn relevant)

Neue Profile = neue YAML-Datei in mac-worker/profiles/ — kein Code-Deploy
nötig, der Mac-Worker liest sie beim Start ein.
2026-05-14 03:11:30 +00:00
root
98cf9eb47e fix(ui): .hidden mit !important — Overlay verschwand nach Login nicht (display:flex auf .overlay überschrieb display:none) 2026-05-14 02:54:22 +00:00
root
42443e1f23 fix(cli): chdir nach lxc-frontend, damit pydantic-settings die .env findet
Beim Aufruf aus /root oder anderen Verzeichnissen scheiterte der CLI mit
PermissionError, weil pydantic-settings .env relativ zum Arbeitsverzeichnis
liest und z. B. /root/.env stat-en wollte (für deploy nicht lesbar).
2026-05-14 02:52:13 +00:00
root
a681ca871b fix(cli): sys.path-Fix für direktes Skript-Aufrufen + Wrapper voice-agent-admin
- cli.py fügt lxc-frontend/ zu sys.path hinzu, sodass
  `python /pfad/zu/cli.py <cmd>` aus jedem Verzeichnis funktioniert
  (vorher nur via `python -m app.cli` aus dem App-Verzeichnis)
- install.sh legt /usr/local/bin/voice-agent-admin als Wrapper an,
  damit man einfach `sudo voice-agent-admin list` aufrufen kann
2026-05-14 02:47:28 +00:00
root
44528ef655 feat(auth): CLI-Tool für Notfall-User-Verwaltung + besseres Login-Logging
- app/cli.py: list / create / reset / promote / delete / verify
  Aufruf: .venv/bin/python -m app.cli <befehl>
- Login-Endpoint loggt jetzt getrennt "User nicht gefunden" vs. "Passwort
  falsch" inkl. Hash-Länge — erlaubt Diagnose ohne Code-Änderung
2026-05-14 02:44:21 +00:00
root
a1167df4f1 feat: User-Verwaltung mit Login, Sessions und Owner-Filter
Backend:
- User-Modell (sqlmodel) mit bcrypt-Passwort-Hash und is_admin-Flag
- Job.owner_id (FK auf user.id), per Migration nachgerüstet (SQLite ALTER)
- Starlette SessionMiddleware mit signiertem Cookie (SESSION_SECRET)
- /api/auth: login / logout / me / needs-setup / setup (Initial-Admin)
- /api/admin: User-CRUD + Passwort-Reset (Admin-only); letzter Admin /
  eigener Account vor Löschung geschützt
- /api/jobs: Owner-Filter (Admin sieht alle inkl. owner_username),
  404 statt 403 bei fremden Jobs (kein Existenz-Leak)

Frontend:
- Auth-Overlay mit Login bzw. Erst-Setup-Modus (auto-Detection)
- Header zeigt User + Logout, Admin-Badge bei Admin-Konten
- Admin-Karte: User anlegen, Passwort zurücksetzen, Löschen (mit Confirm)
- 401 → Login-Overlay (auch bei XHR-Upload)

Deploy:
- install.sh generiert SESSION_SECRET einmalig per python-secrets
- .env wird auf chmod 600 gesetzt
- Erstaufruf zeigt automatisch das Setup-Formular; bestehende Jobs werden
  dem ersten Admin zugeordnet
2026-05-14 02:38:52 +00:00
root
d4b0d21ed1 fix(frontend): no-cache Header für Static-Files und Index
Browser haben nach Deploys die alten app.js / style.css aus dem Cache
weiterbenutzt. Mit Cache-Control: no-cache, must-revalidate wird bei
jeder Anfrage revalidiert (304 wenn unverändert, 200 wenn neu).
2026-05-14 02:24:58 +00:00
root
b57f44cc73 ui: Job-Tabelle aufräumen — feste Spaltenbreiten, Download-Buttons, Fehler unter Status
- table-layout: fixed mit definierten Spaltenbreiten (Status 110px, Downloads 280px)
- Status-Pill bleibt kompakt (white-space: nowrap, nur Status-Name)
- Fehlertext jetzt als eigene Zeile unter dem Pill, max. 3 Zeilen, mit Tooltip
- Downloads als 2x2-Grid aus Pill-Buttons (DOCX / Protokoll / Summary / Transkript)
- Disabled-Buttons grau und ohne href
- HTML-Escape für Titel/Filename/Fehler
2026-05-14 02:20:21 +00:00
root
5321c8602e feat: voice-agent MVP — LXC web frontend + Mac AI worker
LXC-Frontend (FastAPI + HTML/JS):
- Audio-Upload (MP3/WAV/M4A/MP4/OGG/FLAC, max. 500 MB)
- SQLite Job-Store, BackgroundTask-Pipeline
- Job-Liste mit Live-Status, Downloads (DOCX + JSON)
- Mac-Health-Indicator im UI

Mac-Worker (FastAPI):
- /api/transcribe (lightning-whisper-mlx | faster-whisper | mock)
- /api/summarize + /api/protocol via Ollama (llama3.1:8b)
- /api/export/docx via python-docx

Deploy:
- systemd-Service, Nginx Reverse-Proxy
- deploy/install.sh: idempotentes LXC-Setup

Doku: README.md, lxc-frontend/README.md, mac-worker/README.md
2026-05-13 15:33:53 +00:00