feat(providers): add OrcaRouter as a named provider - #6978
Conversation
Add OrcaRouter (https://api.orcarouter.ai/v1) as a first-class named provider alongside OpenRouter, mirroring the existing OpenAI-compatible gateway wiring. - providers/orcarouter: chat completions + tool loop + streaming, targeting https://api.orcarouter.ai/v1 with the OpenAI SDK - app/api/providers/orcarouter/models: enumerates the public OrcaRouter model catalog (https://api.orcarouter.ai/v1/models) - registry/types/models/utils/attachments/stores/hooks/loader: register the orcarouter provider id across all model pickers, BYOK key resolution, attachment support, and the model-list sync - docs: mention OrcaRouter in the Agent block provider list Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: XiaoHuo888-hue <jinhao.song@myflashcloud.com>
|
Someone is attempting to deploy a commit to the Sim Team on Vercel. A member of the Team first needs to authorize it. |
PR SummaryMedium Risk Overview A public catalog route lists chat-capable models (no key required to list). Execution supports streaming, the tool loop, and structured output via Keys resolve via workspace/org BYOK or a user-supplied key. Docs and env/schema wiring mention the new provider. Reviewed by Cursor Bugbot for commit 3cafb60. Bugbot is set up for automated code reviews on this repo. Configure here. |
Greptile SummaryAdds OrcaRouter as an OpenAI-compatible named provider, including model discovery, execution and tool-loop support, BYOK registration, dynamic model-store integration, attachment metadata, contracts, tests, and documentation.
Confidence Score: 4/5The self-hosted environment-key fallback must be wired into OrcaRouter key resolution before merging; the remaining typing issue is non-blocking. Self-hosted executions configured only through ORCAROUTER_API_KEY deterministically fail because the new resolver never reads that variable, while the provider requires the resolved request key. Files Needing Attention: apps/sim/lib/api-key/byok.ts; apps/sim/providers/orcarouter/index.ts; apps/sim/providers/orcarouter/utils.ts
|
| Filename | Overview |
|---|---|
| apps/sim/lib/api-key/byok.ts | Adds OrcaRouter BYOK resolution but omits the declared environment-key fallback, breaking documented self-hosted inference configuration. |
| apps/sim/providers/orcarouter/index.ts | Implements OrcaRouter inference, streaming, structured output, and tool loops; behavior broadly follows OpenRouter, but the new code contains several prohibited any boundaries. |
| apps/sim/app/api/providers/orcarouter/models/route.ts | Adds public model discovery, chat-capability filtering, provider prefixing, blacklist handling, caching, and contract-shaped responses. |
| apps/sim/providers/models.ts | Registers OrcaRouter as a dynamic reseller provider and adds runtime catalog synchronization. |
| apps/sim/app/workspace/[workspaceId]/providers/provider-models-loader.tsx | Loads OrcaRouter models into both provider definitions and the workspace provider store. |
| apps/sim/lib/api/contracts/providers.ts | Adds the typed client contract for the OrcaRouter model-catalog route. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart LR
Catalog[OrcaRouter /v1/models] --> Route[Model catalog route]
Route --> Query[Provider models query]
Query --> Store[Providers store]
Store --> Picker[Agent model picker]
Picker --> Resolver[Provider and API-key resolution]
BYOK[Workspace or organization BYOK] --> Resolver
Env[ORCAROUTER_API_KEY] -. missing fallback .-> Resolver
Resolver --> Provider[OrcaRouter provider]
Provider --> API[OrcaRouter chat completions]
Provider --> Tools[Sim tool execution loop]
Reviews (1): Last reviewed commit: "feat(providers): add OrcaRouter as a nam..." | Re-trigger Greptile
| const isOrcaRouterModel = | ||
| provider === 'orcarouter' || | ||
| useProvidersStore.getState().providers.orcarouter.models.includes(model) | ||
| if (isOrcaRouterModel) { | ||
| if (workspaceId) { | ||
| const byokResult = await getBYOKKey(workspaceId, 'orcarouter') | ||
| if (byokResult) { | ||
| logger.info('Using BYOK key for OrcaRouter', { | ||
| model, | ||
| workspaceId, | ||
| scope: byokResult.scope, | ||
| }) | ||
| return byokResult | ||
| } | ||
| } | ||
| if (userProvidedKey) { | ||
| return { apiKey: userProvidedKey, isBYOK: false } | ||
| } | ||
| throw new Error(`API key is required for OrcaRouter ${model}`) | ||
| } |
There was a problem hiding this comment.
Environment key fallback is skipped
When a self-hosted deployment configures ORCAROUTER_API_KEY without a workspace BYOK or per-block key, this branch throws instead of using the documented environment fallback, causing every OrcaRouter inference request to fail with an API-key-required error.
| const isOrcaRouterModel = | |
| provider === 'orcarouter' || | |
| useProvidersStore.getState().providers.orcarouter.models.includes(model) | |
| if (isOrcaRouterModel) { | |
| if (workspaceId) { | |
| const byokResult = await getBYOKKey(workspaceId, 'orcarouter') | |
| if (byokResult) { | |
| logger.info('Using BYOK key for OrcaRouter', { | |
| model, | |
| workspaceId, | |
| scope: byokResult.scope, | |
| }) | |
| return byokResult | |
| } | |
| } | |
| if (userProvidedKey) { | |
| return { apiKey: userProvidedKey, isBYOK: false } | |
| } | |
| throw new Error(`API key is required for OrcaRouter ${model}`) | |
| } | |
| const isOrcaRouterModel = | |
| provider === 'orcarouter' || | |
| useProvidersStore.getState().providers.orcarouter.models.includes(model) | |
| if (isOrcaRouterModel) { | |
| if (workspaceId) { | |
| const byokResult = await getBYOKKey(workspaceId, 'orcarouter') | |
| if (byokResult) { | |
| logger.info('Using BYOK key for OrcaRouter', { | |
| model, | |
| workspaceId, | |
| scope: byokResult.scope, | |
| }) | |
| return byokResult | |
| } | |
| } | |
| if (userProvidedKey) { | |
| return { apiKey: userProvidedKey, isBYOK: false } | |
| } | |
| if (env.ORCAROUTER_API_KEY) { | |
| return { apiKey: env.ORCAROUTER_API_KEY, isBYOK: false } | |
| } | |
| throw new Error(`API key is required for OrcaRouter ${model}`) | |
| } |
| targetPayload: any, | ||
| messages: any[], | ||
| responseFormat: any, | ||
| model: string | ||
| ): Promise<any[]> { | ||
| const useNative = await supportsNativeStructuredOutputs(model) |
There was a problem hiding this comment.
Provider boundaries use explicit any
The new provider uses any across response-format payloads, request payloads, error metadata, and forced-tool responses, bypassing the repository's type-safety requirement and preventing TypeScript from detecting incompatible provider fields or unsafe property access as the API evolves.
Context Used: TypeScript conventions and type safety (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 3 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 3cafb60. Configure here.
| (contentType === 'video' && BEDROCK_VIDEO_FORMATS.has(extension)) | ||
| ) | ||
| case 'openrouter': | ||
| case 'orcarouter': |
There was a problem hiding this comment.
PDF attachments formatted incorrectly
High Severity
orcarouter is registered for image and PDF attachments like OpenRouter, but formatMessagesForProvider only routes OpenRouter through buildOpenRouterMessageContent. OrcaRouter falls through to buildOpenAICompatibleChatContent, which sends every attachment as image_url, so PDFs never become file parts and multimodal document runs fail or misbehave.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 3cafb60. Configure here.
| return { apiKey: userProvidedKey, isBYOK: false } | ||
| } | ||
| throw new Error(`API key is required for OrcaRouter ${model}`) | ||
| } |
There was a problem hiding this comment.
Env API key never used
Medium Severity
ORCAROUTER_API_KEY is documented as a self-hosted fallback for inference, and similar BYOK resellers (together, baseten, fireworks) fall back to their env keys after BYOK and the block key. The new OrcaRouter branch only tries BYOK then the user-provided key, then throws, so a configured env key alone never authorizes requests.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit 3cafb60. Configure here.
| ...vllmModels, | ||
| ...litellmModels, | ||
| ...openrouterModels, | ||
| ...orcarouterModels, |
There was a problem hiding this comment.
Copilot mock missing provider
Medium Severity
getModelOptions now reads providers.orcarouter.models, but the copilot metadata fallback mock still omits orcarouter. When that mock replaces the store, option resolution throws, is caught, and Agent model options come back empty for copilot.
Reviewed by Cursor Bugbot for commit 3cafb60. Configure here.


