feat(llm): gate OpenRouter routes on configured key - #11362
Conversation
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_a6fc4379-67b0-47c2-be62-0ade5bb07312) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0fcd56d83c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if openrouter_configured(): | ||
| return (_OPENROUTER_PREFERRED_MODELS.get(feature, _OPENROUTER_PREFERRED_MODEL), 'openrouter') |
There was a problem hiding this comment.
Prefix OpenRouter's OpenAI model ID
When OPENROUTER_API_KEY is set and gateway feature mode is off, this returns the bare model gpt-5.6-luna with provider openrouter; get_llm() passes that token to providers._api_model_name(), which only prefixes Gemini models. The gateway and new rate card both correctly use openai/gpt-5.6-luna, so direct requests for every newly preferred feature are sent with the wrong OpenRouter model ID and fail instead of using Luna. Canonicalize the OpenAI prefix in the direct-provider path and test the constructed client's model name.
AGENTS.md reference: backend/AGENTS.md:L189-L189
Useful? React with 👍 / 👎.
| if override is not None and not (openrouter_configured() and legacy_provider == 'openrouter'): | ||
| model = override.primary.model | ||
| provider = override.primary.provider | ||
| else: | ||
| model = legacy_model |
There was a problem hiding this comment.
Preserve vendor BYOK credentials on gateway routes
When both OPENROUTER_API_KEY and gateway feature mode are enabled for a BYOK request, get_llm() forwards the user's vendor key as X-Omi-Byok-OpenAI-Key, but this branch makes the gateway artifact's provider openrouter. executor._execute_route() then checks has_provider_key('openrouter') and fails closed with MISSING_BYOK_KEY before dispatch, even though enrolled BYOK users correctly supplied their required OpenAI key. Remap OpenRouter's vendor-prefixed model to the corresponding vendor provider/model when serving BYOK traffic and add the legacy-principal coverage required for a new fail-closed route.
AGENTS.md reference: AGENTS.md:L121-L121
Useful? React with 👍 / 👎.
| if provider == 'openrouter' and (model.startswith('gpt-') or model.startswith(('o1', 'o3', 'o4'))): | ||
| return f'openai/{model}' |
There was a problem hiding this comment.
Normalize prefixed Luna routes in the gateway executor
With OPENROUTER_API_KEY configured, generated gateway routes use openai/gpt-5.6-luna, but executor._provider_request() recognizes Luna only when the model starts with gpt-5.6, and _sanitize_openai_chat_completions_request() returns unless the provider is literally openai. As a result, the gateway strips Luna cache fields and leaves unsupported non-default temperature or tool reasoning parameters on OpenRouter requests; the desktop gateway preserves caller-supplied temperature, so affected requests can now receive provider 400s instead of the normalization applied to the previous OpenAI route. Normalize by model basename and apply the OpenAI-family guard to OpenRouter's openai/ namespace.
AGENTS.md reference: backend/AGENTS.md:L189-L189
Useful? React with 👍 / 👎.
| 'primary': ProviderRef( | ||
| provider='openrouter', | ||
| model=_provider_model_name('openrouter', model), |
There was a problem hiding this comment.
Keep the fake-provider synthetic aligned with the route
When OPENROUTER_API_KEY is present, this repoints omi:auto:chat-structured to provider openrouter, but backend/scripts/product_capability_synthetics.py::llm_gateway_fake_provider_check still registers its fake provider only under openai. Running the documented credential-free synthetic in a deployed environment that has the key therefore reaches an unsupported provider and reports FAIL, even though it is intended to exercise the in-process gateway without real provider calls. Build the fake registry from the configured lane's provider refs or override the route config for this check.
Useful? React with 👍 / 👎.
| if feature in _openrouter_fallbacks_recorded: | ||
| return | ||
| _openrouter_fallbacks_recorded.add(feature) | ||
| from utils.observability.fallback import record_fallback |
There was a problem hiding this comment.
Move the fallback import to module scope
This newly added function-local import violates the backend's explicit requirement that all imports remain at module scope, bypassing the repository's intended import-hierarchy visibility for code shared across backend services. utils.observability.fallback does not introduce a reverse dependency on model_config, so import record_fallback with the other top-level imports instead.
AGENTS.md reference: backend/AGENTS.md:L179-L187
Useful? React with 👍 / 👎.
0fcd56d to
9c1d249
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9c1d249375
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if openrouter_configured(): | ||
| return (_OPENROUTER_PREFERRED_MODELS.get(feature, _OPENROUTER_PREFERRED_MODEL), 'openrouter') |
There was a problem hiding this comment.
Preserve legacy Gemini-only BYOK routing
When OPENROUTER_API_KEY is configured, a legacy BYOK principal enrolled with only a Gemini fingerprint can still pass utils.byok._check_byok_validity, but this preference changes Gemini-backed features such as followup to OpenRouter Luna before get_llm() checks request credentials. _effective_byok_provider() consequently looks only for an OpenAI key, finds none, and silently uses Omi's OpenRouter credential instead of the user's supplied Gemini key; before this change the same request constructed the Gemini BYOK client. Resolve the BYOK profile before applying the process-wide preference, or preserve the direct vendor route whenever the request has that provider's enrolled key.
Useful? React with 👍 / 👎.
|
|
||
| # Features that can't go through get_llm() (non-ChatOpenAI providers). | ||
| _ANTHROPIC_ONLY_FEATURES = {'chat_agent'} | ||
| _ANTHROPIC_ONLY_FEATURES = set() |
There was a problem hiding this comment.
Migrate the live chat-agent tests with the route
When OPENROUTER_API_KEY is present—the documented prerequisite in test_qos_real_llm.py and test_qos_live_cp9.py—chat_agent now resolves to OpenRouter Luna and is usable through get_llm(), but the live suites still classify it as Anthropic. test_qos_real_llm.py::test_chat_agent_anthropic, test_qos_live_cp9.py::test_chat_agent, and test_qos_all_features_l1.py::test_premium_chat_agent pass the resulting gpt-5.6-luna model ID to anthropic_client, while the all-features suite also excludes it from its get_llm() cases. The documented real-provider verification therefore fails before exercising the new route; update these classifications for configured-key mode.
AGENTS.md reference: AGENTS.md:L28-L30
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
3 issues found across 10 files
Confidence score: 3/5
- In
backend/utils/llm/model_config.py, addingfollowupto_OPENROUTER_PREFERRED_FEATURESappears to reroute resolution to OpenRouter (gpt-5.6-luna) wheneverOPENROUTER_API_KEYis present, which can override expected BYOK/provider behavior and cause unexpected model behavior or spend changes — tighten feature-to-provider routing sofollowuponly shifts where explicitly intended. - In
backend/utils/llm/model_config.py, movingchat_agentinto_OPENROUTER_PREFERRED_FEATURESwhile clearing_ANTHROPIC_ONLY_FEATURESchanges a previously constrained path, sochat_agentmay now run on a different model family with output/quality differences for users — reintroduce an explicit provider guard or capability check forchat_agent. - In
backend/llm_gateway/gateway/config_loader.py, removing theprovider == 'gemini'gate applies the 128-tokensession_titlesbudget to OpenRouter routes too, which risks truncated or lower-quality titles on that path — restore provider-specific budgeting or set separate limits per provider.
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="backend/utils/llm/model_config.py">
<violation number="1" location="backend/utils/llm/model_config.py:201">
P1: Adding 'followup' to _OPENROUTER_PREFERRED_FEATURES changes its resolved provider/model to ('gpt-5.6-luna', 'openrouter') whenever OPENROUTER_API_KEY is set. Since _effective_byok_provider() maps openrouter+gpt-* models to 'openai', a user who enrolled only a Gemini BYOK key for this Gemini-backed feature will fail the openai-key lookup and silently fall back to Omi's own OpenRouter credential instead of using their Gemini key. Consider resolving the BYOK profile before applying the process-wide OpenRouter preference, or preserve the direct vendor route when the request has that provider's enrolled key.</violation>
<violation number="2" location="backend/utils/llm/model_config.py:206">
P2: 'chat_agent' has been added to _OPENROUTER_PREFERRED_FEATURES and _ANTHROPIC_ONLY_FEATURES has been cleared, so when OPENROUTER_API_KEY is configured, chat_agent now resolves through get_llm() to OpenRouter's gpt-5.6-luna instead of Anthropic. The live QoS test suites that still classify chat_agent as Anthropic-only and route its model ID to anthropic_client will fail under configured-key mode. Update those live-suite classifications to match the new OpenRouter routing for chat_agent.</violation>
</file>
<file name="backend/llm_gateway/gateway/config_loader.py">
<violation number="1" location="backend/llm_gateway/gateway/config_loader.py:318">
P2: The session_titles output budget (max_completion_tokens: 128) was previously applied only when the route resolved to Gemini; dropping the `provider == 'gemini'` condition means it now also caps the OpenRouter path. When OPENROUTER_API_KEY is configured, session_titles resolves to gpt-5.6-luna on OpenRouter (it is in _OPENROUTER_PREFERRED_FEATURES), and gpt-5.6-luna is a reasoning model. With a 128-token max_completion_tokens cap, a reasoning model can consume most of that budget on internal reasoning, likely truncating or degrading generated session titles compared with the prior Gemini-2.5-flash-lite/128 token behavior. Worth confirming the cap is still appropriate for the reasoning route, or setting the cap only for the non-OpenRouter path if title quality regresses.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| 'persona_chat_premium', | ||
| 'smart_glasses', | ||
| 'session_titles', | ||
| 'followup', |
There was a problem hiding this comment.
P1: Adding 'followup' to _OPENROUTER_PREFERRED_FEATURES changes its resolved provider/model to ('gpt-5.6-luna', 'openrouter') whenever OPENROUTER_API_KEY is set. Since _effective_byok_provider() maps openrouter+gpt-* models to 'openai', a user who enrolled only a Gemini BYOK key for this Gemini-backed feature will fail the openai-key lookup and silently fall back to Omi's own OpenRouter credential instead of using their Gemini key. Consider resolving the BYOK profile before applying the process-wide OpenRouter preference, or preserve the direct vendor route when the request has that provider's enrolled key.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/utils/llm/model_config.py, line 201:
<comment>Adding 'followup' to _OPENROUTER_PREFERRED_FEATURES changes its resolved provider/model to ('gpt-5.6-luna', 'openrouter') whenever OPENROUTER_API_KEY is set. Since _effective_byok_provider() maps openrouter+gpt-* models to 'openai', a user who enrolled only a Gemini BYOK key for this Gemini-backed feature will fail the openai-key lookup and silently fall back to Omi's own OpenRouter credential instead of using their Gemini key. Consider resolving the BYOK profile before applying the process-wide OpenRouter preference, or preserve the direct vendor route when the request has that provider's enrolled key.</comment>
<file context>
@@ -164,14 +164,92 @@ class AutoLaneRouteRef:
+ 'persona_chat_premium',
+ 'smart_glasses',
+ 'session_titles',
+ 'followup',
+ 'onboarding',
+ 'app_integration',
</file context>
| def _output_budget_for_feature(feature: str) -> dict[str, Any] | None: | ||
| """Keep pilot caps explicit and disabled until an operator enables the experiment.""" | ||
| if feature == 'session_titles' and provider == 'gemini': | ||
| if feature == 'session_titles': |
There was a problem hiding this comment.
P2: The session_titles output budget (max_completion_tokens: 128) was previously applied only when the route resolved to Gemini; dropping the provider == 'gemini' condition means it now also caps the OpenRouter path. When OPENROUTER_API_KEY is configured, session_titles resolves to gpt-5.6-luna on OpenRouter (it is in _OPENROUTER_PREFERRED_FEATURES), and gpt-5.6-luna is a reasoning model. With a 128-token max_completion_tokens cap, a reasoning model can consume most of that budget on internal reasoning, likely truncating or degrading generated session titles compared with the prior Gemini-2.5-flash-lite/128 token behavior. Worth confirming the cap is still appropriate for the reasoning route, or setting the cap only for the non-OpenRouter path if title quality regresses.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/llm_gateway/gateway/config_loader.py, line 318:
<comment>The session_titles output budget (max_completion_tokens: 128) was previously applied only when the route resolved to Gemini; dropping the `provider == 'gemini'` condition means it now also caps the OpenRouter path. When OPENROUTER_API_KEY is configured, session_titles resolves to gpt-5.6-luna on OpenRouter (it is in _OPENROUTER_PREFERRED_FEATURES), and gpt-5.6-luna is a reasoning model. With a 128-token max_completion_tokens cap, a reasoning model can consume most of that budget on internal reasoning, likely truncating or degrading generated session titles compared with the prior Gemini-2.5-flash-lite/128 token behavior. Worth confirming the cap is still appropriate for the reasoning route, or setting the cap only for the non-OpenRouter path if title quality regresses.</comment>
<file context>
@@ -280,9 +286,36 @@ def _generated_feature_route_items(
+def _output_budget_for_feature(feature: str) -> dict[str, Any] | None:
"""Keep pilot caps explicit and disabled until an operator enables the experiment."""
- if feature == 'session_titles' and provider == 'gemini':
+ if feature == 'session_titles':
return {
'experiment': 'session_titles',
</file context>
| 'app_integration', | ||
| 'trends', | ||
| 'translation', | ||
| 'chat_agent', |
There was a problem hiding this comment.
P2: 'chat_agent' has been added to _OPENROUTER_PREFERRED_FEATURES and _ANTHROPIC_ONLY_FEATURES has been cleared, so when OPENROUTER_API_KEY is configured, chat_agent now resolves through get_llm() to OpenRouter's gpt-5.6-luna instead of Anthropic. The live QoS test suites that still classify chat_agent as Anthropic-only and route its model ID to anthropic_client will fail under configured-key mode. Update those live-suite classifications to match the new OpenRouter routing for chat_agent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At backend/utils/llm/model_config.py, line 206:
<comment>'chat_agent' has been added to _OPENROUTER_PREFERRED_FEATURES and _ANTHROPIC_ONLY_FEATURES has been cleared, so when OPENROUTER_API_KEY is configured, chat_agent now resolves through get_llm() to OpenRouter's gpt-5.6-luna instead of Anthropic. The live QoS test suites that still classify chat_agent as Anthropic-only and route its model ID to anthropic_client will fail under configured-key mode. Update those live-suite classifications to match the new OpenRouter routing for chat_agent.</comment>
<file context>
@@ -164,14 +164,92 @@ class AutoLaneRouteRef:
+ 'app_integration',
+ 'trends',
+ 'translation',
+ 'chat_agent',
+ 'wrapped_analysis',
+ }
</file context>
|
Thanks for this well-structured PR — the OpenRouter-key gating is clean, the fallback telemetry is properly deduped, and the test coverage (new File-by-file observations:
Leaving for human maintainer review — the chat_agent routing strategy change (Anthropic-only gate removed, OpenRouter-preferred added) is a product direction decision that needs sign-off. by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with |
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_19628ea0-6888-4e23-bd1d-a44b5c448adf) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e54ac84b28
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| logger.debug('BYOK QoS upgrade: feature=%s %s/%s→%s/%s', feature, model, provider, byok_model, byok_prov) | ||
| model, provider = byok_model, byok_prov | ||
|
|
||
| if byok_key and gateway_feature_mode and provider not in {'openai', 'gemini', 'openrouter'}: |
There was a problem hiding this comment.
Keep OpenAI BYOK traffic on the gateway
When gateway feature mode is enabled and a user supplies an OpenAI BYOK key (for example, conv_discard), the resolved provider is openai, so this exclusion sends the request through _create_byok_client() rather than the gateway. This breaks the existing test_get_llm_feature_gateway_mode_routes_byok_through_gateway_only contract and bypasses gateway routing, accounting, and telemetry for OpenAI-backed BYOK traffic; the new executor remapping already allows an OpenAI key to serve an OpenRouter openai/ route, so this traffic should continue through get_or_create_omi_gateway_llm_for_byok().
Useful? React with 👍 / 👎.
| if feature not in _OPENROUTER_PREFERRED_FEATURES: | ||
| return direct | ||
| if openrouter_configured(): | ||
| return (_OPENROUTER_PREFERRED_MODELS.get(feature, _OPENROUTER_PREFERRED_MODEL), 'openrouter') |
There was a problem hiding this comment.
Log the effective OpenRouter routes at startup
When OPENROUTER_API_KEY is nonblank, this makes nearly every preferred feature resolve to OpenRouter, but the startup QoS loop in clients.py still iterates the static _active_profile map and logs the old OpenAI, Gemini, and Anthropic providers. Consequently the logs explicitly intended to make model-cost issues traceable report routes that the process will not use; build that startup output from _get_model_config() (as get_qos_info() already does) or otherwise log the effective preference.
Useful? React with 👍 / 👎.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_e6f04a34-e6db-4c1e-af60-80d9ad678da6) |
|
Thanks for the follow-up updates here. I re-reviewed the current head and the implementation now looks technically consistent to me; I do not see a code-level blocker. Specific observations from the current diff:
Validation I ran locally with provider/API-key env vars stripped:
I also tried the broader combined selection including Leaving this for human maintainer review because the remaining decision is product/ops direction: switching Automated maintainer review by glm-5.2 on behalf of David. by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with |
a1f714d to
41c7e19
Compare
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_f3e4be1b-67b6-4893-acc2-a7cb9761facd) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 41c7e1988b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if get_provider('chat_agent') == 'openrouter': | ||
| response = get_llm('chat_agent').invoke(SIMPLE_PROMPT) |
There was a problem hiding this comment.
Compare live assertions against effective routes
With the suite's documented required OPENROUTER_API_KEY, get_model() and get_provider() now return the OpenRouter preference, but test_active_profile_get_model (lines 103-108) and TestP13_QosInfo (lines 407-413) still compare those results with the static _active_profile; both tests therefore fail for the newly preferred features. Fresh evidence beyond the prior chat-agent comment is that these remaining whole-profile assertions were not migrated when this branch was added; derive their expectations from the effective configuration instead.
AGENTS.md reference: AGENTS.md:L28-L30
Useful? React with 👍 / 👎.
| - fair_use | ||
| gateway_capability_needed: OpenAI-compatible chat-completions lane with provider refs for openai, native Vertex Gemini, openrouter, perplexity, and anthropic. | ||
| migration_status: get_llm centrally switches Omi-managed feature traffic to generated gateway lanes with OMI_LLM_GATEWAY_FEATURE_MODE=gateway and does not retain a direct-provider fallback; gateway Gemini uses native Vertex generateContent authenticated by GKE Workload Identity; Gemini BYOK remains unsupported and fails closed. | ||
| migration_status: get_llm centrally switches Omi-managed feature traffic to generated gateway lanes with OMI_LLM_GATEWAY_FEATURE_MODE=gateway; managed text prefers OpenRouter Luna only when OPENROUTER_API_KEY is configured and otherwise retains the direct provider route; gateway Gemini uses native Vertex generateContent authenticated by GKE Workload Identity; Gemini BYOK remains unsupported and fails closed. |
There was a problem hiding this comment.
Update the public chat route inventory
When OPENROUTER_API_KEY is configured, _apply_optional_openrouter_routes() also changes omi:auto:public-shared-conversation-chat from OpenAI gpt-5-nano to OpenRouter openai/gpt-5.6-luna, but the surface inventory immediately below this new migration statement still declares current_provider_model: gateway-only openai/gpt-5-nano. This leaves the checked-in provider/model inventory wrong in the configured production mode, including its credential and cost implications; document the conditional OpenRouter route for this dedicated surface.
AGENTS.md reference: AGENTS.md:L33-L33
Useful? React with 👍 / 👎.
| gateway_config = llm_gateway_dependencies.get_gateway_config() | ||
| lane = gateway_config.lanes['omi:auto:chat-structured'] | ||
| route = gateway_config.route_artifacts[lane.active_route] |
There was a problem hiding this comment.
Restore service tokens when route discovery fails
If gateway configuration loading or the chat-structured lane/route lookup raises, these new operations fail before entering the try/finally, after both service-token environment variables have already been overwritten with the sentinel. timed_check() catches the exception and continues running the synthetic process, so later checks inherit the fake credentials instead of the caller's original environment; include route discovery and fake-registry setup inside the cleanup scope.
Useful? React with 👍 / 👎.
41c7e19 to
2c72195
Compare
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_2874f0e2-5185-4b88-9fb0-f8c163277694) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 2c721950ad
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| 'session_titles', | ||
| 'followup', | ||
| 'onboarding', | ||
| 'app_integration', | ||
| 'trends', |
There was a problem hiding this comment.
Update the Gemini live-test expectations
When the documented OPENROUTER_API_KEY is set, these features now resolve to gpt-5.6-luna/openrouter, but test_qos_real_llm.py::TestPremiumGemini still asserts that each resolves to gemini-2.5-flash-lite, so the documented real-provider suite fails before invoking the newly selected route. The related CP9 test also still skips these effective OpenRouter routes when GEMINI_API_KEY is absent even though that key is documented as optional; classify the tests from get_provider() or otherwise derive their expectations from the effective configuration.
AGENTS.md reference: AGENTS.md:L28-L30
Useful? React with 👍 / 👎.
Summary
OPENROUTER_API_KEY.Verification
make preflightscripts/pre-push origingit diff --checkNotes
The public-chat and BYOK suites pass independently. Importing them in that order in one pytest process still exposes their existing module-stub contamination, so that ordering was not used as the passing claim.
No live OpenRouter request was made because no API key is configured; key-present behavior is covered with synthetic test fixtures.
Note
Medium Risk
Touches central model resolution, gateway routing, and BYOK credential mapping across many features; misconfiguration could shift traffic or readiness, though absence of the key preserves prior direct-provider behavior.
Overview
OpenRouter is now opt-in via config. When
OPENROUTER_API_KEYis non-blank, a large set of managed text features (includingchat_agentandwrapped_analysis) resolve to OpenRouter (gpt-5.6-lunaor the existing Gemini model on OR). Without the key, those features keep their direct OpenAI/Anthropic/Gemini routes and emit one-time degraded fallback telemetry.Gateway and clients follow the same gate. Generated gateway lanes skip YAML overrides that would fight OpenRouter when the key is present; static lanes for public shared chat and chat-structured can switch primary to OpenRouter when configured. Readiness requires
OPENROUTER_API_KEYwhen any active chat route uses OpenRouter. The executor remaps OpenRouteropenai/*refs to direct OpenAI for BYOK users with an OpenAI key, applies GPT-5.6 request sanitization on those routes, and adds OpenRouter cost rate cards plusopenai/model ID prefixing in providers.chat_agentis no longer anthropic-only forget_llm()when OpenRouter is enabled; integration/QoS tests branch accordingly. Docs inventory text reflects key-gated preference instead of OpenRouter-only managed text.Reviewed by Cursor Bugbot for commit 2c72195. Configure here.
Failure-Class: none