fix(context-length): restore base_url for two guarded configs (#7743)
The #7535 ownership guard skipped the global model.base_url backfill for two shapes that master handled: - A `model` section with `base_url` but no `provider` declares no owner, so `_providers_match_for_context("", <session provider>)` was false and the endpoint was dropped, silently falling back to 256,000 instead of querying the configured /v1/models (the profile-setup path writes this shape). - `opencode_go` and `opencode-go` are two spellings of the same built-in provider, but `_canonical_context_provider` does not fold underscores, so the owner session lost its URL. Allow the backfill when no owner is declared, and compare both sides through api.config._canonicalise_provider_id, which folds `_` to `-` while keeping distinct `custom:*` slugs distinct. The #7535 case (a custom:ark owner vs an opencode-go session) still resolves to an empty base_url.
This commit is contained in:
1 parent
a44e329997
commit
301ed54ab7
2 files changed
+109
-2
No files matched your search
+20
-2
@@ -7189,9 +7189,27 @@ def _context_length_lookup_inputs_for_model(
|
|||||||
# _context_length_config_api_key_for_provider). A built-in registry
|
# _context_length_config_api_key_for_provider). A built-in registry
|
||||||
# provider (empty base_url by design) must keep the slot empty so
|
# provider (empty base_url by design) must keep the slot empty so
|
||||||
# the registry endpoint resolves instead of another provider's URL.
|
# the registry endpoint resolves instead of another provider's URL.
|
||||||
|
#
|
||||||
|
# Two shapes cannot own the slot and therefore cannot conflict, so
|
||||||
|
# they keep master's backfill: a config that declares no provider
|
||||||
|
# at all (the profile-setup path writes model.base_url without one)
|
||||||
|
# and the two spellings of the same built-in id (opencode_go ==
|
||||||
|
# opencode-go), folded through api.config._canonicalise_provider_id
|
||||||
|
# so distinct custom:* slugs stay distinct.
|
||||||
_model_cfg_provider = _canonical_context_provider(model_cfg.get("provider"))
|
_model_cfg_provider = _canonical_context_provider(model_cfg.get("provider"))
|
||||||
if not effective_provider or _providers_match_for_context(
|
_owner_provider = _model_cfg_provider
|
||||||
_model_cfg_provider, effective_provider
|
_session_provider = effective_provider
|
||||||
|
try:
|
||||||
|
from api.config import _canonicalise_provider_id as _canon_provider_id
|
||||||
|
|
||||||
|
_owner_provider = _canon_provider_id(_owner_provider) or _owner_provider
|
||||||
|
_session_provider = _canon_provider_id(_session_provider) or _session_provider
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
if (
|
||||||
|
not effective_provider
|
||||||
|
or not _model_cfg_provider
|
||||||
|
or _providers_match_for_context(_owner_provider, _session_provider)
|
||||||
):
|
):
|
||||||
effective_base_url = str(model_cfg.get("base_url") or "").strip()
|
effective_base_url = str(model_cfg.get("base_url") or "").strip()
|
||||||
|
|
||||||
|
|||||||
@@ -704,3 +704,92 @@ def test_same_provider_session_keeps_global_base_url_backfill():
|
|||||||
assert lookup.base_url == ark_base, (
|
assert lookup.base_url == ark_base, (
|
||||||
"same-owner sessions must keep the model.base_url backfill (#7535 compat)"
|
"same-owner sessions must keep the model.base_url backfill (#7535 compat)"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_base_url_only_config_without_provider_keeps_endpoint():
|
||||||
|
"""Regression (PR #7743 gate): a ``model`` section that declares
|
||||||
|
``base_url`` but NO ``provider`` has no owner that could conflict, so any
|
||||||
|
session must still receive the URL — exactly as master did. The profile
|
||||||
|
setup path writes this shape (api/profiles.py), and dropping the URL made
|
||||||
|
the lookup skip the endpoint and silently fall back to 256,000."""
|
||||||
|
from api.routes import _context_length_lookup_inputs_for_model
|
||||||
|
|
||||||
|
only_base_url = "http://127.0.0.1:9/v1"
|
||||||
|
cfg = {
|
||||||
|
"model": {
|
||||||
|
"base_url": only_base_url,
|
||||||
|
"default": "some-model",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for session_provider in ("custom", "openrouter"):
|
||||||
|
lookup = _context_length_lookup_inputs_for_model(
|
||||||
|
"some-model",
|
||||||
|
session_provider,
|
||||||
|
cfg=cfg,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert lookup.provider == session_provider
|
||||||
|
assert lookup.base_url == only_base_url, (
|
||||||
|
f"a config with no declared provider owner cannot conflict with the "
|
||||||
|
f"{session_provider!r} session, so the base_url must survive the "
|
||||||
|
f"ownership guard"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_underscore_provider_owner_matches_hyphen_session():
|
||||||
|
"""Regression (PR #7743 gate): ``opencode_go`` and ``opencode-go`` are two
|
||||||
|
spellings of the SAME built-in provider, so the owner session must still
|
||||||
|
receive ``model.base_url``. Comparing the ids through the repo's
|
||||||
|
canonicaliser folds the underscore without collapsing distinct
|
||||||
|
``custom:*`` slugs."""
|
||||||
|
from api.routes import _context_length_lookup_inputs_for_model
|
||||||
|
|
||||||
|
owner_base = "http://127.0.0.1:9/v1"
|
||||||
|
cfg = {
|
||||||
|
"model": {
|
||||||
|
"provider": "opencode_go",
|
||||||
|
"base_url": owner_base,
|
||||||
|
"default": "some-model",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
lookup = _context_length_lookup_inputs_for_model(
|
||||||
|
"some-model",
|
||||||
|
"opencode-go",
|
||||||
|
cfg=cfg,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert lookup.provider == "opencode-go"
|
||||||
|
assert lookup.base_url == owner_base, (
|
||||||
|
"opencode_go and opencode-go are the same built-in provider, so the "
|
||||||
|
"owner session must keep the base_url backfill"
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_underscore_provider_still_blocks_distinct_custom_owner():
|
||||||
|
"""Counterpart to test_underscore_provider_owner_matches_hyphen_session:
|
||||||
|
folding underscores must not open the #7535 guard. ``custom:my_box`` and a
|
||||||
|
session on the unrelated built-in ``opencode-go`` are still different
|
||||||
|
providers, so the custom owner's URL stays out of the registry slot."""
|
||||||
|
from api.routes import _context_length_lookup_inputs_for_model
|
||||||
|
|
||||||
|
cfg = {
|
||||||
|
"model": {
|
||||||
|
"provider": "custom:my_box",
|
||||||
|
"base_url": "http://127.0.0.1:9/v1",
|
||||||
|
"default": "some-model",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
lookup = _context_length_lookup_inputs_for_model(
|
||||||
|
"some-model",
|
||||||
|
"opencode-go",
|
||||||
|
cfg=cfg,
|
||||||
|
)
|
||||||
|
|
||||||
|
assert lookup.provider == "opencode-go"
|
||||||
|
assert lookup.base_url == "", (
|
||||||
|
"underscore folding must not let a distinct custom provider's "
|
||||||
|
"base_url leak onto another provider's session (#7535 guard)"
|
||||||
|
)
|
||||||
Reference in new issue
Block a user