@rodboev. providers.<name>.models.<model>.context_length overrides (standard provider, no base_url) were invisible to the session context resolver → wrong window shown/persisted, could trip auto-compression at the wrong threshold. New _context_length_lookup_inputs_for_model helper resolves provider config / base_url / custom_providers across route-load, session-save, and SSE-usage paths; provider-scoped overrides match by provider name and forward as config_context_length (returned before any base-url-gated probe). Maintainer pre-merge items both already satisfied in PR head: no-base_url regression test (test_route_resolver_uses_provider_model_context_length_without_base_url) present; session-save _cfg_base_url assigned before the helper call (safe-bound, no NameError on TypeError fallback). Verified api code byte-identical to PR head; 14 context-length tests pass. + CHANGELOG v0.51.300. Co-authored-by: nesquena-hermes <[email protected]>
199 lines
5.9 KiB
Python
199 lines
5.9 KiB
Python
"""Regression coverage for #3717 provider-scoped context-length overrides."""
|
|
|
|
import sys
|
|
import types
|
|
from pathlib import Path
|
|
|
|
|
|
REPO = Path(__file__).resolve().parent.parent
|
|
STREAMING_PY = (REPO / "api" / "streaming.py").read_text(encoding="utf-8")
|
|
ROUTES_PY = (REPO / "api" / "routes.py").read_text(encoding="utf-8")
|
|
|
|
|
|
def _install_fake_context_resolver(monkeypatch):
|
|
calls = []
|
|
mod = types.ModuleType("agent.model_metadata")
|
|
|
|
def _fake(
|
|
model,
|
|
base_url="",
|
|
*args,
|
|
config_context_length=None,
|
|
provider="",
|
|
custom_providers=None,
|
|
**kwargs,
|
|
):
|
|
calls.append(
|
|
{
|
|
"model": model,
|
|
"base_url": base_url,
|
|
"config_context_length": config_context_length,
|
|
"provider": provider,
|
|
"custom_providers": custom_providers,
|
|
}
|
|
)
|
|
return config_context_length or 256000
|
|
|
|
mod.get_model_context_length = _fake
|
|
if "agent" not in sys.modules:
|
|
agent_pkg = types.ModuleType("agent")
|
|
agent_pkg.__path__ = []
|
|
monkeypatch.setitem(sys.modules, "agent", agent_pkg)
|
|
monkeypatch.setitem(sys.modules, "agent.model_metadata", mod)
|
|
return calls
|
|
|
|
|
|
def test_route_resolver_uses_provider_model_context_length(monkeypatch):
|
|
import api.config as config
|
|
import api.routes as routes
|
|
|
|
calls = _install_fake_context_resolver(monkeypatch)
|
|
monkeypatch.setattr(
|
|
config,
|
|
"get_config",
|
|
lambda *a, **k: {
|
|
"model": {
|
|
"default": "default-model",
|
|
"context_length": 123456,
|
|
},
|
|
"providers": {
|
|
"openrouter": {
|
|
"base_url": "https://openrouter.example/v1",
|
|
"models": {
|
|
"provider-model": {"context_length": 777000},
|
|
},
|
|
},
|
|
},
|
|
"custom_providers": [],
|
|
},
|
|
)
|
|
|
|
result = routes._resolve_context_length_for_session_model(
|
|
"provider-model",
|
|
"openrouter",
|
|
)
|
|
|
|
assert result == 777000
|
|
assert calls[-1]["config_context_length"] == 777000
|
|
assert calls[-1]["base_url"] == "https://openrouter.example/v1"
|
|
assert calls[-1]["provider"] == "openrouter"
|
|
|
|
|
|
def test_route_resolver_uses_provider_model_context_length_without_base_url(monkeypatch):
|
|
import api.config as config
|
|
import api.routes as routes
|
|
|
|
calls = _install_fake_context_resolver(monkeypatch)
|
|
monkeypatch.setattr(
|
|
config,
|
|
"get_config",
|
|
lambda *a, **k: {
|
|
"model": {
|
|
"default": "default-model",
|
|
"context_length": 123456,
|
|
},
|
|
"providers": {
|
|
"anthropic": {
|
|
"models": {
|
|
"provider-model": {"context_length": 777000},
|
|
},
|
|
},
|
|
},
|
|
"custom_providers": [],
|
|
},
|
|
)
|
|
|
|
result = routes._resolve_context_length_for_session_model(
|
|
"provider-model",
|
|
"anthropic",
|
|
)
|
|
|
|
assert result == 777000
|
|
assert calls[-1]["config_context_length"] == 777000
|
|
assert calls[-1]["base_url"] == ""
|
|
assert calls[-1]["provider"] == "anthropic"
|
|
|
|
|
|
def test_route_resolver_uses_named_custom_provider_base_url(monkeypatch):
|
|
import api.config as config
|
|
import api.routes as routes
|
|
|
|
custom_providers = [
|
|
{
|
|
"name": "ZenMux",
|
|
"base_url": "https://zenmux.example/v1",
|
|
"models": {
|
|
"custom-model": {"context_length": "888000"},
|
|
},
|
|
}
|
|
]
|
|
calls = _install_fake_context_resolver(monkeypatch)
|
|
monkeypatch.setattr(
|
|
config,
|
|
"get_config",
|
|
lambda *a, **k: {
|
|
"model": {
|
|
"default": "default-model",
|
|
"context_length": 123456,
|
|
},
|
|
"custom_providers": custom_providers,
|
|
},
|
|
)
|
|
|
|
result = routes._resolve_context_length_for_session_model(
|
|
"custom-model",
|
|
"custom:zenmux",
|
|
)
|
|
|
|
assert result == 888000
|
|
assert calls[-1]["config_context_length"] == 888000
|
|
assert calls[-1]["base_url"] == "https://zenmux.example/v1"
|
|
assert calls[-1]["provider"] == "custom:zenmux"
|
|
assert calls[-1]["custom_providers"] is custom_providers
|
|
|
|
|
|
def test_global_context_length_remains_default_model_only(monkeypatch):
|
|
import api.config as config
|
|
import api.routes as routes
|
|
|
|
calls = _install_fake_context_resolver(monkeypatch)
|
|
monkeypatch.setattr(
|
|
config,
|
|
"get_config",
|
|
lambda *a, **k: {
|
|
"model": {
|
|
"default": "default-model",
|
|
"context_length": 123456,
|
|
},
|
|
"providers": {
|
|
"openai": {
|
|
"base_url": "https://openai.example/v1",
|
|
"models": {},
|
|
},
|
|
},
|
|
},
|
|
)
|
|
|
|
result = routes._resolve_context_length_for_session_model("other-model", "openai")
|
|
|
|
assert result == 256000
|
|
assert calls[-1]["config_context_length"] is None
|
|
assert calls[-1]["base_url"] == "https://openai.example/v1"
|
|
|
|
|
|
def test_streaming_fallbacks_use_shared_provider_context_helper():
|
|
assert STREAMING_PY.count("_context_length_lookup_inputs_for_model(") >= 2
|
|
assert "_cfg_base_url = getattr(agent, 'base_url', '') or resolved_base_url or ''" in STREAMING_PY
|
|
assert "base_url=_cfg_base_url" in STREAMING_PY
|
|
assert "config_context_length=_cfg_ctx_len" in STREAMING_PY
|
|
assert "provider=_cfg_provider" in STREAMING_PY
|
|
assert "custom_providers=_cfg_custom_providers" in STREAMING_PY
|
|
assert "_cfg_base_url" in STREAMING_PY
|
|
|
|
|
|
def test_route_helper_keeps_all_context_length_sources_aligned():
|
|
assert "def _context_length_lookup_inputs_for_model" in ROUTES_PY
|
|
assert 'cfg.get("providers", {})' in ROUTES_PY
|
|
assert 'cfg.get("custom_providers")' in ROUTES_PY
|
|
assert "_model_matches_configured_default" in ROUTES_PY
|