Auto-detect DEFAULT_PROVIDER from whichever LLM API key is set#18
Closed
mkonopelski-gd wants to merge 1 commit into
Closed
Auto-detect DEFAULT_PROVIDER from whichever LLM API key is set#18mkonopelski-gd wants to merge 1 commit into
mkonopelski-gd wants to merge 1 commit into
Conversation
Hand-editing .env to switch from OPENROUTER_API_KEY to ANTHROPIC_API_KEY
(without re-running specflow-init.sh) left DEFAULT_PROVIDER unset, and
docker-compose's hardcoded `${DEFAULT_PROVIDER:-openrouter}` fallback
silently forced openrouter — failing startup validation even though a
valid Anthropic key was present, which also made the TUI startup gate
misreport containers as "not running" (backend never reached ready).
Settings now infers DEFAULT_PROVIDER from ANTHROPIC_API_KEY/
OPENROUTER_API_KEY presence on every construction (not just at one-time
init), matching the contract already documented in
.env.quickstart.example. Explicit DEFAULT_PROVIDER still overrides.
docker-compose passes through blank instead of baking in "openrouter" so
that inference isn't pre-empted.
awrobel-gd
reviewed
Jul 3, 2026
Comment on lines
+238
to
+241
| @model_validator(mode="before") | ||
| @classmethod | ||
| def _infer_default_provider_from_keys(cls, data: object) -> object: | ||
| """Infer DEFAULT_PROVIDER from whichever API key is set, when not explicit. |
Contributor
There was a problem hiding this comment.
how is this hooked to the DEFAULT_PROVIDER field?? I dont see direct connection
awrobel-gd
reviewed
Jul 3, 2026
Comment on lines
+243
to
+248
| Single source of truth for the auto-detection documented in | ||
| .env.quickstart.example ("set ONE of the two keys ... if both are set, | ||
| OpenRouter is used by default"). Runs on every Settings() construction — | ||
| not only at `specflow-init.sh` time — so switching keys by hand later | ||
| (without re-running init) still resolves to the right provider instead | ||
| of silently falling back to OpenRouter and failing startup validation. |
Contributor
There was a problem hiding this comment.
this logic will be buried here - better to remove this docstring and just explain this in Settings class as it is now. And make sure README / QUICKSTART have same wording
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
.envto switch fromOPENROUTER_API_KEYtoANTHROPIC_API_KEY(without re-runningspecflow-init.sh) leftDEFAULT_PROVIDERunset. docker-compose's hardcoded${DEFAULT_PROVIDER:-openrouter}fallback then forcedopenrouter, failing backend startup validation (Missing environment variables: OPENROUTER_API_KEY) even though a valid Anthropic key was present.specflow tui:/health/readyreturned 503 (backend up but not ready), and the startup gate reused the "containers aren't running" screen for that case, making it look like Docker/container detection was broken when it wasn't.Settingsnow infersDEFAULT_PROVIDERfrom whichever ofANTHROPIC_API_KEY/OPENROUTER_API_KEYis actually set, on every construction — not just once at init time — matching the contract already documented in.env.quickstart.example("set ONE of the two keys ... if both are set, OpenRouter is used by default"). An explicitDEFAULT_PROVIDERstill wins.docker-compose.ymlnow passesDEFAULT_PROVIDERthrough blank when unset instead of baking inopenrouter, so the new inference isn't pre-empted before it reaches the backend.Test plan
backend/test/core/test_enums.py::TestDefaultProviderInference— new cases: anthropic-only, openrouter-only, both-set, neither-set, explicit-override-wins, blank-string-still-infers (mirrors compose's${DEFAULT_PROVIDER:-}).backend/test/services/test_startup_validation.py::test_anthropic_only_passes_without_explicit_default_provider— regression test reproducing the exact original failure.make unit-tests— backend (1897 passed, 40 skipped) + mcp_server (569 passed), all green.make check-complexity-diff— average CC unchanged (3.66 → 3.66); only the new method (_infer_default_provider_from_keys, CC 5) added.