Chat with your documents. Upload a PDF or TXT, ask questions in natural language, get grounded answers with source citations — live-streamed token by token.
- Upload PDF or TXT files via drag & drop
- Index — files are split into chunks, embedded with OpenAI, stored in a local Chroma vector DB
- Ask — questions go through semantic search → top-K chunks → LLM with grounding prompt
- Stream — the answer appears token-by-token via SSE; sources show which file and page each claim came from
- Inspect — click any source chip to open a side panel with the matched chunk plus its neighbors for context
| Layer | Tech |
|---|---|
| Backend | FastAPI · LangChain · Chroma · OpenAI (gpt-4o-mini + text-embedding-3-small) |
| Frontend | Vue 3 · Vite · SSE streaming |
| Storage | Chroma (local, persistent) · JSON metadata index |
| Tests | pytest (12 tests, no network) · Playwright (e2e config) |
cd backend
python -m venv .venv
# Windows
.venv\Scripts\activate
# macOS / Linux
source .venv/bin/activate
pip install -r requirements.txt
cp .env.example .env # then set OPENAI_API_KEY in .env
uvicorn main:app --reload --port 9000API: http://localhost:9000 · Swagger: http://localhost:9000/docs
cd frontend
npm install
npm run dev # http://localhost:9001cd backend
pytest # 12 unit tests, no API key needed| Method | Endpoint | Description |
|---|---|---|
POST |
/upload |
Upload PDF/TXT, index into Chroma |
POST |
/ask |
SSE streaming Q&A with sources |
GET |
/documents |
List indexed documents |
DELETE |
/documents/{id} |
Remove document + vectors |
GET |
/chunks/{id} |
Fetch chunk + neighbor context window |
GET |
/health |
Config + liveness check |
ChainMain/
├── backend/
│ ├── main.py # FastAPI app, CORS, router wiring
│ ├── config.py # Settings via pydantic-settings + .env
│ ├── routes/ # upload · ask · documents · chunks
│ ├── services/ # document_service · qa_service
│ ├── rag/ # loader → splitter → embeddings → vectorstore → retriever → pipeline
│ ├── models/schemas.py # Pydantic models
│ └── tests/ # 12 pytest unit tests
│
└── frontend/
└── src/
├── App.vue
├── components/ # Upload · Chat · Message · DocumentList · ChunkPreview
└── services/api.js # fetch + SSE consumer
All settings live in backend/.env (copy from .env.example):
OPENAI_API_KEY=sk-...
EMBEDDING_MODEL=text-embedding-3-small
LLM_MODEL=gpt-4o-mini
CHUNK_SIZE=1000
CHUNK_OVERLAP=200
TOP_K=4
MAX_UPLOAD_MB=20- RAG pipeline (upload → index → ask → stream)
- Per-document filter (search only selected files)
- Source chip → chunk context panel with neighbors
- Upload validation (size, extension, MIME)
- Server-side stream cancel on client disconnect
- Conversation memory (follow-up questions)
- Persistent chat history (SQLite)
- Reranking (cross-encoder)
- Local models — Ollama +
sentence-transformers - Auth + rate limiting
- Docker Compose one-command start