File size: 8,676 Bytes
821ccae abf3c4b 821ccae 1eec5a6 821ccae 1874e21 821ccae 1eec5a6 821ccae 4775f5f 821ccae 1eec5a6 821ccae 1eec5a6 821ccae 1eec5a6 821ccae abf3c4b 821ccae abf3c4b 821ccae abf3c4b 821ccae abf3c4b 821ccae c9c222c abf3c4b 5ab35ee abf3c4b 5ab35ee abf3c4b 5ab35ee 4775f5f 5ab35ee abf3c4b 5ab35ee abf3c4b 1520970 24361e0 c9c222c 24361e0 1520970 abf3c4b 821ccae 1eec5a6 821ccae 1eec5a6 821ccae 1eec5a6 821ccae abf3c4b 1eec5a6 821ccae 1eec5a6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | ---
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
[](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 |
| :---: | :---: |
|  |  |
## 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)
|