Skip to content

feat(llm): add OrcaRouter as a first-class provider with API-key and OAuth 2.0 + PKCE login - #328

Open
zsanig22-dotcom wants to merge 1 commit into
aiming-lab:mainfrom
zsanig22-dotcom:orcarouter/task-6625
Open

zsanig22-dotcom wants to merge 1 commit into
aiming-lab:mainfrom
zsanig22-dotcom:orcarouter/task-6625

Conversation

@zsanig22-dotcom

Copy link
Copy Markdown

Adds OrcaRouter as a first-class LLM provider: a named entry in the provider table, a real model dropdown built from the gateway's own catalogue, and two ways to authenticate — a pasted sk-orca-… key and a Connect with OrcaRouter OAuth 2.0 + PKCE login. Both entries are usable independently and both end at the same downstream credential.

What OrcaRouter is

OrcaRouter is an OpenAI-compatible AI gateway built for both models and agents, with adaptive routing, automatic failover, zero-markup inference, observability, guardrails, and agent-tool governance.

It also runs gateway-level, zero-trust security for AI agents on the same endpoint — screening every prompt/response and governing every tool call on a default-deny basis, with no application code changes.

Discord: discord.gg/YEubt8enRA · X: https://x.com/OrcaRouter

I'm an engineer on the OrcaRouter team.

Authentication (both entries, one seam)

Credential acquisition is a small interface (CredentialSource in researchclaw/llm/orcarouter.py) with two adapters; provider requests, model discovery and every AI entry point read the resolved credential and never re-implement auth.

API key Connect with OrcaRouter
Config api_key_env: ORCAROUTER_API_KEY, researchclaw orcarouter key --set, or the settings page researchclaw orcarouter login, or the Connect button
Provider name orcarouter orcarouter-oauth
Storage project's existing secret mechanisms (env / tracked config) same CredentialStore, same masking/clear path
  • Flow B (out-of-band code) is the default; Flow A (loopback redirect) is selected with --flow loopback. The repository's product surface is a CLI plus a local researchclaw serve dashboard — both run where a browser redirect back to 127.0.0.1 is not guaranteed (headless/remote boxes), so the OOB code is the flow that always works; loopback is available and tested where a local browser exists. Flow C device grant is not implemented — it does not replace PKCE.
  • PKCE is S256 only: base64url(sha256(verifier)), no padding. A fresh verifier and state are drawn from secrets per attempt, the verifier never leaves the process until exchange (never in a URL, log, telemetry, or the page), and the loopback callback compares state in constant time before the code is used.
  • Origins are kept separate and are not derivable from each other: auth defaults to https://www.orcarouter.ai with authorize at /auth and exchange at POST /api/v1/auth/keys; inference and the catalogue default to https://api.orcarouter.ai/v1. ORCA_AUTH_BASE_URL / ORCA_API_BASE_URL / shared ORCA_BASE_URL override explicitly and win over the defaults; remote origins must be HTTPS, HTTP is accepted only for loopback. https://api.orcarouter.ai/v1/auth/keys is never built — the code path that would construct it raises instead, and a test asserts the string is absent from user-facing output.
  • PKCE returns a persistent OrcaRouter API key, not a refresh token. It is stored in the same credential store, reused across restarts until revoked, and never refreshed or re-minted on start (the gateway allows at most 10 PKCE keys per user per 24h). A relay 401 is terminal reauthentication: only the exact account and credential generation that issued the rejected request is marked needsReauth, so an in-flight failure from a previous generation cannot contaminate a fresh login, and the old secret is not silently deleted before a new login succeeds.
  • Denial, state mismatch, timeout, expired/reused code, 403, 429, and network errors all terminate with an actionable message instead of hanging or hot-looping, and response bodies are scrubbed of credential material before they reach a message.
  • The settings page releases the login lock on success, denial, exchange error, timeout, explicit cancel, switching flow, closing the panel, unmount, reload, and pagehide. A monotonic attempt/generation id stops a stale response from overwriting a newer login; pagehide clears the busy state and hint synchronously and issues a keepalive cancel to the server rather than relying on a finally that the generation guard would swallow.

Model catalogue and capability filtering

The model control is a real dropdown/search selector fed by GET https://api.orcarouter.ai/v1/models with the user's own bearer key, so the list is what that workspace can actually call. Vendor/model namespace is preserved verbatim. The key stays on the server: the browser receives minimal model metadata only.

Options are recomputed per entry point and re-filtered when the provider, task type, or attachment set changes; a selected model that no longer fits is cleared with a prompt to re-choose, never silently kept.

Entry point Filter
text chat / agent ?capability=chat and supported_endpoint_types ∩ {openai, anthropic, gemini, openai-response}; image-generation / openai-video / jina-rerank models excluded
multimodal understanding chat and architecture.input_modalities must explicitly contain the modality actually being sent; undeclared capability fails closed
embedding ?capability=embedding (or strict embeddings endpoint match)
image generation ?capability=image (or strict image-generation)
video generation strict openai-video
rerank strict jina-rerank

