feat: OpenRouter as a second provider with admin provider management #39
Reference in New Issue
Block a user
Delete Branch "feat/multi-provider-openrouter"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
feat: OpenRouter as a second provider with admin provider management
Implements the multi-provider plan (
.omo/plans/multi-provider-openrouter.md): OpenRouter joins NeuralWatt as the second dispatch provider, with full provider lifecycle management in the admin portal.What's in it
Config
DispatchProvidergainshas_energy_telemetry: bool = Falseandenabled: bool = Trueconfig/config.yamlshipsneuralwatt(telemetry on) +openrouter(telemetry off, both enabled);.env.examplegainsOPENROUTER_API_KEY=Admin portal — Providers page (new)
/admin/providerswith list/create/edit/delete UI following the existing design system (switches,escapeHtml, restart-required messaging, provenance badges)GET/POST/DELETE /admin/api/providers{,/{name}}persisting toconfig.local.yamloverlay — malformed URL → 422, deleting the default provider → 422, deleting a base-configured provider → 403#generated-athidden below 900px)Poller — multi-provider
fetch_openrouter()+parse_openrouter_model(): public catalog, virtual routers (openrouter/autoetc.) excluded, variants kept (:batch→latency_class='flex',:free→standard),id→model_id,canonical_slug→base_model_id, per-token prices normalized to per-1Mmain()iterates enabled providers with failure isolation (one provider's outage/zero-catalog doesn't abort the other), per-providermark_stale(provider=), and in-process tiering so new rows are routable immediatelyDispatcher
default_provider(fixesllm-router/openai/gpt-6-astra-style pins dispatching to the wrong base URL)_provider_refusal_since): a NeuralWatt account refusal no longer poisons classification for a router pointed at OpenRouter; cooldown reusesclassifier.cooldown_secondshas_energy_telemetry=falseproviders — OpenRouter keepalive comments (: OPENROUTER PROCESSING) pass through to clients, telemetry logs NULL, nothing fabricatedTesting
test_admin_providers,test_openrouter_poller,test_multi_provider_poller,test_multi_provider_dispatch, migratedtest_classifier_cascade)/admin/providersat 1400px and 800px: full CRUD round-trip, XSS escape check, zero console errors after idle (screenshots in.omo/evidence/)Operational follow-ups (after merge)
systemctl --user restart llm-router.service— the running instance on 8080 still serves the pre-OpenRouter code (production.envalready hasOPENROUTER_API_KEY, only in the main checkout)PYTHONPATH=src python -m poller— first run upserts + tiers the OpenRouter catalognullapi_key_envwhen the field is left empty (422 until a value is entered) — cosmetic, pre-existing pattern