Summary
Add OrcaRouter as a first-class named provider in Sim's model picker, mirroring the existing OpenRouter integration. OrcaRouter is an OpenAI-compatible gateway (
https://api.orcarouter.ai/v1, key prefixsk-orca-) that routes across many upstream providers — the same shape as OpenRouter, so it fits the existing reseller-provider wiring with no new architectural surface.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.
What changed
orcarouterprovider (apps/sim/providers/orcarouter/): OpenAI-compatible chat completions with the full tool loop and streaming, using the same shared streaming/tool helpers as the other OpenAI-compatible providers. Targetshttps://api.orcarouter.ai/v1via the OpenAI SDK.apps/sim/app/api/providers/orcarouter/models/route.ts): enumerates the public OrcaRouter/v1/modelscatalog (like OpenRouter's, no key required to list), filtering to chat-capable models.ProviderId/ProviderName/BYOKProviderIdunions,PROVIDER_DEFINITIONS+DYNAMIC_MODEL_PROVIDERS, provider metadata, attachment support (images + PDFs), the providers store, the model-list loader, and the query/contract wiring.orcarouterkeys, with workspace/org key support.Models are typed as
orcarouter/<id>and appear in the model combobox automatically via the catalog route.Testing
vitest— all provider, blocks, stores, contracts, and api-provider tests pass: 2807 tests across 194 files (including a new 10-testorcarouterProvidersuite).tsc --noEmit— clean.biome check— clean on all changed files.check:api-validation— passes (baseline route count bumped 1162 → 1163 for the new route).GET https://api.orcarouter.ai/v1/models→200POST https://api.orcarouter.ai/v1/chat/completionswithmodel: "orcarouter/auto"→200, returnedORCA-LIVE-OK(routed togemini-3.1-flash-lite).Checklist
Disclosure: I'm an engineer on the OrcaRouter team.