What it does
Full-stack RAG (Retrieval-Augmented Generation) application: upload documents, ask natural-language questions, get streamed answers grounded in the source text. Spring Boot 3.3 + Spring AI 1.0, OpenAI GPT-4o, PostgreSQL + pgvector for vector search, with a 3-strategy PDF extraction pipeline (PDFBox → Tesseract OCR → GPT-4o Vision) and SSE token streaming with source citations.
How it works
An upload returns 202 Accepted with a document ID straight away and the work continues in the background, so the frontend polls the document's status through PROCESSING, READY, and FAILED.
Extraction goes through a DocumentHandler per file type. For PDFs the handler runs a three-strategy waterfall per page: PDFBox digital text first, Tesseract OCR when that comes back too short, and GPT-4o Vision as the last resort, so no page is silently skipped.
The extracted text is chunked with JTokkit against OpenAI's CL100K_BASE tokenizer into 500-token chunks with 50 tokens of overlap, embedded with text-embedding-3-small, and stored in pgvector behind an IVFFlat index of 100 lists.
A question is embedded the same way and matched by cosine similarity in native SQL, top-k 5 by default. The retrieved chunks go into the prompt, and the answer comes back over Server-Sent Events on a Flux<ServerSentEvent> so each token is flushed past Tomcat's 8 KB buffer and reaches the browser immediately, followed by the source citations.
Design decisions
- 01Asynchronous ingestion: the upload returns 202 Accepted with a document ID at once and the frontend polls status (PROCESSING, READY, FAILED).
- 02Strategy pattern for extraction: one handler per file type behind a DocumentHandler interface; PDF, DOCX, PPTX, XLSX, text, and images.
- 03PDF three-strategy waterfall per page: PDFBox digital text, then Tesseract OCR, then GPT-4o Vision as the last resort, so no page is silently skipped.
- 04Token-aware chunking with JTokkit using OpenAI's CL100K_BASE tokenizer: 500-token chunks with 50-token overlap.
- 05Native SQL for cosine similarity search on pgvector with an IVFFlat index (100 lists); top-k 5 by default.
- 06Server-Sent Events over a Flux<ServerSentEvent> so each token is flushed past Tomcat's 8 KB buffer and reaches the browser immediately.
- 07Cross-document search implemented purely in SQL, with no schema change; confidence is the average cosine similarity of the retrieved chunks.
Stack
- Language
- Java 21
- Framework
- Spring Boot 3.3, Spring MVC, Spring AI 1.0
- Models
- OpenAI GPT-4o, text-embedding-3-small (1536 dimensions)
- Vector store
- PostgreSQL 16 with pgvector
- Persistence
- Spring Data JPA (Hibernate), Flyway
- Streaming
- Project Reactor, Spring WebFlux
- Extraction
- Apache PDFBox, Tesseract via Tess4J, GPT-4o Vision, Apache POI
- Tokenization
- JTokkit
- API and tooling
- SpringDoc OpenAPI, Lombok
- Frontend
- Vanilla HTML/CSS/JS
- Build and run
- Docker Compose