- Python 90.5%
- JavaScript 7.3%
- CSS 1.6%
- HTML 0.3%
- Dockerfile 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
bookclaw-app:dev only exists locally, so `docker compose pull` failed with "pull access denied". All app services now set `pull_policy: build`: pull only refreshes postgres/redis/ollama, `up` rebuilds the app image from the build cache. README documents the day-to-day Docker calls (status, readyz, logs, first-user registration, stop, pull/rebuild, QA) and the one-time Ollama model pulls. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
| .github/workflows | ||
| docs | ||
| frontend | ||
| migrations | ||
| src/bookclaw | ||
| tests | ||
| .dockerignore | ||
| .env.example | ||
| .gitignore | ||
| agents.md | ||
| alembic.ini | ||
| compose.yaml | ||
| Dockerfile | ||
| LICENSE | ||
| pyproject.toml | ||
| README.md | ||
| uv.lock | ||
BookClaw
Personal Book Operating System – KI-gestütztes, selbst hostbares System zur Entwicklung von Büchern aus gesprochenen Gedanken, Interviews, Notizen und Dokumenten.
Der Mensch erzählt ungeordnet. BookClaw hilft beim Erinnern, Ordnen, Prüfen, Strukturieren und Schreiben, ohne fehlende Fakten zu erfinden.
Status
Frühe Entwicklung. Aktuell umgesetzt:
- Epic 1 – Foundation: Repo-Grundgerüst, FastAPI, Konfiguration, PostgreSQL/Alembic, Redis, Healthchecks, CI.
- Epic 2 – Project + Source: Project CRUD, BlobStorage-Abstraktion (lokal), Upload mit Streaming-SHA-256-Hashing, MIME-/Größenvalidierung, Source Immutability.
- Epic 3 – Transcription:
ProcessingRun-Audit-Trail, versionierteTranscript/TranscriptSegment-Domäne,TranscriptionProvider-Abstraktion mit Faster-Whisper-Adapter (optionale Extra-Dependency), Redis-Job-Queue, Worker-Entrypoint, idempotente Transkriptions-Requests. - Epic 4 – Knowledge:
Claim/Memory/Person/Place/Topic-Domäne mit Provenance (SourceReferencebis auf Transkript-Segment-Ebene), Memory-/Entity-Agent mit strukturierter LLM-Extraktion (Ollama-Adapter, Schema-Validierung + begrenzter Repair-Retry), Confirm/Reject-Workflow für Claims (nie automatisch aus Confidence abgeleitet), Volltext- und semantische Suche (pgvector), Herkunftsprüfung (UC-10). - Epic 5 – Timeline:
TemporalRange/PartialDatemit präzisionsehrlicher Validierung (nie erfundene Monats-/Tagesangaben),Event-Domäne, Timeline-Agent als dritter Agent im Extraction-Run, Contradiction-Domäne mit menschlichem Resolution-Workflow (nie automatisch aus Confidence entschieden), Embedding-basierte Kandidatenvorauswahl für die Widerspruchserkennung (UC-06, UC-07). - Epic 6 – Interview:
Question-Domäne (exponiert als "interviews"-Ressource gemäß Architecture-API-Sketch), Interview Agent mit Gap-/Wiederholungs-bewusstem Kontext (bekannte Entitäten, abgedeckte Jahre, alle bisherigen Fragen), "eine offene Frage gleichzeitig" ohne separates Sperr-Feld, typisierte Antworten laufen als verbatim Source/Transcript durch dieselbe Provenienzkette wie gesprochene und stoßen automatisch die Extraktion an (UC-04). - Epic 7 – Outline + Writer:
Outline-Aggregat (Book →BookPart→Chapter→Scene) mit Structure Agent, der Titel/Zusammenfassungen aus vorhandenem Material vorschlägt, aber bewusst keine Claim-IDs errät (UC-08); eine Outline bleibtproposedbis der Mensch sie explizit bestätigt (Human-in-the-Loop, erst dann darf ein Kapitel entworfen werden); Writer Agent erzeugt quellengebundene Kapitelentwürfe aus per semantischer Suche gefundenen Claims, filtert dabei jede vom Modell zurückgemeldete, aber nicht tatsächlich angebotene Claim-ID defensiv heraus (UC-09); jeder Entwurf ist eine neue, nie überschriebeneManuscriptRevision. - Epic 8 – Export: vollständiges Projektarchiv als ein Zip aus reinem YAML/Markdown —
Projekt, Sources-Manifest (Originalquellen optional per Flag einschließbar), Transkripte,
das gesamte Knowledge-Modell (Claims/Memories/Personen/Orte/Themen/Timeline/
Contradictions), Interviews, Outline und Manuskript (je Kapitel die neueste Revision als
Markdown), plus der komplette
ProcessingRun-Audit-Trail (UC-11); braucht als einziger Job-Typ keinen AI-Gateway-Aufruf — reine deterministische Serialisierung aus bereits vorhandenen Daten, mitunzipund einem Texteditor lesbar, keine proprietäre BookClaw-Installation zum Lesen nötig (Lastenheft §14). - Project Agent (UC-01):
ProjectProposal-Domäne, aus einem frei formulierten Ideentext vorgeschlagen (Titel, Buchtyp, Themen, Zielgruppe, Erzählstimme, eine grobe Kapitel-Titel-Liste, offene Annahmen/Fragen) — läuft bewusst vor jedemProject(docs/ARCHITECTURE.md§17), wofürProcessingRun.project_idals einzige Ausnahme in der ganzen Codebase nullable ist; „AI Proposal ist nicht direkt das persistierte Projekt ohne Bestätigung" — erst eine explizite Bestätigung erzeugt das echteProject. - Web-UI, alle zwölf Hauptbereiche (Lastenheft §15):
frontend/, reines HTML/CSS/JS ohne Build-Schritt oder npm-Abhängigkeit, als Hash-Router-SPA (frontend/js/router.js+frontend/js/views/*.js), vonbookclaw dev/serveunter/mitausgeliefert. Dashboard (Statistiken + Projekt anlegen, sowohl manuell als auch über UC-01s Ideen-Flow), Aufnahme/Import (MediaRecorder-Aufnahme direkt im Browser + Datei-Import), Inbox (Erinnerungen + Claim-Bestätigung), Timeline, Personen/Orte/Themen, Interview (geführter Ein-Fragen-Flow), Offene Fragen (Übersicht/Audit über alle Fragen), Widersprüche, Outline, Manuskript, Quellen und Einstellungen (Projekt bearbeiten, KI-Modelle & Provider je Projekt auswählen, Export). Durchgängige visuelle Trennung von KI-Vorschlägen/Unsicherheit von bestätigten Fakten (Developer Guidelines §31); keininnerHTMLim gesamten Frontend — jeder API-Text kommt übercreateElement/textContent. Dazu ein dreizehnter, nicht in Lastenheft §15 aufgeführter Bereich "Warteschlange": Länge/Inhalt der Redis-Job-Queue sowie alle Verarbeitungsvorgänge des aktuellen Projekts inkl. Fehlermeldung — die erste Anlaufstelle für "warum passiert nichts". Details und bewusste Auslassungen infrontend/README.md. - Login, Rollen, Projekt-Löschung (Lastenheft §23): Registrierung/Login/Logout über
ein httpOnly-JWT-Session-Cookie (
auth/,api/v1/auth.py), Rollenuser/admin(der erste je registrierte User wird automatisch Admin), jedesProjectträgt einenowner_user_id— jede weitere Tabelle hängt bereits anproject_id, sodass jede Eingabe transitiv auf einen User zurückführbar ist. Autorisierung (api/authorization.py): Projekt-Zugriff nur für den Besitzer oder einen Admin, sonst 403. Settings-UI: "Projekte löschen" — paginierte Liste mit Checkboxen, Bulk-Löschung entfernt ein Projekt vollständig inkl. aller gespeicherten Daten (PostgresON DELETE CASCADEdurch die gesamte Objekthierarchie plus explizites Löschen der zugehörigen Blobs). Bekannte, bewusste Lücke: Autorisierung sitzt auf Projekt-Ebene; Endpunkte, die eine Unterressource direkt per ID adressieren (GET /sources/{id}o. Ä.), prüfen den Besitz noch nicht — siehedocs/PROJEKT_OK.md.
Bewusst noch nicht enthalten (spätere Epics/Roadmap): die S3-Storage-Implementierung
(Protokoll steht, LocalBlobStorage ist die einzige konkrete Implementierung),
Sprechererkennung/Cloud-STT (Lastenheft §9, "Später"), manuelles Bearbeiten des
Manuskripttexts selbst (jede Revision entsteht bisher nur über den Writer Agent), eine
Merge-UI für Personen/Orte/Themen, und DOCX/EPUB/PDF-Export (Lastenheft §14 "Später").
API (Auszug)
POST /api/v1/auth/register (E-Mail+Passwort; der erste je registrierte User wird Admin)
POST /api/v1/auth/login (setzt das httpOnly-Session-Cookie)
POST /api/v1/auth/logout
GET /api/v1/auth/me
GET /api/v1/users (Admin-only: alle User)
PATCH /api/v1/users/{id}/role (Admin-only: Rolle user/admin setzen)
POST /api/v1/project-proposals (UC-01, freier Ideentext, 202 Accepted)
GET /api/v1/project-proposals
GET /api/v1/project-proposals/{id}
POST /api/v1/project-proposals/{id}/confirm (Human-in-the-Loop -> erzeugt das Project)
POST /api/v1/projects
GET /api/v1/projects
GET /api/v1/projects/{id}
PATCH /api/v1/projects/{id} (title/description/privacy_profile/model_configuration —
nur gesendete Felder ändern sich; model_configuration ersetzt
als Ganzes: Pro-Projekt-Override der Whisper/KI-Provider+Modelle,
siehe "KI-Modelle & Provider" im Settings-UI)
GET /api/v1/projects/paginated?limit=&offset= (Settings "Projekte löschen"-Liste, nur eigene Projekte)
DELETE /api/v1/projects (Body: {"project_ids": [...]}; vollständige Löschung inkl. Daten)
POST /api/v1/projects/{id}/sources (multipart upload)
GET /api/v1/projects/{id}/sources
GET /api/v1/sources/{id}
POST /api/v1/sources/{id}/transcriptions (enqueue, 202 Accepted)
GET /api/v1/sources/{id}/transcripts (alle Versionen)
GET /api/v1/transcripts/{id}
GET /api/v1/processing-runs/{id} (Status pollen)
GET /api/v1/projects/{id}/processing-runs (alle Verarbeitungsvorgänge dieses Projekts)
GET /api/v1/queue (Diagnose: Länge + Inhalt der Redis-Warteschlange)
POST /api/v1/transcripts/{id}/extractions (enqueue, 202 Accepted)
GET /api/v1/projects/{id}/claims
GET /api/v1/projects/{id}/claims/search?q=...&mode=fulltext|semantic
GET /api/v1/claims/{id}
GET /api/v1/claims/{id}/sources (Herkunft, UC-10)
POST /api/v1/claims/{id}/confirm
POST /api/v1/claims/{id}/reject
GET /api/v1/projects/{id}/memories
GET /api/v1/memories/{id}
GET /api/v1/projects/{id}/{people,places,topics}
GET /api/v1/projects/{id}/timeline (UC-06, sortiert; unsicher zuletzt)
POST /api/v1/projects/{id}/contradictions/detection (enqueue, 202 Accepted)
GET /api/v1/projects/{id}/contradictions
GET /api/v1/contradictions/{id}
POST /api/v1/contradictions/{id}/resolution
POST /api/v1/projects/{id}/interviews (enqueue next question, 202 Accepted)
GET /api/v1/projects/{id}/interviews (alle Fragen des Projekts)
GET /api/v1/interviews/{id}
POST /api/v1/interviews/{id}/answers (Text-Antwort -> Source/Transcript -> Extraction)
POST /api/v1/interviews/{id}/skip
POST /api/v1/projects/{id}/outlines (enqueue Outline-Vorschlag, 202 Accepted)
GET /api/v1/projects/{id}/outlines (aktuelle Outline)
GET /api/v1/outlines/{id}
POST /api/v1/outlines/{id}/confirm (Human-in-the-Loop, UC-08)
POST /api/v1/chapters/{id}/drafts (enqueue Kapitelentwurf, 202 Accepted, UC-09)
GET /api/v1/chapters/{id}/drafts (alle Revisionen dieses Kapitels)
GET /api/v1/manuscript-revisions/{id}
POST /api/v1/projects/{id}/exports (enqueue Projektarchiv, 202 Accepted, UC-11)
GET /api/v1/projects/{id}/exports (alle Exports dieses Projekts)
GET /api/v1/exports/{id}
GET /api/v1/exports/{id}/download (Zip-Download)
Dokumentation
docs/LASTENHEFT.md– fachliche Anforderungen, Use Cases, MVP, Roadmapdocs/ARCHITECTURE.md– technische Zielarchitekturdocs/DEVELOPER_GUIDELINES.md– verbindliche Entwicklungsregelndocs/adr/– Architekturentscheidungen
Diese drei Spezifikationsdokumente sind bei jeder Weiterentwicklung verbindlich zu beachten
(siehe DEVELOPER_GUIDELINES.md §35, Konfliktreihenfolge: Datenintegrität/Privacy >
Lastenheft > Architecture > Guidelines > lokale Implementierungsbequemlichkeit).
Lokale Entwicklung
Der Manuskripteditor (frontend/) wird vom API-Prozess unter http://localhost:8000/
mitausgeliefert — kein separates Setup, keine CORS-Konfiguration nötig. OpenAPI-Doku unter
/docs.
Healthchecks:
GET /api/v1/healthz– Liveness (keine Abhängigkeiten)GET /api/v1/readyz– Readiness (prüft PostgreSQL und Redis)
Variante A – alles in Docker (kein Python/uv auf dem Host)
Voraussetzungen: nur Docker/Podman mit Compose.
git clone <repo-url>
cd bookclaw
docker compose up -d --build # postgres, redis, migrate, api, worker
API danach unter http://localhost:8000. api und worker teilen sich ein Image
(Dockerfile); migrate (alembic upgrade head) läuft als einmaliger Service vor beiden.
./src und ./frontend sind ins Image gemountet, bookclaw dev lädt Änderungen per
Autoreload — für Code-Edits ist kein Rebuild nötig (nur bei geänderten Dependencies:
docker compose build).
Python-Abhängigkeiten sind über uv.lock reproduzierbar fixiert; Docker und CI verwenden
das Lockfile im Locked-Modus. Nach einer Änderung an pyproject.toml deshalb uv lock
ausführen und das aktualisierte uv.lock mit committen.
Konfiguration kommt aus dem environment:-Block in compose.yaml (Hostnamen zeigen auf
die Compose-Services, nicht localhost). Eine .env auf dem Host wird von den Containern
nicht gelesen.
Stack prüfen und manuell testen
docker compose ps # api/worker "Up", postgres/redis "healthy"
curl -s localhost:8000/api/v1/readyz # {"status":"ok", ...} für PostgreSQL und Redis
docker compose logs -f api worker # Logs mitlesen, z. B. wenn ein KI-Job hängt
Im Browser http://localhost:8000/ öffnen und registrieren — der erste registrierte User
wird automatisch Admin. Danach ein Projekt anlegen und die Bereiche durchklicken; für
Outline, Writer und Interview muss Ollama mit Modellen laufen (siehe unten).
Stoppen:
docker compose down # Daten bleiben in den Volumes erhalten
docker compose down -v # inkl. Datenbank- und Blob-Volumes löschen
Images aktualisieren
bookclaw-app:dev wird nur lokal gebaut und liegt in keiner Registry. Die App-Services
haben deshalb pull_policy: build: docker compose pull aktualisiert nur die fremden
Images (postgres, redis, ollama) und überspringt das eigene, up baut es bei Bedarf aus dem
Build-Cache neu.
docker compose pull # postgres/redis/ollama aktualisieren
docker compose up -d --build # eigenes Image neu bauen und starten
Automatische Tests
QS im selben Image (Profil qa, erreicht Postgres/Redis über das Compose-Netz):
docker compose run --rm qa # pytest (volle Suite)
docker compose run --rm qa ruff check .
docker compose run --rm qa ruff format --check .
docker compose run --rm qa mypy src
Transkription (faster-whisper) ist im Standard-Image bereits enthalten
(INSTALL_SPEECH: "1" im x-bookclaw-build-Anchor in compose.yaml, für alle vier
App-Services identisch, da sie sich einen Image-Tag teilen). Für einen schlankeren Build
ohne Transkription den Wert dort auf "0" setzen und neu bauen.
Variante B – uv auf dem Host, nur Infra in Docker
Voraussetzungen: Docker/Podman, uv, Python 3.13+.
git clone <repo-url>
cd bookclaw
cp .env.example .env
docker compose up -d postgres redis
uv sync --extra dev
alembic upgrade head
uv run bookclaw dev
Worker starten (verarbeitet u.a. Transkriptions-Jobs aus der Redis-Queue):
uv sync --extra dev --extra speech # faster-whisper ist eine optionale Extra
uv run bookclaw worker
Ohne die speech-Extra läuft alles andere normal weiter; nur die tatsächliche
Modell-Inferenz schlägt mit einer klaren Fehlermeldung fehl (pip install 'bookclaw[speech]').
Optionaler lokaler Ollama-Provider (beide Varianten)
docker compose --profile ollama up -d ollama
Einmalig die Standardmodelle laden (BOOKCLAW_EXTRACTION_MODEL/WRITER_MODEL und
BOOKCLAW_EMBEDDING_MODEL); sie liegen danach im Volume bookclaw_ollama_data:
docker compose exec ollama ollama pull llama3.1
docker compose exec ollama ollama pull nomic-embed-text
Externe Provider auf dem Host (LM Studio o. Ä.), außerhalb von Docker
Statt des ollama-Compose-Service kann api/worker (Variante A) einen beliebigen
Server ansprechen, der auf derselben Maschine, aber außerhalb von Docker läuft — z. B. LM
Studio. Zwei getrennte Themen:
Whisper (Transkription) ist kein Netzwerk-Provider — faster-whisper läuft als
eingebettete Python-Bibliothek im worker-Prozess selbst, und LM Studio bietet ohnehin
keine Sprach-zu-Text-Funktion. Um faster-whisper außerhalb von Docker laufen zu lassen
(z. B. für GPU-Zugriff), den Worker per Variante B auf dem Host starten
(uv run bookclaw worker, mit der speech-Extra) — er verbindet sich dabei ganz normal
über localhost mit dem in Docker laufenden Postgres/Redis (Ports sind in compose.yaml
nach außen freigegeben).
LLM-Aufgaben (Extraktion, Interview, Outline, Widersprüche, Writer, Embeddings): LM
Studio spricht OpenAI's /chat/completions+/embeddings-API, nicht Ollamas natives
/api/chat — deshalb ist es als eigener Provider openai_compatible verdrahtet
(ai/providers/openai_compatible.py), nicht einfach über BOOKCLAW_OLLAMA_BASE_URL
erreichbar. Einrichtung:
- LM Studio starten, den gewünschten Server-Port merken (Standard:
1234) und mindestens ein Chat-Modell sowie optional ein Embedding-Modell laden. compose.yamlsetzt fürapi/workerbereitsextra_hosts: host.docker.internal:host-gateway— Container erreichen den Host also über den Hostnamenhost.docker.internal, nichtlocalhost.BOOKCLAW_OPENAI_COMPATIBLE_BASE_URLzeigt standardmäßig schon aufhttp://host.docker.internal:1234/v1(compose.yaml); anpassen, falls LM Studio einen anderen Port nutzt.BOOKCLAW_OPENAI_COMPATIBLE_API_KEYnur setzen, falls der Server das verlangt (LM Studio normalerweise nicht).- Pro Projekt in den Einstellungen ("KI-Modelle & Provider") die gewünschte(n) Aufgabe(n)
auf Provider
openai_compatibleumstellen und den exakten Modellnamen aus LM Studio eintragen (sieheGET http://localhost:1234/v1/modelsfür die genaue Schreibweise).
Für Variante B (Worker außerhalb von Docker) direkt localhost statt
host.docker.internal verwenden — dort läuft ohnehin kein Docker-Networking dazwischen.
Qualitätssicherung
ruff check .
ruff format --check .
mypy src
pytest
Architekturprinzipien (Kurzfassung)
- Sources sind unveränderlich (Source Immutability).
- KI-Ausgaben sind keine Wahrheit – Confirmation ist eine Nutzerentscheidung.
- Provenance ist Pflicht: jeder Claim führt auf seine Quelle zurück.
- Unsicherheit ist Daten, keine erfundene Präzision.
- Domain-Logik ist providerunabhängig (kein Vendor-Lock-in).
- Kein automatischer Fallback von lokal nach Cloud.
- Menschliche Entscheidungen überschreiben KI-Entscheidungen.
- Offener Export ist Kernfeature, kein Nachgedanke.
Details siehe docs/DEVELOPER_GUIDELINES.md.
Lizenz
Siehe LICENSE.