Skip to content

Commit 86f045b

Browse files
authored
Merge pull request #23 from artiz/claude/fleischwolf-rag-system-3knaso
feat(rag): add fleischwolf-rag — pluggable RAG subsystem
2 parents cf66785 + d3435b9 commit 86f045b

51 files changed

Lines changed: 6746 additions & 1 deletion

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.env.example

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# fleischwolf-rag configuration. Copy to `.env` and adjust.
2+
# Every key has a built-in default (shown here), so an empty .env still runs
3+
# with the offline-friendly stack (SQLite + Ollama).
4+
5+
# --- Database (vector store) ---
6+
# sqlite | postgres | memory
7+
RAG_DB_BACKEND=sqlite
8+
# sqlite://<path> or postgres://user:pass@host:5432/dbname
9+
RAG_DATABASE_URL=sqlite://data/rag.db
10+
11+
# --- Embeddings ---
12+
# ollama | gemini | onnx | hash
13+
RAG_EMBED_PROVIDER=ollama
14+
RAG_EMBED_MODEL=bge-m3
15+
RAG_EMBED_DIM=1024
16+
# word | token
17+
RAG_CHUNK_UNIT=word
18+
19+
# Ollama (default provider)
20+
OLLAMA_BASE_URL=http://localhost:11434
21+
22+
# Gemini (RAG_EMBED_PROVIDER=gemini)
23+
GEMINI_API_KEY=
24+
RAG_GEMINI_MODEL=gemini-embedding-001
25+
26+
# Local ONNX (RAG_EMBED_PROVIDER=onnx; build with --features onnx-embed)
27+
RAG_EMBED_ONNX_PATH=models/embed/bge-m3.onnx
28+
RAG_EMBED_TOKENIZER=models/embed/tokenizer.json
29+
30+
# --- LLM (OpenRouter) — for Multi-Query, HyDE, and answer synthesis ---
31+
# OpenRouter keys start with "sk-or-". Any OpenAI-compatible endpoint works:
32+
# with a native DeepSeek key (sk-...) use
33+
# OPENROUTER_BASE_URL=https://api.deepseek.com and RAG_LLM_MODEL=deepseek-chat
34+
OPENROUTER_API_KEY=
35+
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1
36+
RAG_LLM_MODEL=deepseek/deepseek-chat
37+
38+
# --- Chunking ---
39+
RAG_CHUNK_SIZE=300
40+
RAG_CHUNK_OVERLAP=0.05
41+
42+
# --- Retrieval ---
43+
# vector | bm25 | hybrid | multi-query | hyde
44+
RAG_RETRIEVAL_MODE=hybrid
45+
RAG_TOP_K=5
46+
RAG_RRF_K=60
47+
RAG_MULTIQUERY_N=4
48+
49+
# --- Document source ---
50+
# folder | ftp | sftp (ftp/sftp require --features remote-sources)
51+
RAG_SOURCE=folder
52+
RAG_SOURCE_PATH=./rag/data
53+
# For ftp/sftp:
54+
RAG_SOURCE_URL=
55+
RAG_SOURCE_USER=
56+
RAG_SOURCE_PASSWORD=
57+
58+
# folder to store ingested documents in MD (for debugging / re-ingestion)
59+
RAG_DOCUMENTS_OUTPUT=./data/
60+
61+
# --- Message queue (async ingestion) ---
62+
# memory | rabbitmq | redis (rabbitmq/redis require the matching feature)
63+
RAG_QUEUE=memory
64+
RABBITMQ_URL=
65+
REDIS_URL=
66+
67+
# --- REST API (fleischwolf-rag serve) ---
68+
RAG_HTTP_ADDR=127.0.0.1:3222
69+
# Comma-separated list of accepted API keys (X-Api-Key / Authorization: Bearer).
70+
# The server refuses to start with an empty list. CHANGE THESE.
71+
RAG_API_KEYS=dev-key-change-me

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Cargo.lock
44
# Secrets — never commit (holds CARGO_REGISTRY_TOKEN)
55
.env
66
.env.*
7+
# …but the documented template for fleischwolf-rag config is committed.
8+
!.env.example
9+
10+
# Local RAG vector store (default RAG_DATABASE_URL=sqlite://data/rag.db)
11+
/data/
712

