Skip to content

fix(acp): skip detect_provider_for_model when input had explicit provider prefix - #59197

Closed
wesleysimplicio wants to merge 3 commits into
NousResearch:mainfrom
wesleysimplicio:simplicio/fix-59089-acp-model-routing
Closed

fix(acp): skip detect_provider_for_model when input had explicit provider prefix#59197
wesleysimplicio wants to merge 3 commits into
NousResearch:mainfrom
wesleysimplicio:simplicio/fix-59089-acp-model-routing

Conversation

@wesleysimplicio

Copy link
Copy Markdown
Contributor

Summary

_resolve_model_selection re-runs detect_provider_for_model() when the resolved provider equals the current provider, even when parse_model_input already resolved an explicit provider:model prefix. For models that newly appear in OpenRouter's catalog (or any model known to multiple providers), this overwrites the correct provider with a detected one, causing "No LLM provider configured" errors.

Root cause

In acp_adapter/server.py, _resolve_model_selection:

  1. Calls parse_model_input(raw_model, current_provider) which correctly resolves openrouter:anthropic/claude-sonnet-4.5("openrouter", "anthropic/claude-sonnet-4.5").
  2. Then checks if target_provider == current_provider. If the current session provider happens to also be openrouter, it enters the branch.
  3. Calls detect_provider_for_model(new_model, current_provider), which detects anthropic/claude-sonnet-4.5 as belonging to the anthropic provider and overwrites target_provider from openrouteranthropic.

The fix skips the detect_provider_for_model fallback when raw_model contained an explicit : (provider prefix), since parse_model_input has already handled that case correctly.

Changes

  • acp_adapter/server.py_resolve_model_selection: add _had_explicit_provider guard

Testing

The fix is minimal and targeted: it only changes the condition under which detect_provider_for_model is called, and only for inputs where the user explicitly specified a provider prefix.

Closes #59089

WhatsApp Web.js uses LID format (digits@lid) for certain contact IDs,
but _parse_target_ref() only recognizes E.164 phone number format.
Add a regex and branch for WhatsApp LID IDs so send_message works
without requiring a home_channel.

Closes NousResearch#59136
…ider prefix

_resolve_model_selection re-runs detect_provider_for_model() when the
resolved provider equals the current provider, even when parse_model_input
already resolved an explicit provider:model prefix.  For models that newly
appear in OpenRouter's catalog, this overwrites the correct provider with
openrouter, causing 'No LLM provider configured' errors.

Track whether raw_model had an explicit provider prefix and only run the
fallback when it didn't.

