Commit Graph
11 Commits
Author SHA1 Message Date
schakal00783andClaude Opus 5 772385713e feat(gui): Projektansicht mit Ordnern, Unterreitern und angehefteten Aufnahmen
Neuer Reiter neben "Aufnahmen": Projektordner bündeln Aufnahmen zweistufig
(Ordner + Unterreiter), auf beiden Ebenen anheftbar. Anheften geht aus dem
Projekt heraus (Dropdown mit Filter) oder über den 📌-Knopf in der
Aufnahmenliste, der Ordner und Ebene abfragt, neue Ordner anlegt und
bestehende Anheftungen zum Lösen zeigt. Klick auf eine angeheftete Aufnahme
springt in die Aufnahmen-Ansicht.

Ein Pin ist eine Referenz, kein Besitz: die Aufnahme bleibt unverändert in
der Liste und darf in mehreren Ordnern hängen.

project_pins.tab_id ist INT NOT NULL DEFAULT 0 (0 = Ordner-Ebene) statt NULL,
weil MariaDB NULL von UNIQUE-Indizes ausschließt — sonst ließe sich dieselbe
Aufnahme mehrfach auf dieselbe Ebene anheften. So macht INSERT IGNORE das
Anheften idempotent.

purgeRecording räumt project_pins mit ab; getProject blendet Aufnahmen im
Papierkorb aus, ohne deren Pin zu löschen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-02 23:05:07 +02:00
Sascha 963ce427da fix: Geisterzaehler nach Neustart + rkllm-Leck in /api/search/answer
Aufgefallen beim Nachsehen, warum die GUI bei 29:32 Audio 2h34min Analysezeit zeigte.

1. Ein systemctl restart SIGKILLt alle Kindprozesse, die DB-Zeile blieb aber auf
   analysis_status=pending mit gesetztem analysis_started_at. Die GUI zaehlte dadurch
   ewig hoch fuer Arbeit, die es nicht mehr gab. db.failStaleJobs() setzt beim Start
   pending/transcribing auf error - zum Startzeitpunkt kann kein Job legitim laufen.

