Package Layout
The deep, domain-grouped src/ragspine map — what lives in each of the nine domains, the dependency direction between them, and how the core stays SDK-free.
RAGSpine uses a deep, domain-grouped layout: organize by domain/feature, never by
technical layer, so the folder path locates a file before you read its name. A package splits
the moment it holds a second responsibility. There are nine top-level domains under
src/ragspine/, each with a thin __init__.py (a docstring + a Submodules: index) that
lazy-loads its children.
The tree
The same map, annotated (from the project's README.md):
src/ragspine/
├── common/ cross-cutting: company profile, sensitivity, glossary, observability
├── extraction/ documents → a frozen StyledGrid intermediate representation (IR)
│ ├── extractors/ xlsx / pptx / pdf (digital + scanned/OCR), style- & color-aware
│ ├── routing/ per-page PDF triage (digital vs scanned vs export)
│ ├── color/ controlled color-semantics registry
│ └── verification/ dual-channel cross-check → review queue
├── ingestion/ IR/text → stores
│ ├── structured/ fact ingestion + batch manifest ledger (idempotent)
│ ├── narrative/ document chunk ingestion + extraction
│ └── review/ human review-queue state machine (SME)
├── storage/ fact store (numeric) + chunk store (narrative), sqlite, full lineage
├── retrieval/ narrative RAG
│ ├── chunking/ paragraph-granular chunker + versioned chunk store
│ ├── lexical/ Okapi BM25 (CJK uni+bigram) + RRF fusion
│ ├── vector/ injectable embedding backends (default: none = pure BM25)
│ ├── rerank/ LLM listwise reranker (RRF-fallback)
│ └── link/ adapter wiring retrieval into the agent (strips RESTRICTED at exit)
├── agent/ intent parsing, clarification gateway, tool-use loop, llm provider
├── eval/ QA + extraction evaluation harnesses with baseline gates
├── service/ FastAPI app, RQ task queue, ingestion jobs, FAQ short-circuit cache
└── pipeline/ static topology export (.topology() → Mermaid / DOT / JSON)What lives where
Dependency direction
The layering is strict and acyclic — common (and storage) sit at the bottom depending on
nothing first-party; agent, extraction, and retrieval build on them; ingestion,
eval, and service are coordinators; pipeline is an isolated leaf everyone may delegate
into but which imports no one.
service (composition root — imports almost everything)
/ | \
ingestion eval …
| \ \
extraction agent retrieval ──► pipeline (retrieval delegates .topology() into pipeline)
\ | \ /
\ storage /
\ | /
common (leaf — depends on nothing first-party)Prop
Type
The core imports zero SDKs. Every external dependency enters through a Protocol, and the
only files touching heavy SDKs (anthropic / openai / fastapi / redis / docling) are the
edge adapters. The anthropic import is lazy — inside AnthropicProvider.__init__, not at module
top — and submodules load lazily (PEP 562). A top-level import ragspine eagerly loads no domain
and pulls no third-party SDK, so the engine runs fully offline with the deterministic
MockProvider.
Channels
The structured channel (function-calling over the fact store), the narrative channel (hybrid retrieve → listwise rerank → cited synthesis), and the composite path that runs both and merges.
Extraction
Documents → a frozen StyledGrid IR. Style- and color-aware xlsx/pptx/pdf extractors, per-page PDF routing, a versioned color-semantics registry, and dual-channel cross-checking into a review queue.