Multiservice document intelligence prototype for OCR, LayoutLMv3 layout parsing, vector retrieval, LLM orchestration, monitoring, and reproducible benchmarking/evaluation.
The project is best read as a technical case study: it combines service code, offline benchmarks, and a local public-corpus SEC evaluation. It is not presented as a production deployment, a legal/financial correctness system, or a customer-data benchmark.
- Async document ingestion with Redis queues, MinIO object storage, PostgreSQL metadata, and worker status updates.
- OCR processing for PDFs/images through EasyOCR.
- Layout-aware document processing with LayoutLMv3 token classification code.
- Tenant-scoped vector indexing and retrieval through Pinecone.
- Hybrid retrieval reranking that combines vector candidate scores with BM25 lexical scores, plus opt-in SEC metadata-aware reranking for the public SEC section benchmark.
- Labeled synthetic retrieval benchmark comparing vector-only, BM25-only, and hybrid reranking strategies with Recall@k, MRR, and nDCG.
- Real-service document RAG evaluation harness for curated local PDF corpora, with manifest validation, ingestion runs, Pinecone retrieval evaluation, optional answer proxy evaluation, and JSON/Markdown reports.
- Public corpus acquisition tooling for CUAD legal contracts and SEC EDGAR financial filings, generating manifest-compatible corpora without committing raw files.
- Public-safe synthetic PDF corpus generator for smoke-testing PDF ingestion workflow readiness.
- Preflight and report-promotion tooling for safer local document RAG evaluation runs.
- LLM prompt selection for legal contracts and financial reports.
- Cost-aware LLM routing between Gemini and Mistral using typed complexity scoring.
- LLM fallback, response caching, citation extraction, confidence scoring, and token/cost accounting.
- Reliability mechanisms including rate limiting, semaphores, retries, circuit breaker state, context sanitization, and task fail paths.
- Prometheus/Grafana monitoring configuration and service-level Prometheus metrics.
- Reproducible mock LLM routing benchmark with fixed workload, baselines, JSON/Markdown evidence, and tests.
The platform is split into ingestion, OCR/layout workers, embedding generation, inference API, LLM orchestration, and monitoring services. Docker Compose wires these services to Redis, PostgreSQL, MinIO, Pinecone, MLflow, Prometheus, Grafana, and a local Mistral-compatible inference server.
flowchart LR
Upload["PDF/image upload"] --> Ingest["ingestion API"]
Ingest --> Store["MinIO + PostgreSQL + Redis"]
Store --> OCR["OCR worker"]
OCR --> Layout["LayoutLMv3 parser"]
Layout --> Embed["embedding worker"]
Embed --> Pinecone["Pinecone vectors"]
Query["User query"] --> Inference["inference API"]
Inference --> Pinecone
Pinecone --> Rerank["BM25 / SEC-aware rerank"]
Rerank --> Orchestrator["LLM orchestrator"]
Orchestrator --> Answer["answer + citations + metrics"]
See:
docs/architecture.mddocs/case-study.mddocs/repository-guide.mddocs/llm-routing-benchmark.mdbenchmarks/corpora/README.mdbenchmarks/results/retrieval_benchmark_latest.md
- A document is uploaded to the ingestion service with a tenant ID and document type.
- The ingestion service validates the file, stores the raw object in MinIO, writes metadata/status records, and queues OCR work in Redis.
- OCR and layout workers process the document asynchronously and queue embedding work.
- The embedding worker chunks extracted text/layout output, embeds chunks, and writes tenant-scoped vectors to Pinecone.
- A query request goes through authentication, rate limits, query caching, vector retrieval, BM25 reranking, and context construction in the inference API.
- The inference API calls the LLM orchestrator, which selects a prompt/model path, calls the provider wrapper, extracts citation markers, estimates token/cost fields, and returns the response.
- Benchmarks and corpus harnesses exercise these pieces in mock/offline mode or against explicitly configured local services.
| Path | Purpose |
|---|---|
services/ingestion/ |
Upload API, document metadata, OCR worker, and queue integration. |
services/layout-parser/ |
LayoutLMv3-based layout parsing worker. |
services/embedding/ |
Text/layout chunking, sentence-transformer embeddings, and Pinecone writes. |
services/inference-api/ |
Query API, auth/rate limiting, vector retrieval, BM25/SEC reranking, context construction, LLM calls. |
services/llm-orchestrator/ |
Prompt selection, typed complexity scoring, model routing, provider wrappers, caching, citation extraction. |
benchmarks/ |
LLM routing benchmark, synthetic retrieval benchmark, public-corpus acquisition, SEC labeling/enrichment, document RAG harness, report promotion. |
benchmarks/corpora/ |
Corpus manifests, source registry, synthetic manifest, and selected sanitized SEC report summaries. Raw local corpora stay ignored. |
tests/ |
Unit and benchmark tests for routing, hybrid retrieval, corpus manifests, SEC labeling/enrichment, and report tooling. |
docs/ |
Architecture notes, case study, routing benchmark methodology, and repository guide. |
monitoring/ and services/monitoring/ |
Prometheus/Grafana configuration and monitoring service modules. |
infrastructure/ |
Kubernetes/Terraform scaffolding. This is not presented as production validation. |
Create and activate a Python environment, then install dependencies:
python -m venv .venv
.\.venv\Scripts\Activate.ps1
pip install -r requirements.txt -r requirements-dev.txtRun deterministic tests that do not require external API keys:
pytest tests\unit\test_llm_routing.py -q
pytest tests\benchmark\test_llm_routing_benchmark.py -q
pytest tests\benchmark\test_retrieval_benchmark.py -qRun the mock LLM routing benchmark:
python benchmarks\llm_routing_benchmark.py --output-dir benchmarks\results --run-id mock_latestRun the synthetic offline retrieval benchmark:
python benchmarks\retrieval_benchmark.py --output-dir benchmarks\results --run-id latestReview the checked-in benchmark evidence:
benchmarks/results/llm_routing_benchmark_mock_latest.jsonbenchmarks/results/llm_routing_benchmark_mock_latest.mdbenchmarks/results/retrieval_benchmark_latest.jsonbenchmarks/results/retrieval_benchmark_latest.md
For the full Docker Compose stack:
- PostgreSQL for document metadata and MLflow backend storage.
- Redis for queues, cache, rate-limit windows, and circuit breaker state.
- MinIO for object storage and local MLflow artifacts.
- Pinecone for vector retrieval.
- MLflow for training/benchmark tracking hooks.
- Prometheus and Grafana for metrics and dashboards.
- Mistral-compatible local inference endpoint through vLLM.
- Gemini API key for Gemini provider calls.
The focused tests, mock LLM benchmark, and synthetic retrieval benchmark listed above do not start these services and do not require real provider credentials.
Common variables:
POSTGRES_DB
POSTGRES_USER
POSTGRES_PASSWORD
POSTGRES_URL
REDIS_URL
MINIO_ENDPOINT
MINIO_ROOT_USER
MINIO_ROOT_PASSWORD
MINIO_ACCESS_KEY
MINIO_SECRET_KEY
PINECONE_API_KEY
PINECONE_ENVIRONMENT
PINECONE_INDEX
GEMINI_API_KEY
GEMINI_MODEL_NAME
MISTRAL_API_URL
LLM_ORCHESTRATOR_URL
INGESTION_SERVICE_URL
DOCUMENT_RAG_EVAL_TENANT_ID
DOCUMENT_RAG_EVAL_API_KEY
DOCUMENT_RAG_EVAL_BEARER_TOKEN
DOCUMENT_RAG_EVAL_EMBEDDING_MODEL
PINECONE_NAMESPACE
SEC_USER_AGENT
MLFLOW_TRACKING_URI
PROMETHEUS_URL
GRAFANA_ADMIN_PASSWORD
JWT_SECRET_KEY
API_KEY_PEPPER
Use .env.example for local defaults. Do not commit .env files or real secrets.
Focused routing tests:
pytest tests\unit\test_llm_routing.py -qBenchmark runner tests:
pytest tests\benchmark\test_llm_routing_benchmark.py -q
pytest tests\benchmark\test_retrieval_benchmark.py -q
pytest tests\benchmark\test_public_corpus_workflow.py -qFull test suite:
pytestMake targets are also available:
make test
make test-llm-routing
make test-hybrid-retrieval
make test-benchmarkSome integration tests may require external services or heavier ML dependencies. Prefer focused tests when validating LLM routing changes.
Mock LLM routing benchmark:
python benchmarks\llm_routing_benchmark.py --output-dir benchmarks\results --run-id mock_latestSynthetic offline retrieval benchmark:
python benchmarks\retrieval_benchmark.py --output-dir benchmarks\results --run-id latestCurated PDF document RAG harness:
python benchmarks\e2e_document_rag_eval.py validate-only --manifest benchmarks\corpora\example_manifest.json --skip-file-checkMake target:
make benchmark-llm-routingThe LLM benchmark compares:
always_expensive: Gemini for every query.always_cheap: Mistral for every query.heuristic: repository router logic.
The retrieval benchmark compares:
vector_only: deterministic semantic proxy ranking.bm25_only: BM25 reranking over the same simulated vector candidate pool.hybrid_70_30,hybrid_50_50, andhybrid_30_70: score-weight ablations.
Benchmarks write JSON and Markdown reports. CSV is optional for the retrieval benchmark and ignored by .gitignore.
Raw PDFs should be placed under the ignored local directory:
benchmarks\corpora\local_pdfs\
Use benchmarks\corpora\example_manifest.json as the manifest template. The manifest is committed, but private PDFs and local generated reports are ignored by default.
Validate a corpus manifest and local PDF references:
python benchmarks\e2e_document_rag_eval.py validate-only --manifest benchmarks\corpora\my_manifest.jsonRun ingestion against a local ingestion service:
python benchmarks\e2e_document_rag_eval.py ingest --manifest benchmarks\corpora\my_manifest.json --tenant-id tenant_eval_local --ingestion-url http://localhost:8001 --poll-status --run-id local_ingestRun Pinecone-backed retrieval evaluation after ingestion:
python benchmarks\e2e_document_rag_eval.py retrieve --manifest benchmarks\corpora\my_manifest.json --tenant-id tenant_eval_local --pinecone-index doc-intelligence --ingestion-run benchmarks\corpora\results\document_rag_eval_ingest_local_ingest.json --run-id local_retrieveRun optional answer proxy evaluation against the query service:
python benchmarks\e2e_document_rag_eval.py answer --manifest benchmarks\corpora\my_manifest.json --tenant-id tenant_eval_local --query-api-url http://localhost:8000 --ingestion-run benchmarks\corpora\results\document_rag_eval_ingest_local_ingest.json --run-id local_answerThese commands produce local JSON/Markdown reports under benchmarks\corpora\results\. The reports are ignored by default unless a specific, safe report is intentionally selected for review.
Generate a public-safe synthetic PDF smoke corpus:
python benchmarks\generate_synthetic_pdf_corpus.py --output-pdf-dir benchmarks\corpora\local_pdfs\synthetic_smoke --manifest-out benchmarks\corpora\synthetic_smoke_manifest.json --overwrite --seed 7 --num-docs 6Prepare a CUAD manifest from local CUAD-style metadata without downloading PDFs:
python benchmarks\acquire_public_corpus.py cuad --metadata-json benchmarks\corpora\local_pdfs\cuad_metadata.json --output-pdf-dir benchmarks\corpora\local_pdfs\cuad --manifest-out benchmarks\corpora\cuad_manifest.generated.json --sample-size 10Prepare a SEC EDGAR manifest from local filing metadata without network access:
python benchmarks\acquire_public_corpus.py sec-edgar --filings-json benchmarks\corpora\local_pdfs\sec_filings.json --output-file-dir benchmarks\corpora\local_pdfs\sec_edgar --manifest-out benchmarks\corpora\sec_edgar_manifest.generated.json --sample-size 6Fetch SEC filing metadata only when SEC_USER_AGENT is set to a real contact string:
$env:SEC_USER_AGENT="Your Name your.email@example.com"
python benchmarks\acquire_public_corpus.py sec-edgar --fetch-metadata --ticker AAPL --form-type 10-K --sample-size 1 --manifest-out benchmarks\corpora\sec_edgar_manifest.generated.jsonRun preflight before service calls:
python benchmarks\e2e_document_rag_eval.py preflight --preflight-target retrieve --manifest benchmarks\corpora\synthetic_smoke_manifest.json --pdf-root benchmarks\corpora\local_pdfsPromote a local report into a sanitized public summary:
python benchmarks\promote_document_rag_report.py benchmarks\corpora\results\document_rag_eval_retrieve_local.json --output-md benchmarks\corpora\results\sanitized_document_rag_summary.mdMake targets mirror these commands: corpus-generate-synthetic, corpus-acquire-cuad, corpus-acquire-sec, corpus-preflight, corpus-validate, corpus-ingest, corpus-retrieve, corpus-answer, and corpus-promote-report.
LLM routing reports include:
- command used
- timestamp
- git commit
- environment summary
- query categories
- selected model per query
- estimated input/output tokens
- estimated cost
- estimated latency p50/p95/p99
- cache hit rate
- fallback count
- quality proxy fields
- limitations
Retrieval reports include:
- command used
- timestamp
- git commit
- environment summary
- dataset paths and query categories
- number of chunks and queries
- selected strategy per query
- Recall@1, Recall@3, Recall@5, MRR, and nDCG@5
- category-level metrics
- top-5 misses and candidate-pool misses
- limitations
Document RAG corpus reports include:
- command used
- timestamp and git commit
- environment summary
- manifest path and PDF root
- corpus document/query counts
- services used, without secrets
- ingestion success/failure counts when ingestion is run
- Recall@1, Recall@3, Recall@5, MRR, and nDCG@5 when retrieval labels are available
- optional answer proxy metrics, including non-empty answer rate, citation presence, and expected-hint overlap
- per-query rows, misses, limitations, and unsupported claims
The current checked-in LLM routing benchmark is mock/synthetic. The current checked-in retrieval benchmark is synthetic/offline and uses simulated vector scores. These reports support reproducibility of the methods, not real production performance.
The curated PDF document RAG harness now includes checked-in sanitized local real-service SEC EDGAR retrieval reports. They are public-corpus, Pinecone-backed, and section-level, but remain environment-specific evidence and must not be described as production retrieval quality.
Current retrieval benchmark evidence from benchmarks/results/retrieval_benchmark_latest.json:
| Strategy | Recall@1 | Recall@3 | Recall@5 | MRR | nDCG@5 |
|---|---|---|---|---|---|
| vector_only | 0.9000 | 1.0000 | 1.0000 | 0.9667 | 0.9754 |
| bm25_only | 0.8667 | 0.9667 | 1.0000 | 0.9222 | 0.9468 |
| hybrid_70_30 | 0.9667 | 1.0000 | 1.0000 | 1.0000 | 1.0000 |
| hybrid_50_50 | 0.9667 | 1.0000 | 1.0000 | 1.0000 | 1.0000 |
| hybrid_30_70 | 0.9667 | 1.0000 | 1.0000 | 1.0000 | 0.9946 |
Current public SEC section-level retrieval evidence from benchmarks/corpora/results/sanitized_sec_section_retrieval_v2_summary.md:
| Corpus | Queries | Label granularity | Namespace | Candidate pool | Candidate misses | Recall@1 | Recall@3 | Recall@5 | MRR | nDCG@5 |
|---|---|---|---|---|---|---|---|---|---|---|
| 8 public SEC 10-K filings | 29 | section | tenant_eval_sec_sections_v2 |
100 | 6 | 0.7586 | 0.7931 | 0.7931 | 0.7759 | 0.7804 |
Previous section baseline from benchmarks/corpora/results/sanitized_sec_section_retrieval_summary.md: candidate pool 25, 13 candidate misses, Recall@1 0.1034, Recall@3 0.2759, Recall@5 0.3448, MRR 0.1879, nDCG@5 0.2269.
Interpretation: the v2 run shows that section-aware indexed metadata and SEC-aware reranking materially improve this controlled local SEC section benchmark. It does not prove production retrieval quality, legal correctness, financial correctness, or chunk-level retrieval quality.
Current public SEC answer proxy evidence from benchmarks/corpora/results/sanitized_sec_section_answer_summary.md:
| Corpus | Queries | Retrieval setup | Model | Delay/retry handling | Failures | Non-empty answer rate | Required citation presence | Expected-hint overlap |
|---|---|---|---|---|---|---|---|---|
| 8 public SEC 10-K filings | 29 | tenant_eval_sec_sections_v2, SEC-aware rerank, candidate pool 100 |
Gemini | 15s source run plus failed-query retry at 30s delay, max 1 retry | 16 | 0.448276 | 0.413793 | 0.413793 |
Interpretation: this is a lightweight answer/citation proxy over the same local SEC retrieval setup. The combined v5 report preserves the original 29-query denominator, retries only the 18 failed v4 rows, recovers 2 additional answers, and still records 16 service/provider failures. It does not prove factual, legal, financial, or semantic answer correctness.
- The repository contains code for OCR-based document ingestion and LayoutLMv3 layout parsing.
- The repository contains vector retrieval over Pinecone-indexed document chunks plus BM25 reranking over retrieved candidates.
- The repository contains a labeled synthetic retrieval benchmark comparing vector-only, BM25-only, and hybrid reranking strategies.
- The repository contains a real-service evaluation harness for curated PDF corpora, supporting manifest validation, ingestion runs, Pinecone-backed retrieval evaluation, optional answer proxy evaluation, and report generation.
- The repository contains public corpus acquisition tooling for CUAD and SEC EDGAR that generates manifest-compatible corpora while keeping raw files ignored by default.
- The repository contains checked-in sanitized SEC EDGAR section-level retrieval reports from local Pinecone-backed runs, including a v2 SEC-aware reranking ablation with explicit limitations.
- The repository contains a checked-in sanitized SEC EDGAR answer proxy report over the v2 retrieval setup, reporting non-empty answer rate, citation presence, expected-hint overlap, failures, model counts, and limitations.
- The repository contains a public-safe synthetic PDF corpus generator for smoke-testing the PDF ingestion workflow.
- The repository contains preflight and report-sanitization tooling for safer local document RAG evaluation runs.
- The repository contains typed, tested LLM routing with cost-aware model selection.
- The repository contains deterministic tests for routing, prompts, fallback, caching, citations, confidence, cost estimation, and malformed provider responses.
- The repository contains a reproducible mock benchmark comparing LLM routing strategies.
- The repository contains monitoring hooks and Prometheus/Grafana configuration.
- No production usage is claimed.
- No real users are claimed.
- No uptime, QPS, or SLA is claimed.
- No real cost savings are claimed without real billing/provider evidence.
- No compliance readiness is claimed.
- No production security guarantee is claimed.
- No real LLM answer accuracy is claimed from the mock benchmark.
- No production retrieval quality or real Pinecone performance is claimed from the synthetic retrieval benchmark.
- No real PDF/Pinecone result beyond the checked-in sanitized SEC section-level local retrieval and answer proxy reports is claimed.
- No customer/private document evaluation is claimed.
- No CUAD evaluation result is claimed. No SEC answer correctness result is claimed beyond the checked-in lightweight answer proxy metrics.
- No legal or financial correctness is claimed from the synthetic retrieval benchmark, synthetic PDF smoke corpus, SEC section-level retrieval report, or SEC answer proxy report.
- No claim is made that BM25 is a separate first-stage index; current hybrid retrieval reranks vector candidates with BM25.
- No LayoutLMv3 production accuracy number is claimed.
- The LLM benchmark is mock/synthetic and uses estimated latency/cost.
- The synthetic retrieval benchmark is offline and uses simulated vector scores; the SEC report is a separate local Pinecone-backed run.
- The committed SEC retrieval reports are section-level only. The best v2 run still has 6 candidate-pool misses out of 29 queries and does not include chunk-level labels.
- The committed SEC answer proxy report is partial: 16 of 29 queries failed through service/provider failure categories after one failed-query retry pass, and the reported citation/hint metrics are not semantic correctness.
- Public acquisition tooling has not produced a committed CUAD evaluation result in this repository state.
- SEC filings are often HTML and may need local rendering/conversion before PDF ingestion.
- Hybrid retrieval is currently a reranking layer over vector candidates, not a separate first-stage BM25 index.
- Some Docker Compose images use
latest, which weakens environment reproducibility. - Full local stack execution requires external services and credentials.
- Real provider benchmark mode is not implemented yet.
These are not current claims; they are practical next steps.
- Add independently reviewed page/chunk labels for the SEC corpus instead of relying only on generated section labels.
- Test whether SEC-aware metadata/reranking generalizes beyond the current 8-filing sample.
- Reduce remaining candidate-pool misses with retrieval changes that are evaluated before/after.
- Add a real-provider benchmark mode with opt-in credentials, clear cost controls, and strict report labeling.
- Broaden CI coverage for corpus manifest validation, report promotion, and retrieval metric utilities.
- Pin remaining
latestcontainer images to improve environment reproducibility.
.github/workflows/test-and-benchmark-smoke.yml runs deterministic routing tests, hybrid retrieval unit tests, LLM benchmark tests, and the mock LLM benchmark without real API keys or external services. Retrieval benchmark tests can be run locally with pytest tests\benchmark\test_retrieval_benchmark.py -q.