2. /api/search/answer lud rkllm und gab es nie frei: 1h48min lang 82,6% RAM belegt,
   nur 237 MB verfuegbar. Genau der Zustand, den analyzeRecording und rediarize mit
   llm.stopServer() bewusst vermeiden, weil der TitaNet-Helfer daneben an OOM stirbt
   (auf #71 seinerzeit 48 Rohsprecher). stopServer() jetzt im finally der Route.
2026-07-31 15:25:49 +00:00
Sascha d5b9733e78 feat(gui+search): Inhaltssuche mit Zeitstempel-Verweis, Papierkorb (30 Tage), Titel umbenennen
Drei vom Nutzer angeforderte Funktionen.

Titel umbenennen: PATCH /api/recordings/:id/title. Neue Spalte recordings.title_locked,
damit eine spaetere KI-Analyse einen von Hand gesetzten Titel nicht wieder ueberschreibt.

Papierkorb: DELETE setzt nur noch deleted_at, nichts wird angefasst. Wiederherstellen,
endgueltig loeschen, Aufraeumlauf beim Boot und taeglich (Boot-Lauf, weil das Board oft
tagelang aus ist). Behebt nebenbei einen Altfehler: das bisherige Loeschen liess die
words-Zeilen verwaist in der DB und die WAV/MP3-Dateien auf der Karte zurueck.

Inhaltssuche: neue Tabelle passages (1689 Zeilen aus 22 Aufnahmen) mit Sprecher und
Zeitfenster je Passage. Trefferliste kommt aus SQL in Millisekunden, die formulierte
Antwort mit Belegen [1][2] wird nachgereicht (Qwen3-4B, ~140 s) - deshalb zweistufig.
Hybrid: MariaDB FULLTEXT + Passage-Embedding + Dokument-Embedding aus Titel/Zusammenfassung
(multilingual-e5-small, ONNX/CPU, taskset A55). Ohne Modell degradiert die Suche sauber
auf Volltext statt auszufallen.

Gemessene Grenzen sind dokumentiert statt geschoent: e5-small trennt auf verrauschtem
ASR-Text schwach (Kosinus-Spanne aller Passagen nur 0,8085-0,8560; eine irrelevante
Passage schlug die richtige Antwort). Dokument-Ebene und IDF-Wertung heben die richtige
Passage auf Rang 11 von 296, aber nicht in die Top-8, die das LLM liest. Naechster Hebel
waere multilingual-e5-base.

Relative Datumsangaben werden deterministisch aufgeloest, nicht vom LLM - ein falsches
Kalenderfenster waere schlimmer als gar keins.

Neu: app/search.js, app/emb.js, app/inference/embed_text.py, docs/09-suche-und-verwaltung.md
Board-Backups: .bak-search
2026-07-31 13:20:20 +00:00
Sascha 810cee1dba Diarisierung: Marker-Zwangsbedingungen absichern, Voiceprints als cannot-link; LLM: Sektions-Pfad ab 9k Zeichen
#79 verlor einen von vier Sprechern und lieferte eine 1082-Zeichen-Zusammenfassung.

Diarisierung: ein zweiter, falsch verankerter Marker verschmolz Saschas Cluster
in Merles (Kosinus -0,727). must-link prueft jetzt die akustische Aehnlichkeit,
Anker werden nach Trefferqualitaet gewichtet, und enrolled Stimmen wirken als
cannot-link -- damit ist der Box-Besitzer immer ein eigener Sprecher.

LLM: der Whole-Pass komprimierte das erste Thema weg. Sektions-Pfad jetzt ab
9000 Zeichen, kleinere Chunks, woertlicher Kontext aus dem Vorgaenger-Chunk und
ein gezielter Pruef-Aufruf gegen ueberdehnte Einzelaussagen.

#79: 4 Sprecher, Zusammenfassung ~5300 Zeichen, 7 Themenbloecke.
Regression: #70 -> 4, #68 -> 2, #74 -> 20 Sprecher unveraendert.
2026-07-29 16:09:30 +02:00
Claude 448e2b0726 Namen nachtraeglich in fertige Zusammenfassung uebernehmen
speaker_labels speichert das Label jedes Clusters im LLM-Text; relabelAnalysis()
ersetzt beim Umbenennen wortgenau in summary/todos/transcript_clean und baut die
Teilnehmerliste neu. #74: keine 'Sprecher N' mehr.
2026-07-27 23:52:28 +02:00
Claude 61531fc50a Fix: Voice-Auto-Naming tot (TitaNet bricht bei langen Schnitten ab + vergiftete Promise-Kette)
identifySpeakers() embettete den laengsten Lauf ungekuerzt; TitaNet-ONNX bricht
ab 180 s ab (broadcast 12288 by 17998) und riss den spk_embed-Helfer mit. Da
spk.js die Aufrufe ueber eine Promise-Kette serialisiert, schlugen danach ALLE
weiteren Sprecher mit 'spk beendet' fehl.

- ID_CUT_SEC=25s in identifySpeakers
- Kette in spk.js und tdChain in recorder.js failure-isoliert
- pro Name gewinnt nur der beste Kosinus (Fabio war auf 2 Clustern)
2026-07-27 17:30:43 +02:00
Claude 48fa6b4f9e Diarization v2: Fenster-Clustering mit gelernter Schwelle + Live-Namensmarker
#74 (22 Teilnehmer) ergab automatisch 44 Sprecher, dieselbe Stimme mehrfach.
Ursache gemessen: ein Zentroid pro sherpa-Cluster aus max. 3 Schnitten, fester
Kosinus auf rohen Embeddings (Kreuz-Turn-Kosinus ~0,4 = keine Trennschaerfe).

v2: sherpa liefert nur Turn-Grenzen, Identitaet aus 3-s-Fenster-Embeddings,
Mittelwert-Zentrierung, Average-Linkage-AHC, Weak-Fold, Schwelle pro Aufnahme
per 2-Gauss-EM gelernt. Validiert: #74 44->20, #70 ->4, #68 ->2.

Live-Namensmarker: Namen waehrend der Aufnahme tippen; Zeitstempel benennt
spaeter den Cluster und dient als must-/cannot-link-Zwangsbedingung.
2026-07-27 17:00:45 +02:00
schakal00783andClaude Fable 5 1016eaa095 Glossar: Eintraege bearbeitbar (PUT /api/glossary/:id + GUI-Editier-Flow)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0113vDKdSNtD3Dwcw4QMu9yV
2026-07-16 23:55:27 +02:00
schakal00783andClaude Fable 5 fdae4ce119 rkllm vor Diar/Identify freigeben — TitaNet-OOM neben residentem LLM
llm.stopServer() am Anfang von rediarize() und analyzeRecording():
spk-embed starb sonst OOM (48 Roh-Sprecher auf #71). E2E #71: 48->2
Sprecher + saubere Whole-Pass-Summary. Offen: identify-spk-Fehler
auch mit freiem RAM (Voice-Auto-Naming tot).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0113vDKdSNtD3Dwcw4QMu9yV
2026-07-16 23:04:57 +02:00
schakal00783andClaude Fable 5 2f00ddeec6 #70-Debug: Diar Weak-Fold (7->4 Sprecher), LLM-Robustheit + Whole-Pass-Modellgrenze, Thermal-Cap fuer analyzeRecording
- diar.js: Stufe-3 Weak-Fold (DIAR_MIN_TOTAL_SEC 20s) gegen Phantom-Sprecher
- llm.js: End-Instruktion, JSON-Repair, Sanity+Fallback, Chunk-Retry, Quote-Sanitizer, gelabelter/gewichteter Reduce; WHOLE_MAX 20000 (Gemma-4B-Grenze)
- rkllm_server.cpp: max_context_len 8192->12288
- server.js: analyzeRecording unter A76-Takt-Cap (95-97 Grad ungedeckelt)
- Doku Kap. 03/04/05 + Changelog

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0113vDKdSNtD3Dwcw4QMu9yV
2026-07-16 14:37:07 +02:00
Claude f9e1eb82c4 feat: komplette Codebase vom Board ins Repo gespiegelt
app/ = /opt/callrec (Node-Backend, Vite-Frontend, Python-Inferenz,
Audio-Skripte, rkllm_server.cpp), system/ = systemd-Units,
WirePlumber/PipeWire-Drop-ins, sudoers. Modelle, Binaries, Aufnahmen
und node_modules bleiben draussen (.gitignore).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-16 11:18:29 +02:00