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
metadata
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

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 β€” frontend + API from a single Railway container; dashboard at /#/dashboard.

Screenshots

Chat β€” answer with source passage Dashboard β€” live metrics
Chat view Dashboard view

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)

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

cd backend
pip install -r requirements.txt
uvicorn app.main:app --reload   # http://localhost:8000

Frontend

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

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 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 build 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, 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.

Other hosts work too: any Docker host can build backend/Dockerfile, and native-Python hosts (Render, …) can use backend/Procfile.

Keep the demo in sync automatically

The sync-to-hf 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 + 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 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