A live response is authoritative. On failure a small, verified cold-start seed / last-known-good catalogue is used with a degraded/refresh state shown in the UI — never free text, and a restored model id is re-validated against the current filtered list before it is re-selected. The seed is orcarouter/auto, anthropic/claude-opus-4.8, openai/gpt-5.5, google/gemini-3.5-flash, deepseek/deepseek-v4-pro, with verified context / input-modality / reasoning metadata retained (including GPT-5.5's low/medium/high/xhigh).

AI entry points covered

This repository's AI request surface, traced from create_llm_client outward, is text chat/agent completion — the research pipeline stages, the debate/tournament engines, code agents, and the researchclaw CLI all route through it — plus embedding/rerank and image generation, which are wired to their own backends. The OrcaRouter provider is registered in the central provider table (README.md, config.researchclaw.example.yaml, researchclaw/llm/__init__.py) and is therefore reachable from every chat/agent entry point at once. The capability filters above are implemented and tested for all six kinds even where this repository has no caller yet, so a future entry point cannot leak an incompatible model into its selector. No entry point in this repository sends an image to the chat client — messages are {role, content} text and the only image producer is pinned to Gemini's native API — so the multimodal selector is not applicable here and is recorded as such rather than faked.

Evidence

Item Source
Provider / product https://www.orcarouter.ai (verified 2026-09-17)
Inference POST https://api.orcarouter.ai/v1/chat/completions, OpenAI-compatible (verified with a real request through create_llm_client)
Model catalogue GET https://api.orcarouter.ai/v1/models (verified live; 16 chat models returned for the test workspace)
Auth / OAuth https://www.orcarouter.ai/auth authorize, POST https://www.orcarouter.ai/api/v1/auth/keys exchange, PKCE S256, no client secret, no pre-registered redirect URI
Key lifecycle / revocation Keys are workspace-scoped and revocable in the OrcaRouter dashboard; a revoked or rejected key yields a terminal 401 that marks only that credential generation for reauthentication
Terms / legal entity https://www.orcarouter.ai (site footer — terms, privacy, and the operating entity are published there)
Aggregator routing / resale authorization OrcaRouter routes to upstream vendors under its own agreements; the gateway is the contracting party for inference
Maintainer / verification date OrcaRouter team; documented surfaces above re-verified 2026-09-17
Discord · X discord.gg/YEubt8enRA · https://x.com/OrcaRouter

Screenshots of the real settings page (/providers) — both authentication entries side by side with the key masked, and the real model dropdown expanded — are produced by scripts/orcarouter_ui_evidence.py (real FastAPI app, real Chromium, live catalogue) and validated against the delivery checklist before hand-over. The bundle stays out of the patch: evidence has to be the output of a run of the code under test, so orca-evidence/ is git-ignored and tests/test_orcarouter_ui.py generates it, holds it to the checklist, and asserts nothing under it is tracked.

Tests

Run with pytest, per CONTRIBUTING.md:

Command Result
pytest tests/test_orcarouter_provider.py tests/test_orcarouter_cli.py -q 61 passed
pytest tests/test_orcarouter_pkce.py -q 22 passed
pytest tests/test_orcarouter_catalog.py tests/test_orcarouter_model_select.py -q 65 passed
pytest tests/test_orcarouter_server.py tests/test_orcarouter_ui.py -q 28 passed
pytest tests/test_orcarouter_live.py -q (live, real key) 5 passed
pytest tests/ -q 3139 passed, 49 skipped

Both adapters are asserted to produce the same credential result, and provider requests plus catalogue discovery are asserted not to care which adapter produced it. Tests cover API-key save/read/clear/masking; verifier/challenge/state generation; the authorize URL; the exact exchange path and body; successful persistence; denial; Flow A state mismatch; code reuse and expiry; scope downgrade; terminal classification of a corrupt key; precise-account reconnect on 401; 429 and network failure; and the assertion that neither the verifier nor the key appears in any log or error. Auth requests are asserted to reach only the auth origin and inference/catalogue only the API origin (or an explicit override), and a revoked key is asserted not to trigger a pseudo-refresh while a stale generation cannot overwrite fresh credentials. The PKCE tests drive the shipped connect adapter against a local fake auth server end to end (authorize → callback/OOB → exchange → persist); the live check drives create_llm_client and the model selector through the real gateway. All fixtures use fake keys and codes.

A note on the final commit: an earlier revision of this branch had committed the generated orca-evidence/ bundle into the repository. That is backwards — a committed screenshot is stale by construction and the delivery checklist refuses a patch that ships one — so the bundle is now git-ignored, generated at its default in-repo output by the test run, and checked from there.

…OAuth 2.0 + PKCE login

Signed-off-by: zsanig22-dotcom <zsanig22-dotcom@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant