Skip to content

[Feature]: Honor compat.openRouterRouting in BYOK custom providers (OpenRouter provider routing: sort/order/only) #288

Description

@SvenMeyer

Product or interface

CLI - headless (same config/runtime path as CLI - interactive TUI)

Use case and problem

When an OpenRouter model is used as a BYOK custom_provider, there is no way to control OpenRouter provider routing from config.yaml. OpenRouter can route the same model to different upstream inference providers (DeepSeek, DeepInfra, Fireworks, Morph, Relace, ...), and its request body supports a provider object (sort, order, only, ignore, allow_fallbacks, data_collection, zdr, max_price, require_parameters, ...). See https://openrouter.ai/docs/guides/routing/provider-selection.

The underlying transport already supports this, but the BYOK config reader drops it before it reaches the model, so the field is silently ignored:

  • packages/local-runtime-v2/src/service/model-system/resolution/model-resolver-byok.tsreadModelCompat() only reads the boolean keys (BOOLEAN_COMPAT_KEYS), plus maxTokensField, thinkingFormat, and cacheControlFormat. openRouterRouting / vercelGatewayRouting are not read.
  • packages/local-runtime-v2/src/service/model-system/contracts.tsLocalModelCompatOverrides does not include openRouterRouting.
  • The pi transport does honor it: third_party/pi-mono/packages/ai/src/providers/openai-completions.ts (~L619):
    if (model.compat?.openRouterRouting) {
      (params as any).provider = model.compat.openRouterRouting;
    }

Today the only config-only workaround is OpenRouter's model-ID routing variants (deepseek/deepseek-v4.1-flash:nitro, :floor, :exacto). Those cover sort, but they cannot express pinning (only/order), exclusions (ignore), allow_fallbacks: false, data_collection/zdr, or max_price.

Desired behavior

Honor a model-level compat.openRouterRouting (and ideally compat.vercelGatewayRouting) for BYOK custom providers. Example config.yaml:

custom_provider:
  openrouter:
    name: openrouter
    kind: custom
    enabled: true
    api: openai-completions
    options:
      apiKey: sk-or-v1-...
      baseURL: https://openrouter.ai/api/v1
      authMode: api-key
    models:
      deepseek/deepseek-v4.1-flash:
        reasoning: true
        limit:
          context: 1048576
          output: 131072
        compat:
          openRouterRouting:
            only: ["DeepSeek"]
            allow_fallbacks: false

Acceptance criteria:

  • compat.openRouterRouting (and vercelGatewayRouting) declared in LocalModelCompatOverrides and validated in readModelCompat (object with the documented keys; drop unknown/malformed values gracefully, consistent with the existing defensive readers).
  • The object is merged into the resolved model.compat so the pi transport sends it as the request provider field.
  • Works for both a pinned provider (only/order) and a sort strategy.
  • Existing behavior is unchanged when the field is absent (no provider field sent).

Platform

Multiple platforms

Alternatives and additional context

  • Related: [Feature]: Openrouter support in custom models #215 (generic OpenRouter support).
  • Workaround used today: one config entry per routing variant (:nitro, :floor, :exacto). Verified these are resolved server-side by OpenRouter from the model ID alone, so they need no request field — but they only cover sorting.
  • mcode --version: 0.5.1.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions