Skip to content

Commit 13fef4d

Browse files
doc: add comprehensive ARCHITECTURE.md describing codebase structure
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 83ec206 commit 13fef4d

1 file changed

Lines changed: 183 additions & 0 deletions

File tree

ARCHITECTURE.md

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
# WDOC Architecture
2+
3+
WDOC is a Retrieval-Augmented Generation (RAG) system that loads documents from 20+ source types, embeds them into a vector store, and supports querying, summarizing, searching, and parsing tasks via LLMs.
4+
5+
---
6+
7+
## Directory Structure
8+
9+
```
10+
wdoc/
11+
├── __init__.py # Package init, beartype setup
12+
├── __main__.py # CLI entry point (Google Fire)
13+
├── wdoc.py # Main orchestration class (~3000 lines)
14+
└── utils/
15+
├── env.py # Configuration (EnvDataclass, WDOC_* env vars)
16+
├── llm.py # LLM loading via LiteLLM, price tracking
17+
├── embeddings.py # Embedding model setup, FAISS vector store
18+
├── retrievers.py # Retriever creation (multi-query, parent, SVM, KNN)
19+
├── prompts.py # LLM prompt templates (named personas)
20+
├── batch_file_loader.py # Parallel multi-file loading with filetype inference
21+
├── load_recursive.py # Handles compound/recursive filetypes
22+
├── misc.py # Caching, hashing, token counting utilities
23+
├── errors.py # Custom exceptions
24+
├── logger.py # Loguru-based logging
25+
├── filters.py # Document filtering (regex, metadata)
26+
├── interact.py # Interactive CLI features
27+
├── loaders/ # Per-filetype document loaders
28+
│ ├── __init__.py # load_one_doc() dispatcher
29+
│ ├── pdf.py # PDF (15 parser backends)
30+
│ ├── url.py # URLs (jina, playwright, selenium)
31+
│ ├── youtube.py # YouTube videos
32+
│ ├── local_audio.py # Audio transcription (Deepgram)
33+
│ ├── local_video.py # Video extraction
34+
│ ├── epub.py # EPUB books
35+
│ ├── anki.py # Anki flashcards
36+
│ ├── logseq_markdown.py # Logseq notes
37+
│ ├── word.py # Word documents
38+
│ ├── powerpoint.py # PowerPoint
39+
│ └── json_dict.py # JSON entries
40+
├── tasks/ # Task implementations
41+
│ ├── types.py # wdocTask enum
42+
│ ├── query.py # Query: retrieve → evaluate → answer → combine
43+
│ ├── summarize.py # Recursive chunk summarization
44+
│ ├── search.py # Similarity search (no LLM answering)
45+
│ ├── parse.py # Raw document extraction
46+
│ └── shared_query_search.py
47+
└── customs/ # Custom implementations
48+
├── litellm_embeddings.py # LiteLLM-based embeddings
49+
├── binary_faiss_vectorstore.py # Binary FAISS (~32x compression)
50+
├── compressed_embeddings_cacher.py
51+
└── fix_llm_caching.py
52+
```
53+
54+
---
55+
56+
## Data Flow
57+
58+
```
59+
User Input (CLI / Python API)
60+
61+
62+
CLI Parser (__main__.py) ── Google Fire argument parsing
63+
│ Piped input detection (JSON, TOML, text)
64+
65+
wdoc.__init__() ── Configuration & validation
66+
67+
├──► Document Loading (batch_file_loader.py)
68+
│ 1. Infer filetype (regex rules + file magic)
69+
│ 2. Expand recursive types (directories, playlists, JSON arrays)
70+
│ 3. Parallel load via joblib (loaders/*.py)
71+
│ 4. Text splitting (RecursiveCharacterTextSplitter or semantic)
72+
│ 5. Metadata enrichment (hash, title, source, reading time)
73+
74+
├──► Embedding & Vector Store (embeddings.py)
75+
│ 1. Initialize embedding model (LiteLLMEmbeddings)
76+
│ 2. Cache-backed embedding layer
77+
│ 3. Build FAISS index (standard or binary-compressed)
78+
79+
└──► Task Execution
80+
81+
├─ query/search → Retrieval pipeline (see below)
82+
├─ summarize → Chunk-by-chunk summarization
83+
└─ parse → Return raw documents (no LLM)
84+
```
85+
86+
---
87+
88+
## Three LLM Roles
89+
90+
| Role | Default Model | Purpose |
91+
|------|--------------|---------|
92+
| **Main model** | `gemini-3.1-pro` | Answering, summarizing |
93+
| **Eval model** | `gemini-2.5-flash` | Document relevance checking (cheap/fast) |
94+
| **Embed model** | `text-embedding-3-small` | Dense vector embeddings |
95+
96+
All models are loaded through **LiteLLM**, supporting 100+ providers (OpenAI, Anthropic, Google, Mistral, Ollama, OpenRouter, etc.).
97+
98+
---
99+
100+
## Query Pipeline (RAG)
101+
102+
The query task uses named AI personas in sequence:
103+
104+
1. **Raphael (Rephraser)** — Expands user query into multiple alternative phrasings
105+
2. **Vector Store** — Embeds queries and retrieves top-k similar document chunks
106+
3. **Eve (Evaluator)** — Checks each chunk's relevance to the query (eval model, cheap)
107+
4. **Anna (Answerer)** — Extracts an answer from each relevant chunk (main model)
108+
5. **Carl (Combiner)** — Hierarchically clusters and combines intermediate answers into a final response
109+
110+
**Smart top-k expansion**: starts at top_k (default 200); if >90% of documents are relevant, automatically increases top_k and retries until diminishing returns.
111+
112+
---
113+
114+
## Summarization Pipeline
115+
116+
1. Split document into chunks (with overlap)
117+
2. **Sam (Summarizer)** summarizes each chunk, passing the previous chunk's summary as context
118+
3. If `summary_n_recursion > 0`, recursively summarize the summaries
119+
4. Returns a `wdocSummary` dataclass with the full result tree
120+
121+
---
122+
123+
## Configuration System
124+
125+
**`utils/env.py`** defines `EnvDataclass`, a frozen dataclass with 50+ fields loaded from `WDOC_*` environment variables. CLI arguments override env vars.
126+
127+
Key variables:
128+
- `WDOC_DEFAULT_MODEL` / `WDOC_DEFAULT_EMBED_MODEL` / `WDOC_DEFAULT_QUERY_EVAL_MODEL`
129+
- `WDOC_PRIVATE_MODE` — blocks outbound connections, redacts API keys
130+
- `WDOC_MAX_CHUNK_SIZE` — max tokens per chunk
131+
- `WDOC_FAISS_COMPRESSION` — enable binary embeddings
132+
133+
---
134+
135+
## Document Loaders
136+
137+
Each filetype has a dedicated loader in `utils/loaders/`. The dispatcher (`load_one_doc()`) dynamically imports the `load_{filetype}` function.
138+
139+
**Supported types**: `pdf`, `txt`, `word`, `powerpoint`, `epub`, `url`, `youtube`, `youtube_playlist`, `online_pdf`, `online_media`, `local_audio`, `local_video`, `anki`, `logseq_markdown`, `json_dict`, `recursive_paths`, `json_entries`, `toml_entries`, `string`, `ddg` (DuckDuckGo search).
140+
141+
**PDF parsing** is particularly robust: 15 different parser backends are evaluated and the best result is selected automatically.
142+
143+
**URL loading** has multiple fallbacks: Jina → Playwright → Selenium → others.
144+
145+
---
146+
147+
## Caching Layers
148+
149+
1. **LLM response cache**`SQLiteCacheFixed` prevents duplicate LLM calls
150+
2. **Embedding cache**`CacheBackedEmbeddings` avoids re-embedding identical content
151+
3. **Loader cache**`joblib.Memory` caches parsed documents
152+
4. **Document hashing** — content_hash / file_hash / all_hash prevent reprocessing
153+
154+
---
155+
156+
## Error Handling
157+
158+
Custom exceptions in `utils/errors.py`:
159+
- `NoDocumentsRetrieved` — no documents found in vector store
160+
- `NoDocumentsAfterLLMEvalFiltering` — all retrieved docs deemed irrelevant
161+
- `NoRelevantIntermediateAnswers` — LLM found no useful answers
162+
- `TimeoutPdfLoaderError` — PDF parsing timeout
163+
164+
Fallback strategies: multiple parser/loader backends tried in sequence before failing.
165+
166+
---
167+
168+
## Key Dependencies
169+
170+
| Category | Libraries |
171+
|----------|-----------|
172+
| RAG framework | LangChain, LangChain-Community |
173+
| LLM abstraction | LiteLLM, LangChain-LiteLLM |
174+
| Vector store | FAISS |
175+
| Text splitting | Chonkie (semantic), LangChain (recursive) |
176+
| Document parsing | Unstructured, OpenParse, BeautifulSoup, lxml |
177+
| Media | yt-dlp, Deepgram-SDK, pydub |
178+
| Web scraping | Playwright, Selenium, Jina |
179+
| Parallel processing | Joblib |
180+
| Type checking | Beartype (runtime) |
181+
| Logging | Loguru, Rich |
182+
| Observability | Langfuse |
183+
| Caching/storage | SQLAlchemy, LMDB (PersistDict) |

0 commit comments

Comments
 (0)