Docuask / README.md
Claude
Add LLM_PROVIDER preset (groq/gemini/openrouter) for the free LLM
4775f5f unverified
|
Raw
History Blame Contribute Delete
8.68 kB
---
title: DocuAsk
emoji: πŸ“„
colorFrom: green
colorTo: gray
sdk: static
app_build_command: cd frontend && npm ci && npm run build
app_file: frontend/dist/index.html
pinned: false
license: mit
---
# DocuAsk
[![CI](https://github.com/Sriyansh-28/docuask/actions/workflows/ci.yml/badge.svg)](https://github.com/Sriyansh-28/docuask/actions/workflows/ci.yml)
**DocuAsk** is a full-stack document Q&A web app. Upload a PDF (or paste text),
ask questions in a chat box, and get answers with the **source passage** shown
underneath β€” then rate each answer πŸ‘/πŸ‘Ž and watch the usage metrics update on a
live dashboard. It's a small, end-to-end product loop: a React front end, a
FastAPI back end, a lightweight retrieval layer, and a SQLite telemetry layer,
all runnable with one command and deployable as a single container.
πŸ”— **Live demo:** **[docuask-production-c732.up.railway.app](https://docuask-production-c732.up.railway.app)**
β€” frontend + API from a single Railway container; dashboard at
[`/#/dashboard`](https://docuask-production-c732.up.railway.app/#/dashboard).
## Screenshots
| Chat β€” answer with source passage | Dashboard β€” live metrics |
| :---: | :---: |
| ![Chat view](docs/screenshots/chat.png) | ![Dashboard view](docs/screenshots/dashboard.png) |
## Features
- **Upload & parse** β€” drag-and-drop a PDF or paste text; the backend extracts
(pypdf), chunks, and indexes it in memory. Encrypted / broken / non-PDF /
empty / oversize inputs all fail with a clear, friendly message.
- **Chat with sources** β€” every answer shows the passage it was drawn from, so
the retrieval is transparent.
- **Hybrid retrieval** β€” BM25 (`rank_bm25`) combined with a FAISS cosine search
over TF-IDF vectors; deliberately lightweight, no heavyweight model.
- **Optional LLM answers (free)** β€” set `LLM_PROVIDER` (`groq` | `gemini` |
`openrouter`) and `LLM_API_KEY`, and answers become grounded summaries written
from the retrieved passages (the source passage is still shown). All three are
free OpenAI-compatible providers with email/Google sign-in. Without a key it
falls back to an extractive answer, so the app runs with no credentials and no
cost.
- **Feedback & telemetry** β€” each question is logged to SQLite with its measured
latency; πŸ‘/πŸ‘Ž feedback and a `/dashboard` page show total questions, median
latency, and thumbs-up rate live from the DB.
## Run it (one command)
```bash
docker compose up --build
```
Then open **http://localhost:5173**. The frontend calls the API through nginx,
so there's nothing else to configure.
## Run without Docker (dev)
**Backend**
```bash
cd backend
pip install -r requirements.txt
uvicorn app.main:app --reload # http://localhost:8000
```
**Frontend**
```bash
cd frontend
npm install
npm run dev # http://localhost:5173
```
In dev the Vite server proxies `/api/*` to the backend, so no CORS setup is
needed.
## Tests
```bash
cd backend
pytest
```
The suite covers `/health`, upload success and every failure path, retrieval and
`/ask`, `/feedback`, `/stats`, and the combined deploy entrypoint. CI runs it on
every push.
## API
| Method | Path | Purpose |
| ------ | ------------------- | -------------------------------------------------- |
| GET | `/health` | Liveness probe. |
| POST | `/documents` | Ingest a PDF file or raw text β†’ `document_id`. |
| GET | `/documents/{id}` | Document metadata. |
| POST | `/ask` | `{document_id, question}` β†’ answer + source passage. |
| POST | `/feedback` | Attach πŸ‘/πŸ‘Ž (`up`/`down`) to an interaction. |
| GET | `/stats` | Totals, median latency, thumbs-up rate, over-time. |
## Deploy
The frontend deploys to a **Hugging Face Static Space** and the API to any host
that runs a Python web process. They're wired together with one Space variable β€”
no rebuild needed to change the API URL.
### 1. Frontend β†’ Hugging Face Static Space
The README front matter (`sdk: static`) tells HF to run the
`app_build_command` (`cd frontend && npm ci && npm run build`) and serve
`frontend/dist`.
1. Create a new **Space** β†’ **Static** SDK, owner `Sri-28`, name `Docuask`
(the workflow default; case-sensitive on HF).
2. Push this repo to the Space (the [`sync-to-hf`](./.github/workflows/sync-to-hf.yml)
workflow does this automatically β€” see below).
3. In the Space's **Settings β†’ Variables**, add `DOCUASK_API_URL` set to your
deployed API's URL. The frontend reads it at runtime via
`window.huggingface.variables`, so you can change it without rebuilding.
The Space serves at **https://sri-28-docuask.static.hf.space**.
### 2. Backend β†’ Railway
The API is a standard uvicorn app. On **Railway**:
1. **New Project β†’ Deploy from GitHub repo** β†’ select `docuask`.
2. Open the service β†’ **Settings β†’ Root Directory** = `backend`. This makes
[`backend/railway.json`](./backend/railway.json) build
[`backend/Dockerfile`](./backend/Dockerfile) (which installs faiss's
`libgomp1` and honors Railway's injected `$PORT`).
3. **Settings β†’ Networking β†’ Generate Domain** to get a public URL.
4. *(Optional)* In the service **Variables**, set `LLM_PROVIDER` (e.g. `gemini`)
and `LLM_API_KEY` (a free key β€” Gemini's is at
[aistudio.google.com/apikey](https://aistudio.google.com/apikey), Google
sign-in) to enable LLM-written answers; otherwise the API serves extractive
answers. Add a **Volume** at `/data` + `DOCUASK_DB=/data/docuask.db` to
persist telemetry across restarts.
5. Copy the public URL β€” you'll set it as `DOCUASK_API_URL` in the HF Space
(step 3 above).
The Static Space origin (`https://sri-28-docuask.static.hf.space`) is already in
the backend's default CORS allow-list; add more via the `CORS_ORIGINS` env var.
See [`backend/.env.example`](./backend/.env.example).
> Other hosts work too: any Docker host can build `backend/Dockerfile`, and
> native-Python hosts (Render, …) can use [`backend/Procfile`](./backend/Procfile).
### Keep the demo in sync automatically
The [`sync-to-hf`](./.github/workflows/sync-to-hf.yml) workflow mirrors `main` to
your Space on every push. Configure it once under **Settings β†’ Secrets and
variables β†’ Actions**:
- Secret `HF_TOKEN` β€” a Hugging Face token with write scope. **This is the only
required step** (username defaults to `Sri-28`, Space to `Docuask`).
- Optionally override the `HF_USERNAME` / `HF_SPACE` variables.
Until `HF_TOKEN` is set the workflow no-ops, so it never fails the branch.
> **One-origin alternative:** the root [`Dockerfile`](./Dockerfile) +
> [`app/server.py`](./backend/app/server.py) build a single image that serves the
> frontend and API together under one origin (API mounted at `/api`, no CORS).
> Use this on any Docker host if you'd rather run one service than two.
## Project structure
```
docuask/
backend/ # FastAPI app + tests
app/
main.py # API: /health, /documents, /ask, /feedback, /stats
parsing.py # PDF/text extraction + chunking
retrieval.py # BM25 + FAISS (TF-IDF) hybrid index
store.py # in-memory document store
db.py # SQLite interaction telemetry
server.py # combined static + API entrypoint (deploy)
tests/ # pytest suite
frontend/ # Vite + React + Tailwind
src/
App.jsx # layout + hash routing (Chat / Dashboard)
DocumentUploader.jsx, Chat.jsx, Dashboard.jsx
Dockerfile # single-image build for Hugging Face Spaces
docker-compose.yml # local: frontend + backend together
.github/workflows/ # CI: pytest + frontend build on every push
```
## Tech stack
- **Frontend:** React + Vite + Tailwind CSS
- **Backend:** FastAPI (Python)
- **Retrieval:** BM25 + FAISS over TF-IDF
- **Persistence:** SQLite (interaction telemetry)
- **Tests:** pytest
- **CI:** GitHub Actions (pytest + build on every push)
- **Deploy:** Hugging Face Static Space (frontend) + any Python host (API); Docker Compose locally
## Roadmap
See [`PROJECT_SPEC.md`](./PROJECT_SPEC.md) for the full plan:
1. βœ… Skeleton end to end (health check wired browser β†’ API)
2. βœ… Upload & parse flow (PDF/text β†’ chunked, indexed)
3. βœ… Retrieval + chat loop (BM25 + FAISS, source passages)
4. βœ… Data-collection & feedback layer (SQLite, πŸ‘/πŸ‘Ž, `/stats`, dashboard)
5. βœ… Polish, tests, deploy (single-image Docker Space)
## License
[MIT](./LICENSE)