Merge pull request #5247 — repair bare custom-provider suffix on resolver slow path (#5246, @claw-io)

Release v0.51.759 — repair bare custom-provider suffix on resolver slow path (#5246, @claw-io)
This commit is contained in:
nesquena-hermes authored and GitHub committed 2026-06-29 18:16:55 -07:00
commit edc20875fe
3 files changed
+61

No files matched your search

+2
View File
@@ -35,6 +35,8 @@
### Fixed
- **Async continuations (delegate_task / process wakeup) no longer fall back to a bare, mis-routed model.** When a server-side continuation re-entered model resolution without a requested provider, the resolver's slow path skipped the custom-provider repair, so a bare session model wasn't re-qualified to the profile's `custom:…/model` default and could be sent to the wrong endpoint. The slow path now repairs a bare model back to the profile's slash-qualified custom-provider default when the suffix matches (mirroring the fast path). Thanks @claw-io. (#5246, fixes #5225)
- **Settled transcripts with duplicate assistant turns no longer mis-attribute tool-output rendering.** When a reloaded conversation contained byte-identical assistant messages, the legacy tool-metadata fallback used `indexOf` to test anchor ownership, which always resolved to the *first* matching message — so an anchor-owned turn could be wrongly treated as a legacy fallback (or vice versa). Ownership is now gated by each message's actual raw index, and the shared check is consolidated into one helper used at both call sites. Thanks @franksong2702. (#5242)
- **The session sidebar stays fast with a very large archive, and archived search still finds everything.** Archived sidebar rows are now paged (capped per request) instead of all loaded at once, so an instance with thousands of archived sessions no longer pays to render them all. When you turn on "Show archived" and start a search, the sidebar transparently refetches without the archive cap so a title/id match beyond the first archived page is still reachable, then restores normal paging when the search clears. Default (non-search) paging keeps the existing 2000-row safety cap, and a render-generation guard prevents a slower capped/uncapped response from overwriting newer sidebar state. Thanks @santastabber. (#5200)
+18
View File
@@ -5578,6 +5578,24 @@ def _resolve_compatible_session_model_state(
_target = _profile_default or default_model
return _target, profile_provider, True
# Async server-side continuations (for example delegate_task completion
# re-entry) can arrive here with profile context but without a usable
# requested_provider, bypassing the fast-path custom-provider repair
# above. If the profile's configured custom-provider default is a
# slash-qualified model whose suffix matches the bare session model,
# repair back to the profile default before the provider call (#5225).
if (
"/" not in model
and _profile_default
and "/" in _profile_default
and _profile_default.rsplit("/", 1)[-1] == model
and (
_profile_provider_normalized == "custom"
or str(profile_provider).startswith("custom:")
)
):
return _profile_default, profile_provider, True
return model, profile_provider, False
if not model:
@@ -82,6 +82,47 @@ class TestIssue5127CustomProviderBareSuffixRepair:
assert mock_catalog.call_count == 0
assert result == ("x-ai/grok-composer-2.5-fast", "custom:my-proxy", False)
def test_slow_path_repairs_bare_suffix_to_profile_default_for_custom_provider(self):
"""Regression #5225: async continuations may arrive without usable model_provider."""
from api.routes import _resolve_compatible_session_model_state
with patch("api.routes.get_available_models") as mock_catalog:
mock_catalog.return_value = {
"active_provider": "openrouter",
"default_model": "openai/gpt-5.5",
"groups": [],
}
result = _resolve_compatible_session_model_state(
"grok-composer-2.5-fast",
None,
profile_provider="custom:my-proxy",
profile_default_model="x-ai/grok-composer-2.5-fast",
prefer_cached_catalog=True,
)
assert mock_catalog.call_count == 1
assert result == ("x-ai/grok-composer-2.5-fast", "custom:my-proxy", True)
def test_slow_path_does_not_rewrite_unrelated_bare_model_for_custom_provider(self):
from api.routes import _resolve_compatible_session_model_state
with patch("api.routes.get_available_models") as mock_catalog:
mock_catalog.return_value = {
"active_provider": "openrouter",
"default_model": "openai/gpt-5.5",
"groups": [],
}
result = _resolve_compatible_session_model_state(
"other-model",
None,
profile_provider="custom:my-proxy",
profile_default_model="x-ai/grok-composer-2.5-fast",
prefer_cached_catalog=True,
)
assert mock_catalog.call_count == 1
assert result == ("other-model", "custom:my-proxy", False)
def test_openai_codex_stale_openai_slash_still_uses_slow_path(self):
"""Regression: openai/... under openai-codex must not take bare fast return."""
from api.routes import _resolve_compatible_session_model_state