813
# Local editable docling installs used by scripts/compare.sh & performance.sh
914
# .venv-compare is the lightweight (no-torch) declarative-format env;
@@ -23,3 +28,4 @@ __pycache__/
2328
# Local docling ground-truth env & scratch dir for this migration work
2429
/.venv-gt
2530
/scratch-gt
31+

Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
# fleischwolf-pdf — the PDF/image ML pipeline (pdfium text + ONNX layout/OCR)
77
# fleischwolf-asr — the audio/ASR pipeline (symphonia decode + ONNX Whisper)
88
# fleischwolf-cli — the `fleischwolf` command-line binary
9+
# fleischwolf-rag — pluggable RAG (chunk/embed/store/search) over the converter
910
# See MIGRATION.md for the Python → Rust mapping and what is / isn't ported.
1011

1112
[workspace]
@@ -18,6 +19,9 @@ members = [
1819
"crates/fleischwolf-pdf",
1920
# Node.js / Bun N-API bindings (published to npm, not crates.io).
2021
"crates/fleischwolf-node",
22+
# RAG subsystem: chunking + embeddings + vector store + semantic search.
23+
# A self-contained async (tokio) island; does not touch the sync crates above.
24+
"crates/fleischwolf-rag",
2125
]
2226

2327
[workspace.package]

README.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ byte-for-byte against live docling, the ML pipeline against a deterministic
5555
snapshot baseline. See [`COMPARING.md`](./COMPARING.md) and
5656
`scripts/conformance.sh`.
5757

58+
## RAG subsystem
59+
60+
[`crates/fleischwolf-rag`](./crates/fleischwolf-rag) builds a pluggable
61+
Retrieval-Augmented-Generation layer on top of the converter: it turns documents
62+
into Markdown, chunks them (configurable size / overlap), embeds the chunks, and
63+
stores them in a vector database for semantic search. Every external dependency is
64+
a swappable trait — embedders (**Ollama**/Gemini/local-ONNX), vector stores
65+
(**SQLite+sqlite-vec**/PostgreSQL+pgvector), LLM (**OpenRouter**, DeepSeek-V3 by default),
66+
document sources (**folder**/FTP/SFTP), and message queues
67+
(**in-process**/RabbitMQ/Redis). It ships Hybrid, Multi-Query fusion and HyDE
68+
retrieval plus an evaluation harness to compare configurations and an
69+
API-key-protected REST API (`fleischwolf-rag serve`) for document info and
70+
search. Configure it via [`.env`](./.env.example); see the
71+
[crate README](./crates/fleischwolf-rag/README.md) for a quickstart on any
72+
documents folder.
73+
5874
## The API
5975

