Turn evolving manuals, SOPs, and operational documents into persistent wiki pages with citations and revision history. New documents can extend an existing page; every addition keeps its source evidence, and every changed page gets a reviewable revision.
Documents → deduplicate → chunk → extract claims → match pages → append → diff.
Runs locally with FastAPI + SQLite, no API key. The default demo extractor copies sentences; a conservative local matcher chooses a page, creates one when no topic terms overlap, or asks for a choice when the result is unclear. An optional Anthropic adapter requests atomic claims with verified chunk quotes. All pages remain drafts. Canonical pages are the goal; lexical matching does not guarantee one page per real-world subject.
Query-time RAG retrieves context and synthesizes an answer when someone asks a question. LLMWiki maintains a page that can be inspected, incrementally edited, and compared across revisions before a question is asked.
RAG and GraphRAG can also persist indexes, update their corpora, and provide citations. They can complement this project: retrieval could use the maintained pages as context. The distinction is the durable page and its update history, not a claim that one architecture is universally better.
Use Python 3.11 or newer (the commands below use 3.12):
python3.12 -m venv .venv
source .venv/bin/activate
python -m pip install -e .
python scripts/demo.pyThe demo uses the real API routes through FastAPI's in-process client. No running server, API key, or external service is needed. It explicitly selects demo mode and uses a fresh temporary database, so every run starts at revision 1 and leaves your existing database untouched.
- Import the maintenance manual, chunk it, extract three demo claims, and create revision 1.
- Create separate emergency-exit and tool-storage pages.
- Import the supplement; automatically match the existing maintenance page without a target page ID.
- Append two claims to that same page, producing revision 2; display the stored diff.
- Trace a new claim through its chunk to the source and verbatim evidence.
- Re-upload the supplement: reuse source/chunk/claim IDs and keep revision 2.
- Verify both unrelated pages and their histories are unchanged; show the saved decisions.
See the example corpus, actual demo transcript, and
To keep your own wiki and explore the API:
python -m uvicorn app.main:app --reloadOpen interactive API docs. The server stores data in
data/wiki.db. Set LLMWIKI_DB_PATH before starting the process for another
SQLite file. The demo intentionally ignores that setting and uses its own temporary file.
flowchart TD
D[Operational documents] --> S[Exact-content source deduplication]
S --> C[Chunks]
C --> E[ClaimExtractor port]
E --> DEMO[Default local demo adapter]
E --> LLM[Optional Anthropic adapter]
DEMO --> V[Claims with chunk ID and evidence quote]
LLM --> V
V --> M[Local page matcher]
M --> DEC{Matching decision}
DEC -->|Clear match or new topic| P[Create or append to wiki draft]
DEC -->|Ambiguous| R[Return candidates for explicit choice]
R --> P
P --> H[Revision history and version diffs]
H --> TRACE[Page to claim to chunk to source]
DB[(SQLite)] --- S
DB --- V
DB --- P
DB --- H
M --> AUDIT[Saved candidates, scores, reasons]
AUDIT --> DB
extractors.py defines the extraction port; llm.py contains Anthropic HTTP code.
services.py coordinates extraction; matching.py is a pure local matcher;
storage.py owns SQLite transactions. Routes call these application interfaces
without selecting or constructing an Anthropic client.
Extraction finishes before page matching starts. Matching, page changes, revision snapshots, and the decision record share one SQLite write transaction. An error while recording the decision rolls back the page change. This small application uses no worker queue, vector database, or agent framework.
POST /sourceswithtitleandcontentregisters a source and its chunks.POST /sources/{id}/processwith no body extracts claims if needed, then decides whether to create a page, append to one page, returnnoop, or request review.GET /sources/{id}/decisionsretrieves saved decisions, oldest first.
The processing response has decision and page fields. A decision records its
method (local-v1 or manual), action, reason, candidate page IDs/titles/scores,
claim coverage, shared terms, selected page, input/added claim IDs, before/after
revision numbers, and timestamp. These are stored snapshots, so later page edits
do not rewrite the audit trail. Each processing attempt records a decision;
a noop records no new page revision.
The local matcher compares English topic words in claims and existing page titles
and claims. It removes common instruction words and applies simple plural
normalization. Automatic matching requires every incoming claim to have at least
two shared topic terms and a score of at least 0.5, with a 0.15 lead over the
runner-up. An existing claim ID also counts as a direct match. If all incoming IDs
already belong to exactly one page, that page is reused without a revision.
Scores are heuristics, not confidence probabilities.
No shared topic terms means a new draft, titled from the source. Weak, competing,
mixed-topic, or unsupported-language matches return needs_review with no page
change. A title collision also requires review. These rules are tested on small
English examples; paraphrases and synonyms can still produce separate pages.
To resolve an ambiguous decision, repeat processing with one explicit choice:
{"page_id": "existing-page-id"}or:
{"new_page_title": "A distinct topic title"}A conflicting existing title returns 409; choose its ID explicitly. A batch is
routed to at most one page. Mixed-topic sources need review and can use the manual
claim-selection endpoints below. This is append-only updating: old claims and
revisions are preserved, and unrelated pages are not rewritten. Automatic fact
reconciliation and contradiction detection are not implemented.
Default: LLMWIKI_EXTRACTOR=demo. DemoClaimExtractor calls
extract_demo_claims(), which copies sentences into candidate claims. These are
not semantically verified atomic facts. A key in the environment alone never
activates a model call. Both the demo and acceptance workflows stay in demo mode.
Optional: LLMWIKI_EXTRACTOR=anthropic. For a server that uses real extraction:
export LLMWIKI_EXTRACTOR=anthropic
export ANTHROPIC_API_KEY="your-api-key"
# Optional model override; otherwise claude-haiku-4-5-20251001 is used.
export ANTHROPIC_MODEL="claude-haiku-4-5-20251001"
python -m uvicorn app.main:app --reloadAnthropicClaimExtractor uses the Messages API
with structured JSON output.
The prompt requests one supported assertion per claim, preserving conditions and
negations. The adapter validates the schema, rejects blank text and extra fields,
and requires every evidence quote to occur literally in its originating chunk.
All chunks must validate before any claims are saved. A malformed or incomplete
response rejects that extraction; it does not silently substitute demo output.
Quote presence does not prove semantic faithfulness or atomicity. Live-model
quality and costs have not been evaluated; tests use mocked responses only.
Configuration is read when fresh extraction is requested. Missing/invalid
configuration or denied credentials return 503; provider/output failures return
502; timeouts return 504. The server can still start and serve stored data
without a key. Successfully saved extraction is reused, including an empty list,
even if the configured mode changes later. Re-extraction with another model is
not currently an API operation. Simultaneous first requests can perform redundant
model calls, although only one saved claim set wins. Real calls send chunk text
to Anthropic and may incur charges. Matching stays local in both modes.
The existing manual routes remain available:
POST /sources/{id}/claims: run the configured extractor or return cached claims.GET /sources/{id}and/sources/{id}/claims: retrieve saved input and claims.GET /claims/{id}and/chunks/{id}: follow provenance to the source.POST /pages: create a draft fromtitleand a non-emptyclaim_idslist.POST /pages/{id}/claims: append selected IDs, keeping existing claims and order.PATCH /pages/{id}: changetitleor supply the complete orderedclaim_idsselection. Omitted fields stay unchanged; omitting an old ID from the supplied list removes it from the current page, preserving its claim and past snapshots.GET /pagesand/pages/{id}: list drafts or read a full page with claim evidence.GET /pages/{id}/revisionsand/pages/{id}/revisions/{number}: inspect history.GET /pages/{id}/diff?from_revision=1&to_revision=2: compare saved snapshots.GET /pages/search?q=maintenance: search current titles and Markdown bodies.GET /sources/{id}/pagesand/claims/{id}/pages: inspect current reverse references.
Search returns IDs, titles, revision numbers, and a Markdown preview of at most
200 characters. It uses literal substrings (including % and _), handles Chinese
text, and ignores ASCII case. Title matches rank first. Search, reverse references,
and decision history accept limit (1–100, default 20) and offset (default 0).
Reverse references group all matching claim IDs per page; history-only citations
are excluded. Missing IDs return 404; invalid inputs return 422. Reading
unextracted claims returns 409; reading a source's references before extraction
returns an empty list without triggering extraction.
Diffs include added/removed/modified claims, title changes, relative claim order, and a unified Markdown diff. Identity is based on claim IDs. Same-version and reverse comparisons are supported. Manual page creation always creates a draft; use processing or an update route to reuse a page.
- Sources deduplicate by SHA-256 of exact UTF-8 content. Titles do not affect the hash; whitespace, case, and line-ending changes are different content.
- SQLite stores sources, chunks, claims, ordered page associations, full revision snapshots, and matching decisions. IDs and evidence survive process restarts.
- Repeating saved extraction or adding existing claim IDs reuses them. Equal wording from a different source keeps separate claim IDs and evidence; semantic deduplication and multi-source citations on one canonical claim are not implemented.
- A real page change writes one revision. No-op updates write none. Concurrent additions serialize, preserving both updates; concurrent first processing checks the current page set inside the same write transaction before creating a page.
- Startup creates missing tables without deleting existing data. Legacy pages get one baseline revision whose timestamp is the snapshot time, not an invented original creation time. Historical duplicate sources and their original citations remain readable; re-uploads reuse the earliest content owner.
- The source of truth is your SQLite file.
data/, environment files, and caches are ignored by Git; there is no hosted backup service.
PYTHONPATH=src python -m pytest -q
python scripts/acceptance.pyThe suite covers persistence across processes, transaction rollback, concurrency, source deduplication, revisions/diffs, search, reverse references, extractor validation and failures, automatic matching, ambiguous decisions, manual resolution, and the operational example corpus. Test defaults force demo mode and block real HTTP transports. Anthropic tests use fake responses or an in-memory mock transport; no test is intended to make paid API calls.
Acceptance runs both the original manual v0.1 scenario and the new automatic corpus workflow in temporary databases. It returns a nonzero exit code on failure. The original manual API behavior remains covered.
This is a local portfolio backend, not an operational authority. Known limits:
- Fixed 120-word chunks can cut sentences and normalize whitespace. Evidence is checked against the stored chunk; raw-source character offsets are not tracked.
- The matcher is English lexical matching. It is not an embedding/semantic model, and its thresholds have not been evaluated on a representative real corpus.
- One processing request handles one source as one batch. Mixed-topic segmentation, conflict detection, supersession, and automatic fact reconciliation remain open.
- Pages are composed claim bullets, not LLM-written summaries. No production review workflow, authentication, PDF parser, background queue, or chat UI is included.
The design documents in docs/00–06 describe the broader
research direction, including components not implemented here. The current matcher
and transaction contract are documented in automatic processing.
The project was inspired by prior operational-document GraphRAG work; no proprietary
source documents are included.
MIT — see LICENSE.