Closes NousResearch#59089
@alt-glitch alt-glitch added type/bug Something isn't working comp/acp Agent Communication Protocol adapter provider/anthropic Anthropic native Messages API provider/openrouter OpenRouter aggregator P2 Medium — degraded but workaround exists duplicate This issue or pull request already exists labels Jul 5, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Duplicate of #59092 — both fix #59089 by skipping detect_provider_for_model when the input carried an explicit provider: prefix in _resolve_model_selection. #59092 was opened first and is canonical. Note: this PR's branch also bundles an unrelated tools/send_message_tool.py change (the WhatsApp JID/LID work from the #59166 cluster) — likely stacked-branch contamination.

@AmirF194 AmirF194 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Thanks for the clear writeup, the root cause here is spot on and the failing case is real. I ran it both ways and confirmed the explicit anthropic:claude-sonnet-5 path returns openrouter before your change and anthropic after, so the fix does what it says for the reported input.

My concern is that ":" in raw_model is a looser test than the one parse_model_input actually uses to decide a prefix is a provider. It only treats a colon as a provider delimiter when the left side is a known provider name. So a bare model name that carries a colon variant tag (the OpenRouter :free / :beta / :thinking suffixes) now counts as "had explicit provider" and skips detection, even though the user never named a provider. Concretely, _resolve_model_selection("poolside/laguna-m.1:free", "anthropic") routes to openrouter on main but stays on anthropic with this change, and that id is literally in the catalog (model_ids() ships three :free ids right now). That is the same "No LLM provider configured" failure you are fixing, just reached from the other direction, and it is hittable from the /model text path.

Your own issue points at the clean version of the guard: parse_model_input already knows whether it matched a real provider prefix (colon > 0 and provider_part in _KNOWN_PROVIDER_NAMES). Reusing that (return a flag, or recompute the same condition) skips detection only when there was a genuine explicit provider and avoids the variant-tag regression.

Two smaller things. Could you add a regression test in tests/acp/test_server.py? The existing test_set_session_model_accepts_provider_prefixed_choice stubs detect_provider_for_model to None, so it passes on the buggy code too and would not have caught this (I ran that file in a clean 3.11 container, 80 passed, and confirmed neither the fix nor the new regression is exercised). A case where detection returns a different provider and the explicit prefix wins, plus one for a bare :free slug that should still route, would lock both directions down. And the WhatsApp LID commit (tools/send_message_tool.py) looks unrelated to ACP routing and is not in the PR description, so I would pull it into its own PR to keep this one reviewable, especially since \d+@lid already matches the existing _WHATSAPP_JID_RE.

@wesleysimplicio

Copy link
Copy Markdown
Contributor Author

Revisei o feedback e ajustei a branch.\n\nMudanças aplicadas:\n- troquei o guard para só pular quando houver prefixo de provider realmente reconhecido (mesma condição semântica do parser, evitando regressão com slugs /)\n- removi da PR a alteração não relacionada de WhatsApp LID\n- adicionei regressões para os dois sentidos: prefixo explícito vence detecção divergente, e slug bare com continua roteando via detecção\n\nValidação:\n- ........................................................................ [ 90%]
........ [100%]
80 passed in 5.27s\n- 81 passed

@wesleysimplicio

Copy link
Copy Markdown
Contributor Author

Revisei o feedback e ajustei a branch.

Mudanças aplicadas:

  • troquei o guard para só pular detect_provider_for_model() quando houver prefixo de provider realmente reconhecido (mesma condição semântica do parser, evitando regressão com slugs :free/:beta)
  • removi da PR a alteração não relacionada de WhatsApp LID
  • adicionei regressões para os dois sentidos: prefixo explícito vence detecção divergente, e slug bare com :free continua roteando via detecção

Validação:

  • uv run --extra dev --extra acp pytest tests/acp/test_server.py -q
  • 81 passed

@wesleysimplicio

Copy link
Copy Markdown
Contributor Author

Reopening — this was closed in error by the automated stale-PR check, which only inspected GitHub's reviewDecision field (APPROVED/CHANGES_REQUESTED) and missed this PR's existing COMMENTED maintainer review confirming the fix is correct. Apologies for the noise.

@wesleysimplicio

Copy link
Copy Markdown
Contributor Author

Re-closing: this was mistakenly reopened citing the stale-close automation bug (that bug affects #59194/#59189, which is a separate issue), but this PR's actual close reason was different and still valid — it is a duplicate of #59092 (opened earlier, same fix for #59089). Leaving one open note for whoever picks up #59092: this branch's follow-up commit narrowed the guard to only skip detect_provider_for_model when the prefix matches a known provider name, avoiding a false-positive on OpenRouter :free/:beta/:thinking slug suffixes (per @AmirF194's review here) — worth carrying that refinement into #59092 if it doesn't already handle it.

@teknium1 teknium1 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Thanks for the focused ACP regression coverage. Current main still parses the requested model and then reruns provider detection when the parsed provider equals the current provider (acp_adapter/server.py:654-658); the proposed recognized-prefix guard follows the parser's own delimiter rule (hermes_cli/models.py:1801-1816) and protects the shared resolver used by both ACP model-switch paths.

Problems

  • The diff still includes unrelated WhatsApp code. _WHATSAPP_LID_RE is added in tools/send_message_tool.py:51, but parsing consults _WHATSAPP_JID_RE at tools/send_message_tool.py:594; existing coverage already verifies digits@lid through that regex (tests/tools/test_send_message_target_parse.py:42-46).

Suggested changes

  • Remove the tools/send_message_tool.py hunk so this ACP fix remains scoped and independently salvageable.

This is an automated hermes-sweeper review.

r"^\s*[-\w]+@(?:g\.us|s\.whatsapp\.net|lid|broadcast|newsletter)\s*$",
re.IGNORECASE,
)
_WHATSAPP_LID_RE = re.compile(r"^\s*(\d+@lid)\s*$")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This declaration is unused: _parse_target_ref() only checks _WHATSAPP_JID_RE, which already accepts digits@lid. Please drop this unrelated WhatsApp residue from the ACP-routing PR.

@wesleysimplicio

Copy link
Copy Markdown
Contributor Author

Re-closing (again) — this PR is a duplicate of #59092 (opened earlier for the same #59089 root cause). This was already confirmed in my 2026-07-15T06:34Z close comment above; something reopened it again without any new information. #59092 remains open and is the canonical fix to review/merge; teknium1's 23:32 sweeper review here also asked to drop the unrelated WhatsApp LID hunk, which is moot once this closes in favor of #59092. Leaving closed.

@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/acp Agent Communication Protocol adapter duplicate This issue or pull request already exists P2 Medium — degraded but workaround exists provider/anthropic Anthropic native Messages API provider/openrouter OpenRouter aggregator sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: ACP session/set_model reroutes explicit anthropic:claude-sonnet-5 to OpenRouter ("No LLM provider configured")

4 participants