6076
```rust

crates/fleischwolf-node/examples/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,6 @@
1111
"pdf": "node pdf-pipeline.mjs"
1212
},
1313
"dependencies": {
14-
"fleischwolf": "^0.6.1"
14+
"fleischwolf": "^0.9.0"
1515
}
1616
}

crates/fleischwolf-rag/Cargo.toml

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
[package]
2+
name = "fleischwolf-rag"
3+
description = "Pluggable RAG subsystem for Fleischwolf: chunking, embeddings, vector store, and semantic search."
4+
version.workspace = true
5+
edition.workspace = true
6+
license.workspace = true
7+
repository.workspace = true
8+
homepage.workspace = true
9+
readme = "README.md"
10+
keywords = ["rag", "embeddings", "semantic-search", "vector", "docling"]
11+
categories = ["text-processing", "database"]
12+
# MSRV is raised over the workspace default by tokio/sqlx/reqwest; kept in step
13+
# with fleischwolf-pdf (ort) which already needs a recent toolchain.
14+
rust-version = "1.82"
15+
16+
[[bin]]
17+
name = "fleischwolf-rag"
18+
path = "src/bin/fleischwolf-rag.rs"
19+
20+
[features]
21+
# The default build is fully offline-testable: bundled SQLite + HTTP embedders.
22+
default = ["sqlite", "ollama"]
23+
sqlite = ["sqlx/sqlite", "dep:sqlite-vec", "dep:libsqlite3-sys"]
24+
postgres = ["sqlx/postgres"]
25+
# HTTP-only backends ride on reqwest (no native deps) and always compile; the
26+
# feature flags below just gate the small amount of provider-specific code.
27+
ollama = []
28+
gemini = []
29+
openrouter = []
30+
# Heavy / native-dependency backends. Real code, but not exercised in offline CI.
31+
onnx-embed = ["dep:ort", "dep:tokenizers"]
32+
remote-sources = ["dep:suppaftp", "dep:ssh2"]
33+
rabbitmq = ["dep:lapin", "dep:futures-lite"]
34+
redis = ["dep:redis", "dep:futures-lite"]
35+
36+
[dependencies]
37+
# `>=` requirements on purpose while this crate rides an open PR: release.sh
38+
# bumps every sibling's exact pin on master with each release, which would
39+
# break this branch's merge-ref build. Post-merge, release.sh rewrites these
40+
# to exact pins automatically (its sed matches any quoted version here).
41+
fleischwolf = { path = "../fleischwolf", version = ">=0.12" }
42+
# Direct dep only for pdfium_backend::page_count (PDF page metrics); already in
43+
# the graph via fleischwolf, so this adds no compile cost.
44+
fleischwolf-pdf = { path = "../fleischwolf-pdf", version = ">=0.12" }
45+
# Counting slides/sheets in OOXML containers for page metrics (names only).
46+
zip = { version = "2.2", default-features = false, features = ["deflate"] }
47+
48+
tokio = { version = "1", features = ["rt-multi-thread", "macros", "sync", "fs", "time", "signal"] }
49+
axum = "0.8"
50+
async-trait = "0.1"
51+
thiserror = "2"
52+
serde = { version = "1", features = ["derive"] }
53+
serde_json = "1"
54+
dotenvy = "0.15"
55+
clap = { version = "4", features = ["derive", "env"] }
56+
# rustls-tls so the crypto/TLS stack matches what the workspace already builds via
57+
# `ort` (tls-rustls) and sqlx below — no OpenSSL system dependency.
58+
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] }
59+
sqlx = { version = "0.8", default-features = false, features = ["runtime-tokio", "tls-rustls"] }
60+
# sqlite-vec: the `vec0` ANN virtual table, statically compiled into the bundled
61+
# SQLite. libsqlite3-sys must match sqlx's (cargo enforces one copy via `links`);
62+
# we need it directly for sqlite3_auto_extension to register the extension.
63+
sqlite-vec = { version = "0.1.6", optional = true }
64+
libsqlite3-sys = { version = "0.30", optional = true }
65+
pulldown-cmark = { version = "0.13", default-features = false }
66+
uuid = { version = "1", features = ["v4"] }
67+
sha2 = "0.10"
68+
tracing = "0.1"
69+
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
70+
71+
# --- feature-gated / optional backends ---
72+
ort = { version = "2.0.0-rc.12", default-features = false, features = ["std", "download-binaries", "tls-rustls"], optional = true }
73+
tokenizers = { version = "0.20", default-features = false, features = ["onig"], optional = true }
74+
suppaftp = { version = "6", optional = true }
75+
ssh2 = { version = "0.9", optional = true }
76+
lapin = { version = "2", optional = true }
77+
futures-lite = { version = "2", optional = true }
78+
redis = { version = "0.27", default-features = false, features = ["tokio-comp"], optional = true }
79+
80+
[dev-dependencies]
81+
tokio = { version = "1", features = ["rt-multi-thread", "macros", "test-util"] }

0 commit comments

Comments
 (0)