feat: provider key management from Settings — v0.50.159 (PR #867 by @bergeouss, closes #586)

New Providers tab in Settings lets users add/update/remove API keys without editing .env. Six review fixes applied. 18 tests.
This commit is contained in:
nesquena-hermes
2026-04-22 18:09:22 -07:00
committed by GitHub
parent e3607855b1
commit 04b00065f9
8 changed files with 917 additions and 3 deletions

View File

@@ -306,6 +306,7 @@ from api.workspace import (
)
from api.upload import handle_upload, handle_transcribe
from api.streaming import _sse, _run_agent_streaming, cancel_stream
from api.providers import get_providers, set_provider_key, remove_provider_key
from api.onboarding import (
apply_onboarding_setup,
get_onboarding_status,
@@ -585,6 +586,10 @@ def handle_get(handler, parsed) -> bool:
if parsed.path == "/api/models/live":
return _handle_live_models(handler, parsed)
# ── Providers (GET) ──
if parsed.path == "/api/providers":
return j(handler, get_providers())
if parsed.path == "/api/settings":
settings = load_settings()
# Never expose the stored password hash to clients
@@ -948,6 +953,28 @@ def handle_post(handler, parsed) -> bool:
except RuntimeError as e:
return bad(handler, str(e), 500)
# ── Providers (POST) ──
if parsed.path == "/api/providers":
provider_id = (body.get("provider") or "").strip().lower()
api_key = body.get("api_key")
if not provider_id:
return bad(handler, "provider is required")
if api_key is not None:
api_key = str(api_key).strip() or None
result = set_provider_key(provider_id, api_key)
if not result.get("ok"):
return bad(handler, result.get("error", "Unknown error"))
return j(handler, result)
if parsed.path == "/api/providers/delete":
provider_id = (body.get("provider") or "").strip().lower()
if not provider_id:
return bad(handler, "provider is required")
result = remove_provider_key(provider_id)
if not result.get("ok"):
return bad(handler, result.get("error", "Unknown error"))
return j(handler, result)
if parsed.path == "/api/reasoning":
# CLI-parity /reasoning handler — writes to the same config.yaml keys
# the CLI uses (display.show_reasoning, agent.reasoning_effort) so a