RAG Document API

Upload documents, retrieve evidence, and answer questions with page-level sources.

Take-home application. Edit here. Run locally with Docker. FastAPI, Redis, and provider calls do not run on CodeBaobab.

The task

Build an API that accepts PDF documents and answers questions using relevant passages from those documents, with page-level source references.

What to build

  1. Document ingestion. Accept PDF uploads, return job IDs, and extract pages in a Redis Streams worker.
  2. Searchable content. Split pages into overlapping passages and index them with their document IDs and page numbers.
  3. Question answering. Accept a question and selected document IDs, retrieve relevant passages, and build the model’s evidence context.
  4. Source references. Return the retrieved passages and page numbers alongside any generated answer. Reject citations outside the supplied context.
  5. Missing evidence. Return no answer when no passages match, and let the model abstain when retrieved text does not support an answer.

What’s supplied

The repository includes a LangGraph query workflow, upload and status endpoints, a Redis worker, PDF extraction, a SQLite full-text index with BM25 ranking, model adapters, an optional React demo, and Docker Compose.

Your implementation

Start with build_context(passages, max_chars) in app/services/context.py. It selects whole passages that fit within the prompt budget and preserves the source references used by the answer API.

  • Keep retrieval order and avoid duplicate passage IDs.
  • Count passage headers and separators within the context budget.
  • If a passage is too large, skip it and try the next one rather than cutting off its text.
Context-builder contract

Return exactly context and sources. Each source contains id, document_id, and page for an included passage.

Format each section as [id] document=document_id page=page, a newline, then stripped passage text. Separate sections with two newlines.

The budget is an integer from 1 to 12,000, not a boolean. Passages require nonblank string IDs, a positive integer page, and string text. Reject invalid records, keep the first occurrence of each ID, skip blank text, and never mutate the input.

Read tests/unit/test_context.py for exact examples and validation checks.

How to check your work

  1. Run the context-builder tests, then start the full application locally.
  2. Upload a PDF, wait until indexing completes, and send a question to POST /api/query with that document’s ID.
  3. Start with evidence-only mode: it returns passages without calling a model. Configure a local model or your own provider account to test generated answers.
  4. Try an unrelated question, a document still being processed, and a query scoped to only one of two uploaded files.

Extensions

Evaluate dense or hybrid retrieval, add reranking, or implement per-user document access. These are extensions; the supplied baseline uses real lexical retrieval, not a simulated vector database.

Edit here and run the API, worker, Redis, index, and model locally after download. Provider keys stay on your computer. Uploads are limited to four files per batch, 2 MB per file, and 1–50 pages; encrypted PDFs are not supported.

Exercises

AI-assisted Coding6 exercises