Claude Claude Opus 4.8 commited on
Add LLM_PROVIDER preset (groq/gemini/openrouter) for the free LLM
Browse filesLets the answer provider be chosen with one variable instead of typing the
base URL and model. Gemini and OpenRouter offer Google/email sign-in for
users who can't sign up via GitHub. LLM_BASE_URL / LLM_MODEL still override
the preset. Docs updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WN4QRr6dTE2W7hQ2SDnLmY
- README.md +12 -10
- backend/.env.example +8 -10
- backend/app/generate.py +23 -6
README.md
CHANGED
|
@@ -40,12 +40,12 @@ all runnable with one command and deployable as a single container.
|
|
| 40 |
the retrieval is transparent.
|
| 41 |
- **Hybrid retrieval** β BM25 (`rank_bm25`) combined with a FAISS cosine search
|
| 42 |
over TF-IDF vectors; deliberately lightweight, no heavyweight model.
|
| 43 |
-
- **Optional LLM answers (free)** β set `
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
- **Feedback & telemetry** β each question is logged to SQLite with its measured
|
| 50 |
latency; π/π feedback and a `/dashboard` page show total questions, median
|
| 51 |
latency, and thumbs-up rate live from the DB.
|
|
@@ -134,10 +134,12 @@ The API is a standard uvicorn app. On **Railway**:
|
|
| 134 |
[`backend/Dockerfile`](./backend/Dockerfile) (which installs faiss's
|
| 135 |
`libgomp1` and honors Railway's injected `$PORT`).
|
| 136 |
3. **Settings β Networking β Generate Domain** to get a public URL.
|
| 137 |
-
4. *(Optional)* In the service **Variables**,
|
| 138 |
-
|
| 139 |
-
(
|
| 140 |
-
|
|
|
|
|
|
|
| 141 |
5. Copy the public URL β you'll set it as `DOCUASK_API_URL` in the HF Space
|
| 142 |
(step 3 above).
|
| 143 |
|
|
|
|
| 40 |
the retrieval is transparent.
|
| 41 |
- **Hybrid retrieval** β BM25 (`rank_bm25`) combined with a FAISS cosine search
|
| 42 |
over TF-IDF vectors; deliberately lightweight, no heavyweight model.
|
| 43 |
+
- **Optional LLM answers (free)** β set `LLM_PROVIDER` (`groq` | `gemini` |
|
| 44 |
+
`openrouter`) and `LLM_API_KEY`, and answers become grounded summaries written
|
| 45 |
+
from the retrieved passages (the source passage is still shown). All three are
|
| 46 |
+
free OpenAI-compatible providers with email/Google sign-in. Without a key it
|
| 47 |
+
falls back to an extractive answer, so the app runs with no credentials and no
|
| 48 |
+
cost.
|
| 49 |
- **Feedback & telemetry** β each question is logged to SQLite with its measured
|
| 50 |
latency; π/π feedback and a `/dashboard` page show total questions, median
|
| 51 |
latency, and thumbs-up rate live from the DB.
|
|
|
|
| 134 |
[`backend/Dockerfile`](./backend/Dockerfile) (which installs faiss's
|
| 135 |
`libgomp1` and honors Railway's injected `$PORT`).
|
| 136 |
3. **Settings β Networking β Generate Domain** to get a public URL.
|
| 137 |
+
4. *(Optional)* In the service **Variables**, set `LLM_PROVIDER` (e.g. `gemini`)
|
| 138 |
+
and `LLM_API_KEY` (a free key β Gemini's is at
|
| 139 |
+
[aistudio.google.com/apikey](https://aistudio.google.com/apikey), Google
|
| 140 |
+
sign-in) to enable LLM-written answers; otherwise the API serves extractive
|
| 141 |
+
answers. Add a **Volume** at `/data` + `DOCUASK_DB=/data/docuask.db` to
|
| 142 |
+
persist telemetry across restarts.
|
| 143 |
5. Copy the public URL β you'll set it as `DOCUASK_API_URL` in the HF Space
|
| 144 |
(step 3 above).
|
| 145 |
|
backend/.env.example
CHANGED
|
@@ -16,16 +16,14 @@
|
|
| 16 |
# DocuAsk falls back to the extractive best-sentence answer (no key, no cost,
|
| 17 |
# still shows the source passage).
|
| 18 |
#
|
| 19 |
-
#
|
| 20 |
-
#
|
| 21 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
#
|
| 23 |
-
#
|
| 24 |
-
# Groq (default): LLM_BASE_URL=https://api.groq.com/openai/v1
|
| 25 |
-
# LLM_MODEL=llama-3.3-70b-versatile
|
| 26 |
-
# Google Gemini: LLM_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai/
|
| 27 |
-
# LLM_MODEL=gemini-2.0-flash
|
| 28 |
-
# OpenRouter: LLM_BASE_URL=https://openrouter.ai/api/v1
|
| 29 |
-
# LLM_MODEL=<a free model id>
|
| 30 |
# LLM_BASE_URL=https://api.groq.com/openai/v1
|
| 31 |
# LLM_MODEL=llama-3.3-70b-versatile
|
|
|
|
| 16 |
# DocuAsk falls back to the extractive best-sentence answer (no key, no cost,
|
| 17 |
# still shows the source passage).
|
| 18 |
#
|
| 19 |
+
# Pick a free provider with LLM_PROVIDER (groq | gemini | openrouter) and set
|
| 20 |
+
# LLM_API_KEY. All three offer email/Google sign-in β no GitHub needed.
|
| 21 |
+
# groq β https://console.groq.com/keys (default)
|
| 22 |
+
# gemini β https://aistudio.google.com/apikey (sign in with Google)
|
| 23 |
+
# openrouter β https://openrouter.ai/keys
|
| 24 |
+
# LLM_PROVIDER=gemini
|
| 25 |
+
# LLM_API_KEY=...
|
| 26 |
#
|
| 27 |
+
# Advanced: override the preset's endpoint/model directly.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
# LLM_BASE_URL=https://api.groq.com/openai/v1
|
| 29 |
# LLM_MODEL=llama-3.3-70b-versatile
|
backend/app/generate.py
CHANGED
|
@@ -8,8 +8,11 @@ the app runs with no network access and no credentials.
|
|
| 8 |
|
| 9 |
Configure via env vars:
|
| 10 |
LLM_API_KEY β your free API key (required to enable generation)
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
| 13 |
|
| 14 |
The OpenAI SDK is imported lazily so the package is never a hard import-time
|
| 15 |
dependency of the API.
|
|
@@ -22,10 +25,24 @@ import os
|
|
| 22 |
|
| 23 |
logger = logging.getLogger("docuask")
|
| 24 |
|
| 25 |
-
#
|
| 26 |
-
#
|
| 27 |
-
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
|
| 30 |
_SYSTEM = (
|
| 31 |
"You are DocuAsk, a document question-answering assistant. Answer the "
|
|
|
|
| 8 |
|
| 9 |
Configure via env vars:
|
| 10 |
LLM_API_KEY β your free API key (required to enable generation)
|
| 11 |
+
LLM_PROVIDER β preset base URL + model: "groq" (default), "gemini",
|
| 12 |
+
or "openrouter". Sign-in for each is by email/Google, no
|
| 13 |
+
GitHub required.
|
| 14 |
+
LLM_BASE_URL β override the preset's OpenAI-compatible base URL
|
| 15 |
+
LLM_MODEL β override the preset's model id
|
| 16 |
|
| 17 |
The OpenAI SDK is imported lazily so the package is never a hard import-time
|
| 18 |
dependency of the API.
|
|
|
|
| 25 |
|
| 26 |
logger = logging.getLogger("docuask")
|
| 27 |
|
| 28 |
+
# Provider presets: (OpenAI-compatible base URL, default model). Pick one with
|
| 29 |
+
# LLM_PROVIDER; override the pieces individually with LLM_BASE_URL / LLM_MODEL.
|
| 30 |
+
_PRESETS = {
|
| 31 |
+
"groq": ("https://api.groq.com/openai/v1", "llama-3.3-70b-versatile"),
|
| 32 |
+
"gemini": (
|
| 33 |
+
"https://generativelanguage.googleapis.com/v1beta/openai/",
|
| 34 |
+
"gemini-2.0-flash",
|
| 35 |
+
),
|
| 36 |
+
"openrouter": (
|
| 37 |
+
"https://openrouter.ai/api/v1",
|
| 38 |
+
"meta-llama/llama-3.3-70b-instruct:free",
|
| 39 |
+
),
|
| 40 |
+
}
|
| 41 |
+
|
| 42 |
+
_provider = os.getenv("LLM_PROVIDER", "groq").lower()
|
| 43 |
+
_preset_base, _preset_model = _PRESETS.get(_provider, _PRESETS["groq"])
|
| 44 |
+
_BASE_URL = os.getenv("LLM_BASE_URL", _preset_base)
|
| 45 |
+
_MODEL = os.getenv("LLM_MODEL", _preset_model)
|
| 46 |
|
| 47 |
_SYSTEM = (
|
| 48 |
"You are DocuAsk, a document question-answering assistant. Answer the "
|