Claude Claude Opus 4.8 commited on
Commit
4775f5f
Β·
unverified Β·
1 Parent(s): 52fe15e

Add LLM_PROVIDER preset (groq/gemini/openrouter) for the free LLM

Browse files

Lets 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

Files changed (3) hide show
  1. README.md +12 -10
  2. backend/.env.example +8 -10
  3. 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 `LLM_API_KEY` (defaults to
44
- [Groq](https://console.groq.com/keys), a free OpenAI-compatible API) and
45
- answers become grounded summaries written from the retrieved passages (the
46
- source passage is still shown). Point `LLM_BASE_URL`/`LLM_MODEL` at any
47
- OpenAI-compatible provider. Without a key it falls back to an extractive
48
- answer, so the app runs with no credentials and no 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,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**, add `LLM_API_KEY` (a free
138
- [Groq](https://console.groq.com/keys) key) to enable LLM-written answers
139
- (otherwise the API serves extractive answers). Add a **Volume** at `/data` +
140
- `DOCUASK_DB=/data/docuask.db` to persist telemetry across restarts.
 
 
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
- # Default provider is Groq (free, no credit card): get a key at
20
- # https://console.groq.com/keys
21
- # LLM_API_KEY=gsk_...
 
 
 
 
22
  #
23
- # Override to use a different free provider:
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
- LLM_BASE_URL – OpenAI-compatible base URL (default: Groq)
12
- LLM_MODEL – model id (default: a current Groq Llama model)
 
 
 
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
- # Defaults target Groq's free, OpenAI-compatible API. Override any of these to
26
- # use a different free provider (e.g. Google Gemini's OpenAI endpoint).
27
- _BASE_URL = os.getenv("LLM_BASE_URL", "https://api.groq.com/openai/v1")
28
- _MODEL = os.getenv("LLM_MODEL", "llama-3.3-70b-versatile")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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 "