feat(llm): recognize orcarouter/ gateway model refs alongside openrouter - #989
feat(llm): recognize orcarouter/ gateway model refs alongside openrouter#989XiaoHuo888-hue wants to merge 1 commit into
Conversation
The LLM client already strips a core-style "openrouter/<vendor>/<model>" prefix down to the bare "<vendor>/<model>" id a direct OpenRouter call needs. Mirror that for the OrcaRouter gateway: a namespaced "orcarouter/<vendor>/<model>" ref also strips to "<vendor>/<model>", while the auto-router "orcarouter/auto" keeps its prefix because OrcaRouter rejects the bare "auto" id with 503 model_not_found. Apply the same mirror to normalizeAdmissionModelRef, add baseURL inference for api.orcarouter.ai, document the gateway config in the README, and cover the new behavior with unit + harness tests. Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: XiaoHuo888 <sjh00112233@outlook.com>
rwmjhb
left a comment
There was a problem hiding this comment.
The helper-level normalization is well tested, but the new OrcaRouter behavior is not wired consistently into the production client paths:
-
Primary smart-extraction and CLI clients bypass
normalizeDirectModelRef.buildMemoryLlmClientconstructs its primary client with rawllmModel, and the CLI path likewise passes rawconfig.llm.model;createApiKeyClientserializesconfig.modelunchanged. Only admission-client construction calls the normalizer. Thereforeorcarouter/anthropic/claude-sonnet-4.6still reaches those outgoing requests triple-prefixed even though the new helper test says it should becomeanthropic/claude-sonnet-4.6. Please normalize every API-key direct-client construction path and add request-capture tests for primary and CLI clients. -
Reflection resolution does not preserve the advertised auto-router semantics.
splitProviderModel("orcarouter/auto")producesprovider="orcarouter",model="auto", andgenerateReflectionTextpasses those values to the embedded runner. The PR itself documents that OrcaRouter rejects bareauto, but no host-level test demonstrates that OpenClaw reconstructs the required gateway model ID or even recognizes this newly inferred provider from the plugin-onlyllm.baseURLconfiguration. Please implement or prove the complete runner-to-request behavior with a production-boundary test; helper inference alone is insufficient. -
A vendor-prefixed reflection model bypasses the new base-URL inference. With
llm.baseURL=https://api.orcarouter.ai/v1andanthropic/claude-sonnet-4.6,split.providerwins overinferProviderFromBaseURL, so reflection selects provideranthropicrather than the configured gateway. Make reflection resolution gateway-aware before generic provider splitting, preserving the model ID accepted by OrcaRouter, and test the completegenerateReflectionTextrunner arguments/request route.
Please centralize the gateway model-resolution rules so direct, admission, CLI, and reflection paths cannot drift. Non-blocking: remove or substantiate the gateway security marketing claims; this diff implements model routing, not those controls.
Local verification: the four related test files pass 42/42, the full orchestrator suite passes, and npm run build leaves the generated tree clean. GitHub currently reports no checks for this PR; the missing integration cases above are not covered by the green local suites.
This adds a dedicated OrcaRouter model-ref path rather than relying only on the generic OpenAI-compatible base URL, mirroring how this repo already treats the
openrouter/prefix. Named routers such asorcarouter/autopick an upstream per request. OrcaRouter is an OpenAI-compatible gateway that exposes 150+ models behind one API key, and it also provides gateway-level security controls for AI agents.What changed
src/llm-client.ts/src/admission-control.ts—normalizeDirectModelRef/normalizeAdmissionModelRefnow recognize theorcarouter/gateway prefix the same way they already handleopenrouter/:orcarouter/anthropic/claude-sonnet-4.6→anthropic/claude-sonnet-4.6(the namespaced id a direct OrcaRouter call accepts)orcarouter/auto→ staysorcarouter/auto(OrcaRouter rejects the bareautoid with503 model_not_found, so the auto-router keeps its prefix)index.ts—inferProviderFromBaseURLnow mapsapi.orcarouter.aito theorcarouterprovider, so a bare model name plus an OrcaRouter base URL resolves correctly on the reflection path.README.md— documented the OpenAI-compatible gatewayllmconfig with an OrcaRouter example (orcarouter/autoauto-router,https://api.orcarouter.ai/v1).Verification
npm run build(tsc) passes.test/admission-model-resolution.test.mjs,test/infer-provider-from-baseurl.test.mjs,test/llm-api-key-client.test.mjs,test/admission-lane-model-affinity.test.mjs(47 tests, 0 failures;llm-clients-and-authCI group green).createLlmClient:orcarouter/autoreturns a valid JSON completion (200), andanthropic/claude-sonnet-4.6also returns200. The triple-prefixedorcarouter/anthropic/claude-sonnet-4.6form is rejected by OrcaRouter with400, which is exactly why the normalize step strips the gateway prefix only when a vendor namespace remains.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.
I'm an engineer on the OrcaRouter team.