fix(model-context): read real context window for unknown proxy models (#4909)

api/proxy endpoints (OpenRouter, other OpenAI-compatible aggregators)
short-circuit _query_context_length: they only consult the static
KNOWN_CONTEXT_WINDOWS table and otherwise return DEFAULT_CONTEXT (128000).
Any model not in that table — e.g. a freshly listed OpenRouter model like
Owl-alpha — was therefore capped at 128k even though the endpoint's catalog
reports its true window (1048576), so the rest of the model context never
got used.

The short-circuit exists so a context lookup doesn't download a large proxy
catalog on every call. Keep that property for the common case: known models
still resolve from the table with no network. For a model missing from the
table, read the window from the endpoint's /models catalog and cache the
whole id->context map per endpoint, so the catalog is fetched at most once
per endpoint (not once per model) and only for models that were broken
anyway. On any fetch/parse failure or a model absent from the catalog, fall
back to DEFAULT_CONTEXT exactly as before.

Factor the per-entry field extraction the non-proxy path already used into
_model_ctx_from_entry so both paths share it.

Fixes #4886
This commit is contained in:
Ashvin
2026-06-30 22:34:29 +05:30
committed by GitHub
parent 005ff73142
commit 9a80ab24af
2 changed files with 150 additions and 30 deletions
+86 -21
View File
@@ -316,6 +316,83 @@ def _lookup_known(model: str) -> Optional[int]:
return best_ctx
def _model_ctx_from_entry(m: dict) -> Optional[int]:
"""Extract a positive context window from one /models catalog entry.
Checks the common top-level fields first, then a nested meta/model_extra
object. Returns None when no positive window is reported.
"""
if not isinstance(m, dict):
return None
for field in (
"context_length",
"context_window",
"max_model_len",
"max_context_length",
"max_seq_len",
):
val = m.get(field)
if val and isinstance(val, (int, float)) and val > 0:
return int(val)
meta = m.get("meta") or m.get("model_extra") or {}
if isinstance(meta, dict):
# n_ctx is the actual serving context (set via -c flag in llama.cpp)
for field in ("n_ctx", "context_length", "context_window", "max_model_len"):
val = meta.get(field)
if val and isinstance(val, (int, float)) and val > 0:
return int(val)
return None
# Per-endpoint cache of the {model_id: context_length} map parsed from a
# proxy/api catalog. api/proxy endpoints skip the /models download on every
# lookup because a large catalog is expensive; caching the whole map lets us
# pay that download at most once per endpoint instead of once per model.
_catalog_ctx_cache: Dict[str, Dict[str, int]] = {}
def _proxy_catalog_context(endpoint_url: str, model: str) -> Optional[int]:
"""Context window for a model read from the endpoint's /models catalog.
Fetches the catalog once per endpoint and caches the full id->context map,
so an api/proxy endpoint serving a model that isn't in KNOWN_CONTEXT_WINDOWS
(e.g. a new OpenRouter model) still reports its real window instead of the
bare default. Returns None when the catalog can't be read or doesn't list a
positive window for the model.
"""
cat = _catalog_ctx_cache.get(endpoint_url)
if cat is None:
from src.endpoint_resolver import build_models_url
try:
r = httpx.get(build_models_url(endpoint_url), timeout=REQUEST_TIMEOUT)
except Exception as e:
logger.debug(f"Failed to fetch proxy catalog for context length: {e}")
return None
if not r.is_success:
return None
cat = {}
try:
for m in (r.json().get("data") or []):
mid = m.get("id") if isinstance(m, dict) else None
ctx = _model_ctx_from_entry(m) if mid else None
if mid and ctx:
cat[mid] = ctx
except Exception as e:
logger.debug(f"Failed to parse proxy catalog for context length: {e}")
return None
_catalog_ctx_cache[endpoint_url] = cat
if model in cat:
return cat[model]
# Catalog ids may carry a provider prefix (e.g. "openai/gpt-4o") while the
# session stores the bare id; match on the trailing segment as a fallback.
base = model.split("/")[-1]
for mid, ctx in cat.items():
if mid.split("/")[-1] == base:
return ctx
return None
def _query_context_length(endpoint_url: str, model: str) -> Tuple[int, bool]:
"""Query the model API for context length. Returns (context_length, known) where
``known`` is False only for the bare DEFAULT_CONTEXT fallback."""
@@ -330,6 +407,14 @@ def _query_context_length(endpoint_url: str, model: str) -> Tuple[int, bool]:
if known:
logger.info(f"Using known context window for {model}: {known}")
return known, True
# Not in the known table: read the real window from the catalog (cached
# once per endpoint) instead of capping every unknown model at the
# default — that under-reported large windows on aggregators like
# OpenRouter (issue #4886).
api_ctx = _proxy_catalog_context(endpoint_url, model)
if api_ctx:
logger.info(f"Proxy catalog reports context window for {model}: {api_ctx}")
return api_ctx, True
return DEFAULT_CONTEXT, False
# Try llama.cpp /slots endpoint first — reports actual serving context
@@ -370,27 +455,7 @@ def _query_context_length(endpoint_url: str, model: str) -> Tuple[int, bool]:
for m in models_list:
mid = m.get("id", "")
if mid == model or mid.split("/")[-1] == model.split("/")[-1]:
for field in (
"context_length",
"context_window",
"max_model_len",
"max_context_length",
"max_seq_len",
):
val = m.get(field)
if val and isinstance(val, (int, float)) and val > 0:
api_ctx = int(val)
break
if not api_ctx:
meta = m.get("meta") or m.get("model_extra") or {}
if isinstance(meta, dict):
# n_ctx is the actual serving context (set via -c flag in llama.cpp)
for field in ("n_ctx", "context_length", "context_window", "max_model_len"):
val = meta.get(field)
if val and isinstance(val, (int, float)) and val > 0:
api_ctx = int(val)
break
api_ctx = _model_ctx_from_entry(m)
break
except Exception as e:
logger.debug(f"Failed to query context length for {model}: {e}")