Plan 008: Add .env.example and document which model providers actually work out of the box
Executor instructions: Follow this plan step by step. Run every
verification command and confirm the expected result before moving to the
next step. If anything in the "STOP conditions" section occurs, stop and
report — do not improvise. When done, update the status row for this plan
in plans/README.md.
Drift check (run first): git diff --stat 5b5c634..HEAD -- README.md src/constants.py src/utils/provider_routing.py
If provider_routing.py or constants.py changed since this plan was
written, re-derive the env-var list from the live code before writing docs.
Status
- Priority: P3
- Effort: S
- Risk: LOW
- Depends on: none (but if plans 001/004 have landed, include their new env vars — see Step 1)
- Category: docs
- Planned at: commit
5b5c634, 2026-06-12
Why this matters
The README's feature list advertises "Gemini, OpenAI, Anthropic, OpenRouter"
cloud models, but the setup instructions only ever mention GEMINI_API_KEY,
and Anthropic support isn't even installed by default (uv sync skips the
anthropic extra — a user who sets an anthropic/... model gets an
ImportError at their first LLM call). There is no .env.example, so the full
set of recognized environment variables is discoverable only by reading
src/constants.py and src/utils/provider_routing.py. Fifteen minutes of
documentation removes the worst onboarding traps.
Current state
README.md setup section (~lines 97–135): documents uv sync,
uv sync --extra local-embeddings, creating .env via echo commands
with GEMINI_API_KEY and OLLAMA_API_URL, and overriding
HINBOX_OLLAMA_MODEL / HINBOX_CLOUD_MODEL. No mention of OpenAI /
Anthropic / OpenRouter setup despite the feature list at line ~15.
- Environment variables actually read by the code (verified by grep at the
planning commit):
src/utils/provider_routing.py: GEMINI_API_KEY (required for
gemini/ models and gemini embeddings), OPENROUTER_API_KEY (for
openrouter/ models), JINA_API_KEY (for the default cloud embedding
model jina_ai/jina-embeddings-v3 — see src/constants.py:11).
OPENAI_API_KEY and ANTHROPIC_API_KEY are read implicitly by the
OpenAI/Anthropic SDKs (comments at provider_routing.py:111 and :119).
src/constants.py: HINBOX_CLOUD_MODEL (default
gemini/gemini-2.0-flash), HINBOX_OLLAMA_MODEL (default
ollama/qwen2.5:32b-instruct-q5_K_M), OLLAMA_API_URL (default
http://localhost:11434/v1), ENABLE_PROFILE_VERSIONING (default true).
src/utils/embeddings/manager.py: EMBEDDING_MODE (cloud | local | hybrid).
- The
anthropic extra: pyproject.toml [project.optional-dependencies]
has anthropic = ["anthropic>=0.40"]; src/utils/llm.py raises a helpful
ImportError if an anthropic/ model is used without it.
- A real
.env file exists in the working directory (gitignored). Never
open, read, or copy it. The example file is written from the code-derived
list above, with placeholder values only.
justfile has set dotenv-load, so .env is auto-loaded by all recipes —
worth stating in the docs.
Commands you will need
| Purpose |
Command |
Expected on success |
| Env-var census |
`grep -rhoE 'os.(getenv |
environ.get)("[A-Z_]+' src | grep -oE '[A-Z_]{3,}' | sort -u` |
| Lint/CI |
just ci |
exit 0 (docs don't affect it; run anyway) |
Scope
In scope:
.env.example (create, repo root)
README.md (setup section + a new "Supported model providers" subsection)
Out of scope (do NOT touch):
- Any
src/ code — documentation only. (The "warn early when an
anthropic/ model is configured but the extra is missing" idea is noted
in Maintenance notes as deferred.)
- The local
.env file — do not read it, do not modify it, do not quote it.
Git workflow
- Branch:
advisor/008-onboarding-docs
- Commit style: imperative subject, backticks around code identifiers.
- Do NOT push or open a PR unless the operator instructed it.
Steps
Step 1: Create .env.example
Repo root, placeholder values only. Before writing, check whether plans 001
and 004 have landed (grep -n "HINBOX_CHECKPOINT_INTERVAL\|HINBOX_FRONTEND_HOST" src/constants.py)
and include those vars only if present. Content shape:
# hinbox environment configuration — copy to .env (auto-loaded by `just`).
# Only GEMINI_API_KEY is required for the default cloud setup.
# --- Cloud LLM providers ---
GEMINI_API_KEY=your-gemini-api-key # required for default gemini/ models
# OPENAI_API_KEY=... # for openai/ models (read by OpenAI SDK)
# ANTHROPIC_API_KEY=... # for anthropic/ models (needs: uv sync --extra anthropic)
# OPENROUTER_API_KEY=... # for openrouter/ models
# --- Embeddings ---
# JINA_API_KEY=... # required for default cloud embeddings (jina_ai/jina-embeddings-v3)
# EMBEDDING_MODE=cloud # cloud | local | hybrid
# --- Local models (Ollama) ---
# OLLAMA_API_URL=http://localhost:11434/v1
# --- Model overrides ---
# HINBOX_CLOUD_MODEL=gemini/gemini-2.0-flash
# HINBOX_OLLAMA_MODEL=ollama/qwen2.5:32b-instruct-q5_K_M
# --- Behavior toggles ---
# ENABLE_PROFILE_VERSIONING=true
Verify: test -f .env.example && grep -c "=" .env.example → ≥10; and
grep -E "=(sk-|AIza|key-[A-Za-z0-9])" .env.example → no matches (no real-looking keys)
Step 2: README — point at the example file
In the "Set up environment variables" step (~line 107), replace the echo
instructions' lead-in with: copy .env.example to .env and fill in keys
(cp .env.example .env); keep one echo example for the minimal
Gemini-only path. Mention that just auto-loads .env (set dotenv-load).
Verify: grep -n ".env.example" README.md → ≥1 match
Step 3: README — "Supported model providers" subsection
Add a short subsection (near the model-override docs, ~line 125) with a
table:
| Provider |
Model string format |
Required setup |
| Gemini (default) |
gemini/gemini-2.0-flash |
GEMINI_API_KEY |
| OpenAI |
openai/<model> |
OPENAI_API_KEY |
| Anthropic |
anthropic/<model> |
uv sync --extra anthropic + ANTHROPIC_API_KEY |
| OpenRouter |
openrouter/<vendor>/<model> |
OPENROUTER_API_KEY |
| Ollama (local) |
ollama/<model> |
Ollama running at OLLAMA_API_URL |
Cross-check each row against src/utils/provider_routing.py:95-185 before
writing — the prefix-to-provider mapping there is the source of truth (e.g.
confirm the exact openrouter/ model-string shape it parses). Add one
sentence: cloud embeddings default to Jina (JINA_API_KEY); pass --local
or install --extra local-embeddings for fully local operation.
Verify: each table row's prefix appears in
grep -n "startswith\|prefix" src/utils/provider_routing.py output or the
routing match statements — confirm manually, then just ci → exit 0
Test plan
No code changes; no new tests. Verification = the grep gates above plus a
human read-through of the rendered README section.
Done criteria
STOP conditions
Stop and report back (do not improvise) if:
- The env-var census returns names not covered in "Current state" — list
them and extend the example only after confirming what they do from code.
provider_routing.py has gained/lost providers since planning (drift) —
re-derive the table, and flag the change.
- You find yourself about to open the local
.env for "reference" — don't;
that file may contain real credentials.
Maintenance notes
- When a new env var is added to
constants.py or provider_routing.py,
.env.example must gain a line in the same PR — reviewers should check.
- Deferred (code change, separate decision): an early startup warning when
the configured cloud model needs a missing optional extra or env var, so
failures happen at launch rather than at the first LLM call.
Plan 008: Add
.env.exampleand document which model providers actually work out of the boxStatus
5b5c634, 2026-06-12Why this matters
The README's feature list advertises "Gemini, OpenAI, Anthropic, OpenRouter"
cloud models, but the setup instructions only ever mention
GEMINI_API_KEY,and Anthropic support isn't even installed by default (
uv syncskips theanthropicextra — a user who sets ananthropic/...model gets anImportError at their first LLM call). There is no
.env.example, so the fullset of recognized environment variables is discoverable only by reading
src/constants.pyandsrc/utils/provider_routing.py. Fifteen minutes ofdocumentation removes the worst onboarding traps.
Current state
README.mdsetup section (~lines 97–135): documentsuv sync,uv sync --extra local-embeddings, creating.envviaechocommandswith
GEMINI_API_KEYandOLLAMA_API_URL, and overridingHINBOX_OLLAMA_MODEL/HINBOX_CLOUD_MODEL. No mention of OpenAI /Anthropic / OpenRouter setup despite the feature list at line ~15.
planning commit):
src/utils/provider_routing.py:GEMINI_API_KEY(required forgemini/models and gemini embeddings),OPENROUTER_API_KEY(foropenrouter/models),JINA_API_KEY(for the default cloud embeddingmodel
jina_ai/jina-embeddings-v3— seesrc/constants.py:11).OPENAI_API_KEYandANTHROPIC_API_KEYare read implicitly by theOpenAI/Anthropic SDKs (comments at
provider_routing.py:111and:119).src/constants.py:HINBOX_CLOUD_MODEL(defaultgemini/gemini-2.0-flash),HINBOX_OLLAMA_MODEL(defaultollama/qwen2.5:32b-instruct-q5_K_M),OLLAMA_API_URL(defaulthttp://localhost:11434/v1),ENABLE_PROFILE_VERSIONING(default true).src/utils/embeddings/manager.py:EMBEDDING_MODE(cloud | local | hybrid).anthropicextra:pyproject.toml[project.optional-dependencies]has
anthropic = ["anthropic>=0.40"];src/utils/llm.pyraises a helpfulImportError if an
anthropic/model is used without it..envfile exists in the working directory (gitignored). Neveropen, read, or copy it. The example file is written from the code-derived
list above, with placeholder values only.
justfilehasset dotenv-load, so.envis auto-loaded by all recipes —worth stating in the docs.
Commands you will need
just ciScope
In scope:
.env.example(create, repo root)README.md(setup section + a new "Supported model providers" subsection)Out of scope (do NOT touch):
src/code — documentation only. (The "warn early when ananthropic/model is configured but the extra is missing" idea is notedin Maintenance notes as deferred.)
.envfile — do not read it, do not modify it, do not quote it.Git workflow
advisor/008-onboarding-docsSteps
Step 1: Create
.env.exampleRepo root, placeholder values only. Before writing, check whether plans 001
and 004 have landed (
grep -n "HINBOX_CHECKPOINT_INTERVAL\|HINBOX_FRONTEND_HOST" src/constants.py)and include those vars only if present. Content shape:
Verify:
test -f .env.example && grep -c "=" .env.example→ ≥10; andgrep -E "=(sk-|AIza|key-[A-Za-z0-9])" .env.example→ no matches (no real-looking keys)Step 2: README — point at the example file
In the "Set up environment variables" step (~line 107), replace the
echoinstructions' lead-in with: copy
.env.exampleto.envand fill in keys(
cp .env.example .env); keep oneechoexample for the minimalGemini-only path. Mention that
justauto-loads.env(set dotenv-load).Verify:
grep -n ".env.example" README.md→ ≥1 matchStep 3: README — "Supported model providers" subsection
Add a short subsection (near the model-override docs, ~line 125) with a
table:
gemini/gemini-2.0-flashGEMINI_API_KEYopenai/<model>OPENAI_API_KEYanthropic/<model>uv sync --extra anthropic+ANTHROPIC_API_KEYopenrouter/<vendor>/<model>OPENROUTER_API_KEYollama/<model>OLLAMA_API_URLCross-check each row against
src/utils/provider_routing.py:95-185beforewriting — the prefix-to-provider mapping there is the source of truth (e.g.
confirm the exact
openrouter/model-string shape it parses). Add onesentence: cloud embeddings default to Jina (
JINA_API_KEY); pass--localor install
--extra local-embeddingsfor fully local operation.Verify: each table row's prefix appears in
grep -n "startswith\|prefix" src/utils/provider_routing.pyoutput or therouting match statements — confirm manually, then
just ci→ exit 0Test plan
No code changes; no new tests. Verification = the grep gates above plus a
human read-through of the rendered README section.
Done criteria
.env.exampleexists, ≥10 documented variables, zero real secret values.env.exampleand contains the provider tablesrc/(census command matches)just ciexits 0git status)plans/README.mdstatus row updatedSTOP conditions
Stop and report back (do not improvise) if:
them and extend the example only after confirming what they do from code.
provider_routing.pyhas gained/lost providers since planning (drift) —re-derive the table, and flag the change.
.envfor "reference" — don't;that file may contain real credentials.
Maintenance notes
constants.pyorprovider_routing.py,.env.examplemust gain a line in the same PR — reviewers should check.the configured cloud model needs a missing optional extra or env var, so
failures happen at launch rather than at the first LLM call.