Skip to content

feat(llm): recognize orcarouter/ gateway model refs alongside openrouter - #989

Open
XiaoHuo888-hue wants to merge 1 commit into
CortexReach:masterfrom
XiaoHuo888-hue:add-orcarouter-provider
Open

feat(llm): recognize orcarouter/ gateway model refs alongside openrouter#989
XiaoHuo888-hue wants to merge 1 commit into
CortexReach:masterfrom
XiaoHuo888-hue:add-orcarouter-provider

Conversation

@XiaoHuo888-hue

Copy link
Copy Markdown

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 as orcarouter/auto pick 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.tsnormalizeDirectModelRef / normalizeAdmissionModelRef now recognize the orcarouter/ gateway prefix the same way they already handle openrouter/:
    • orcarouter/anthropic/claude-sonnet-4.6anthropic/claude-sonnet-4.6 (the namespaced id a direct OrcaRouter call accepts)
    • orcarouter/auto → stays orcarouter/auto (OrcaRouter rejects the bare auto id with 503 model_not_found, so the auto-router keeps its prefix)
  • index.tsinferProviderFromBaseURL now maps api.orcarouter.ai to the orcarouter provider, so a bare model name plus an OrcaRouter base URL resolves correctly on the reflection path.
  • README.md — documented the OpenAI-compatible gateway llm config with an OrcaRouter example (orcarouter/auto auto-router, https://api.orcarouter.ai/v1).

Verification

  • npm run build (tsc) passes.
  • New + existing unit tests pass: 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-auth CI group green).
  • Live end-to-end against the real gateway using the plugin's own createLlmClient: orcarouter/auto returns a valid JSON completion (200), and anthropic/claude-sonnet-4.6 also returns 200. The triple-prefixed orcarouter/anthropic/claude-sonnet-4.6 form is rejected by OrcaRouter with 400, 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.

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 rwmjhb left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The helper-level normalization is well tested, but the new OrcaRouter behavior is not wired consistently into the production client paths:

  1. Primary smart-extraction and CLI clients bypass normalizeDirectModelRef. buildMemoryLlmClient constructs its primary client with raw llmModel, and the CLI path likewise passes raw config.llm.model; createApiKeyClient serializes config.model unchanged. Only admission-client construction calls the normalizer. Therefore orcarouter/anthropic/claude-sonnet-4.6 still reaches those outgoing requests triple-prefixed even though the new helper test says it should become anthropic/claude-sonnet-4.6. Please normalize every API-key direct-client construction path and add request-capture tests for primary and CLI clients.

  2. Reflection resolution does not preserve the advertised auto-router semantics. splitProviderModel("orcarouter/auto") produces provider="orcarouter", model="auto", and generateReflectionText passes those values to the embedded runner. The PR itself documents that OrcaRouter rejects bare auto, but no host-level test demonstrates that OpenClaw reconstructs the required gateway model ID or even recognizes this newly inferred provider from the plugin-only llm.baseURL configuration. Please implement or prove the complete runner-to-request behavior with a production-boundary test; helper inference alone is insufficient.

  3. A vendor-prefixed reflection model bypasses the new base-URL inference. With llm.baseURL=https://api.orcarouter.ai/v1 and anthropic/claude-sonnet-4.6, split.provider wins over inferProviderFromBaseURL, so reflection selects provider anthropic rather than the configured gateway. Make reflection resolution gateway-aware before generic provider splitting, preserving the model ID accepted by OrcaRouter, and test the complete generateReflectionText runner 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.

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.

2 participants