Audit auf Optimierungspotenzial ergab:
- /api/anrufe las bei jeder Anfrage ALLE meta.json unter ablage/ neu (auch
bereits archivierte, erst danach rausgefiltert) - waechst mit der
Gesamtzahl je empfangener Anrufe, jetzt von bis zu 3 Logins alle 3s
parallel gepollt.
- /api/verlauf las bei jeder Anfrage bis zu 300 KB aus dem FreeSWITCH-Log
und scannte 4000 Zeilen per Regex, ebenfalls ungecacht.
Beide bekommen jetzt einen kurzen 2s-Cache (gleiches Muster wie
dienste.status()). Maskierung bei Testzugang arbeitet jetzt auf einer Kopie
(dict(a) je Eintrag) statt die gecachten Objekte in-place zu veraendern -
sonst haetten maskierte Rufnummern in die naechste, nicht maskierte Antwort
durchsickern koennen.
Dringlichkeit-Farben (rot/orange/gruen/grau) und der Sortierrang standen in
drei Dateien dupliziert (dashboard.py, deck.py, server.py-JS) - bei der
letzten Farbaenderung mussten alle drei von Hand synchron gehalten werden.
Jetzt eine Quelle in pipe/kategorien.py (DRINGLICHKEIT_FARBE,
DRINGLICHKEIT_RANG - Rang aus DRINGLICHKEITEN abgeleitet statt separat
gepflegt), server.py injiziert die Farben serverseitig wie schon die
Kategorie-Labels.
Getestet: RANG/FARBE identisch zum alten Stand, /api/anrufe und /api/verlauf
liefern bei zwei Anfragen innerhalb 2s dasselbe (gecachte) Ergebnis, Maskierung
veraendert nachweislich nur die Kopie.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EbdWYnDtyMgxsq4eN1WnrQ
Welches Profil (PROFIL) gerade aktiv ist, stand bisher nur in der .env - im
laufenden Leitstand war es unsichtbar, obwohl Kategorien und Ansage sich
je nach Profil komplett unterscheiden (Praxis vs. Aufzug-Notdienst).
kategorien.json bekommt ein "profil_name"-Feld (Anzeigename), neues
kategorien.PROFIL_NAME laedt es. Browsertab-Titel und Header-Untertitel im
Leitstand sowie Titel/Ueberschrift im Dashboard-Export zeigen es jetzt an.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EbdWYnDtyMgxsq4eN1WnrQ
Kategorien, Prompt und Notfall-Sicherheitsnetz waren fest auf eine
Hausarztpraxis zugeschnitten (categorize.py: KATEGORIEN-Liste, _NOTFALL_MUSTER-
Regex). Fuer einen anderen Einsatzzweck (z.B. 24h-Aufzug-Notdienst) musste
bisher Python-Code geaendert werden.
Neu: profile/<name>/ buendelt prompt.txt, kategorien.json, sicherheitsnetz.txt
und ansage.txt - alles, was fachlich vom Einsatzzweck abhaengt, an einer
Stelle. PROFIL in der .env waehlt den aktiven Ordner (Default "praxis", altes
Verhalten bleibt exakt gleich - per Test verifiziert: identische Kategorien-
Liste und Sicherheitsnetz-Muster). profile/aufzug-notdienst/ liegt als
ausformuliertes Beispielprofil bei (eigene Kategorien, eigenes
Sicherheitsnetz, eigene Ansage).
Dringlichkeitsstufen (niedrig/normal/hoch/notfall) bleiben bewusst fest -
nur die Kategorien sind einsatzzweck-spezifisch. Neues pipe/kategorien.py
laedt das Profil-Schema einmal fuer categorize.py, dashboard.py, deck.py und
server.py (Leitstand-JS: Kategorie-Label kommen jetzt vom Server statt fest
im Frontend zu stehen).
prompts/categorize_de.txt und telefon/ansage.txt sind nach profile/praxis/
umgezogen (git erkennt es als Rename).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EbdWYnDtyMgxsq4eN1WnrQ
Nimmt Anrufe einer Hausarztpraxis entgegen, transkribiert sie lokal,
kategorisiert das Anliegen und legt es strukturiert ab. Keine Patientendaten
verlassen den Rechner: Spracherkennung, Sprachmodell und Sprachsynthese laufen
lokal, im Datenpfad steht kein Cloud-Dienst.
Aufbau:
- FreeSWITCH nimmt über einen SIP-Trunk an, spielt die Ansage und nimmt auf.
- pipe.watch erkennt fertige Aufnahmen und schiebt sie durch die Pipe.
- whisper.cpp transkribiert, ein lokales Sprachmodell (Ollama) ordnet in
Kategorie und Dringlichkeit ein — mit erzwungenem JSON-Schema.
- Ablage lokal, optional Nextcloud (WebDAV) und ein Deck-Board.
- Leitstand (pipe.server): Board mit Eingang/In Bearbeitung/Rückfragen/Erledigt,
Aufnahmen zum Anhören, Verlauf und Ampeln für alle beteiligten Dienste.
Zugangsdaten und Anrufdaten liegen außerhalb des Repositorys (.env, ablage/,
telefon/). Konfiguration siehe .env.example, Einrichtung siehe README.