Skip to content

feat: add OrcaRouter as a named model-prefix provider - #111

Merged
yetone merged 1 commit into
yetone:mainfrom
putraperdana1207-pixel:orcarouter-provider
Aug 31, 2026
Merged

feat: add OrcaRouter as a named model-prefix provider#111
yetone merged 1 commit into
yetone:mainfrom
putraperdana1207-pixel:orcarouter-provider

Conversation

@putraperdana1207-pixel

Copy link
Copy Markdown

Adds OrcaRouter as a named, first-class LLM provider in the same shape Cumora already routes Novita: prefix an agent's model id with orcarouter/ (e.g. orcarouter/openai/gpt-4o-mini) and the call is forwarded to OrcaRouter with the prefix stripped. That means anyone running Cumora Cloud or BYOA can point their agents at OrcaRouter from the model id alone — no custom base URL, no treating a gateway as an anonymous OpenAI-compatible endpoint.

OrcaRouter is an OpenAI-compatible AI gateway built for both models and agents. Like OpenRouter, it exposes a provider/model namespace across many models — but it also combines adaptive routing, automatic failover, zero-markup inference, observability, guardrails, and agent-tool governance behind the same endpoint. Adding orcarouter as a first-class provider means this project's users can use that stack directly, without treating OrcaRouter as an anonymous custom base URL. 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.

Implementation mirrors the existing novita/ route in server/src/llm.ts: a new orcarouter.ts module with a base-URL-swapped OpenAI client, plus an else if (isOrcaRouterModel(...)) branch in withProviderRouting that strips the prefix and forwards. The one difference from Novita is that OrcaRouter speaks the Responses API natively (verified live, streaming and non-streaming), so no Chat-Completions translation shim is needed. Like Novita, an unset ORCAROUTER_API_KEY degrades to the tenant's normal client instead of failing the run.

Verified:

  • npm run server:typecheck, npm run typecheck, npm run lint, npm run guard:big-brain, npm run guard:llm-tracked all pass.
  • Unit tests: new server/src/__tests__/orcarouter.test.ts plus the full non-Redis server suite (817 pass / 0 fail).
  • Live smoke against the real OrcaRouter API with a real key: non-streaming responses.create and streaming both return 200 with valid Responses output, and a client obtained via getLlmClient correctly routes an orcarouter/* model to OrcaRouter.

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

I'm an engineer on the OrcaRouter team.

Mirrors the existing Novita provider route in llm.ts: agents opt in by
prefixing their model id with `orcarouter/`, and the call is forwarded
to api.orcarouter.ai with the prefix stripped. Unlike Novita (which has
no Responses API and needs a translation shim), OrcaRouter speaks the
Responses API natively, so the route is a pure base-URL swap through
server/src/orcarouter.ts.

Degrades like Novita: when ORCAROUTER_API_KEY is unset, an orcarouter/*
model falls through to the tenant's normal client instead of failing the
run with a bare-bearer 401.
@yetone

yetone commented Aug 30, 2026

Copy link
Copy Markdown
Owner

Thanks for the clean implementation, and for disclosing your affiliation up front — that's appreciated.

Technical review: this passes. I went through it against the Novita route it mirrors and found nothing to fix:

  • withProviderRouting is a faithful generalization of withNovitaRouting; the rename is carried through novita.ts's doc comments so the cross-references stay honest.
  • The degrade-not-die guard is right: an unset ORCAROUTER_API_KEY falls through to the tenant's normal client rather than sending a bare bearer to api.orcarouter.ai, and warnProviderUnconfiguredOnce keeps that to one log line per provider rather than one per hop.
  • isOrcaRouterModel(undefined) is false, so an unmodelled call passes straight through.
  • Fully inert by default: opt-in needs both an env key and an orcarouter/ model prefix.
  • I checked whether the missing timeout on orcarouterClient() was a regression — novitaClient() omits it too, so this is parity with the pattern you copied, not a new gap. (Both arguably should carry SDK_TIMEOUT_MS; that's a pre-existing issue for a separate PR, not yours to fix here.)

What's holding it: whether Cumora carries a third-party gateway as a named first-class provider isn't a code-quality question — it's a product and positioning call about the project, including what goes in .env.example and what the Novita precedent does or doesn't commit us to. That belongs to @yetone, not to me, so I'm not going to merge it on his behalf. I've flagged it for him.

Nothing is blocked on you, and no changes are requested. I'll update this thread either way.

@putraperdana1207-pixel

Copy link
Copy Markdown
Author

Thanks for the detailed review — glad the withProviderRouting generalization and the degrade-not-die fallthrough held up against the Novita route, and thanks for confirming the timeout omission is parity with novitaClient() rather than a new gap.

Agreed that the remaining question is a product/positioning call for @yetone, not a code issue. Nothing is outstanding on my side; I'm happy to make whatever adjustments that decision implies — the .env.example entry, docs, or any rework if the owner wants a different shape.

@yetone
yetone merged commit 9905bb3 into yetone:main Aug 31, 2026
6 checks passed
@yetone yetone mentioned this pull request Aug 31, 2026
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