scrum: validate API key presence when enabling a cloud provider #2

Open
profit wants to merge 1 commits from scrum/provider-key-validation into main
Owner

Summary

Surfaced by the lakehouse scrum-master pipeline (finding F5, grounded by reading admin_save_config at line 7058). A POST /api/admin/config body like:

{"providers": {"openai": {"enabled": true, "api_key": ""}}}

...was silently accepted even when no key was stored and no env var was set. The provider got flagged enabled in the UI, and every subsequent query against it 401d at call time with an opaque error.

Fix

When an incoming patch sets enabled=True, compute the effective key — incoming body → stored config → env var (get_api_key(name) already handles the env fallback at line 1888). If the effective key is empty, reject with 400:

{"error": "Cannot enable provider openai without an API key. ..."}

Behavior unchanged for disabled providers and for updates that dont touch the enabled flag.

Prerequisite commits

Same 4-commit pre-existing local-main tail as PR #1 (needs fa6ccff Ollama Cloud provider because get_api_key has ollama_cloud in its env-var map there). Stacked on the same base, will rebase cleanly after #1 lands or main is pushed.

Not in this PR

  • F4 cloud-determinism consensus — this is a feature (wrap every cloud call in N=3 + majority vote), not a defect. Tripling cost on every cloud call needs a product call from maintainer, not a scrum auto-patch.
  • F2 / F3 (tier middleware, sentinel) — false positives. Both fully implemented in code, reviewer couldnt see them through tree-split distillation.
## Summary Surfaced by the lakehouse scrum-master pipeline (finding **F5**, grounded by reading `admin_save_config` at line 7058). A `POST /api/admin/config` body like: ```json {"providers": {"openai": {"enabled": true, "api_key": ""}}} ``` ...was silently accepted even when no key was stored and no env var was set. The provider got flagged enabled in the UI, and every subsequent query against it 401d at call time with an opaque error. ## Fix When an incoming patch sets `enabled=True`, compute the **effective key** — incoming body → stored config → env var (`get_api_key(name)` already handles the env fallback at line 1888). If the effective key is empty, reject with `400`: ```json {"error": "Cannot enable provider openai without an API key. ..."} ``` Behavior unchanged for disabled providers and for updates that dont touch the `enabled` flag. ## Prerequisite commits Same 4-commit pre-existing local-main tail as PR #1 (needs `fa6ccff Ollama Cloud provider` because `get_api_key` has `ollama_cloud` in its env-var map there). Stacked on the same base, will rebase cleanly after #1 lands or main is pushed. ## Not in this PR - **F4 cloud-determinism consensus** — this is a feature (wrap every cloud call in N=3 + majority vote), not a defect. Tripling cost on every cloud call needs a product call from maintainer, not a scrum auto-patch. - **F2 / F3** (tier middleware, sentinel) — false positives. Both fully implemented in code, reviewer couldnt see them through tree-split distillation.
profit added 5 commits 2026-04-24 11:14:44 +00:00
New provider: Ollama Cloud (ollama.com)
- Native Ollama chat API with bearer token auth
- Provider card in Admin → Providers tab
- "Ollama Cloud" tab with Pull Models button (fetches 36 models)
- Search/filter models, one-click Add
- Models route as ollama_cloud::modelname through query_ollama_cloud()
- Test button verifies connection

OpenRouter fix:
- Cleared bad API key from config (was dd00bea4... not sk-or-)
- Real key from /home/profit/.env now used (sk-or-v1-579...)
- Fixed OpenAI provider that had wrong base_url (ollama.com→api.openai.com)
- Bumped OR timeout to 180s for free model rate limits

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Endpoint now returns all models, not just free ones
- Each model includes: name, context_length, free flag, prompt/completion cost
- UI shows pricing: "128K ctx · $2.50/M tok" for paid, "128K ctx · free" for free
- Filter dropdown: All Models / Free Only / Paid Only
- Search still works alongside the filter
- 29 free + 314 paid models available (GPT-5.4, Grok 4.20, Gemini 3.1, etc)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two bugs preventing adaptive mode from working:
- ml-adaptive was missing from ML_IDS array (line 3160) — model checkboxes
  never rendered, so no models were selected
- adaptive-synthesizer was missing from populateAllSelects() ids array
  (line 3733) — synthesizer/judge dropdown was always empty

Both are single-line fixes. The backend pipeline (run_adaptive) was
complete and correct — self-eval, RAG retrieval, escalation, quality
scoring, KB storage all work. The UI just never wired the config.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
New mode: Deep Analysis — 6-phase autonomous pipeline:
1. Research: all selected models answer in parallel
2. Debate: models challenge each other's findings
3. Consensus: merge research with critiques, identify strong/weak points
4. Self-Eval: structured scoring (accuracy, completeness, actionability, nuance)
5. Final Synthesis: strongest model produces definitive answer
6. Knowledge Base: result stored for future RAG retrieval

Designed for cloud models (Ollama Cloud, OpenRouter). Every successful
run trains the local knowledge base so future adaptive runs benefit.
Purple accent in mode selector to distinguish from standard modes.

Token tracking fix:
- Added est_tokens, input_chars, output_chars columns to team_runs
- save_run() now calculates and stores token estimates for ALL runs
- Both logged-in and public/demo/showcase runs track tokens
- Enables accurate usage analytics across all users

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
admin_save_config silently accepted enabled=True for any provider even
when no api_key was configured (neither in the request body, nor already
stored, nor available in the provider's env var). Result: /api/admin/config
returned 200, the provider was marked enabled in UI and config, then every
actual query to that provider 401'd at call time with an opaque error.

The fix: when the incoming patch sets enabled=True, compute the effective
key (request body preferred, falling back to stored config, falling back
to the provider's env var via get_api_key()) and reject with 400 if the
effective key is empty. Keeps all existing behavior for disabled providers
and for updates that don't touch enabled.

Surfaced by lakehouse scrum-master pipeline run 2026-04-24 (finding F5).
This pull request can be merged automatically.
This branch is out-of-date with the base branch
You are not authorized to merge this pull request.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin scrum/provider-key-validation:scrum/provider-key-validation
git checkout scrum/provider-key-validation
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: profit/llm-team-ui#2
No description provided.