diff --git a/raven/providers/common_models.py b/raven/providers/common_models.py index d5da8c6..77e1333 100644 --- a/raven/providers/common_models.py +++ b/raven/providers/common_models.py @@ -82,6 +82,10 @@ "deepseek/deepseek-v4-flash", "deepseek/deepseek-v4-pro", ], + "minimax": [ + "minimax/MiniMax-M3", + "minimax/MiniMax-M2.7", + ], "minimax_global": [ "minimax-global/MiniMax-M3", "minimax-global/MiniMax-M2.7", diff --git a/raven/providers/rates.py b/raven/providers/rates.py index e00365c..01de730 100644 --- a/raven/providers/rates.py +++ b/raven/providers/rates.py @@ -527,11 +527,29 @@ def _try_litellm_context_window(model: str, *, allow_import: bool = True) -> int return None +def _registry_context_window(model: str) -> int | None: + """A current vendor window declared by the provider registry, or None.""" + from raven.providers.registry import find_by_model, find_by_name, split_model_id + + prefix, vendor_model = split_model_id(model) + if not prefix: + return None + spec = find_by_model(model) + if spec is None: + return None + source = find_by_name(spec.metadata_prefix) if spec.metadata_prefix else spec + if source is None: + return None + wanted = vendor_model.casefold() + return next((window for model_id, window in source.model_context_windows if model_id.casefold() == wanted), None) + + def resolve_context_window(model: str, *, allow_fetch: bool = True) -> int | None: """Return a model's real context window in tokens, or None. - LiteLLM's static metadata first, then OpenRouter's catalogue for ids that - name OpenRouter. The snapshot is deliberately not a source: a window sizes + Current model facts declared by the routing registry come first, then + LiteLLM's static metadata, then OpenRouter's catalogue for ids that name + OpenRouter. The snapshot is deliberately not a source: a window sizes trimming, so a community-maintained file that goes stale or wrong would shape the next request rather than cost a label. Unknown models return None so the caller keeps its configured default. @@ -542,6 +560,10 @@ def resolve_context_window(model: str, *, allow_fetch: bool = True) -> int | Non behalf (see ``_try_litellm_context_window``) -- a caller cheap enough to pass this is cheap enough not to pay a fresh import either. """ + window = _registry_context_window(model) + if window: + return window + window = _try_litellm_context_window(model, allow_import=allow_fetch) if window: return window diff --git a/raven/providers/registry.py b/raven/providers/registry.py index 69d9c86..d6529c7 100644 --- a/raven/providers/registry.py +++ b/raven/providers/registry.py @@ -80,6 +80,10 @@ class ProviderSpec: # per-model param overrides, e.g. (("kimi-k2.5", {"temperature": 1.0}),) model_overrides: tuple[tuple[str, dict[str, Any]], ...] = () + # Vendor model id to context window for current models the pinned LiteLLM + # metadata does not yet describe correctly. + model_context_windows: tuple[tuple[str, int], ...] = () + # OAuth-based providers (e.g., OpenAI Codex) don't use API keys is_oauth: bool = False # if True, uses OAuth flow instead of API key @@ -484,9 +488,11 @@ def claims(self, model: str) -> bool: detect_by_base_keyword="", strip_model_prefix=False, model_overrides=(), + model_context_windows=(("MiniMax-M3", 1_000_000), ("MiniMax-M2.7", 204_800)), # Needed by `provider test` and the wizard preflight, which probe # /v1/models before any LiteLLM call resolves an endpoint. default_api_base="https://api.minimax.io/v1", + default_model="minimax/MiniMax-M3", ), ProviderSpec( name="minimax_global", diff --git a/tests/test_provider_catalog.py b/tests/test_provider_catalog.py index c27e5e1..c9d2cee 100644 --- a/tests/test_provider_catalog.py +++ b/tests/test_provider_catalog.py @@ -119,6 +119,7 @@ def test_registry_and_schema_declare_the_same_providers() -> None: "zai", "dashscope", "groq", + "minimax", "minimax_global", "minimax_cn", ] diff --git a/tests/test_provider_rates.py b/tests/test_provider_rates.py index a88e558..65d061d 100644 --- a/tests/test_provider_rates.py +++ b/tests/test_provider_rates.py @@ -263,6 +263,19 @@ def test_a_litellm_mapped_window_comes_from_litellm_with_no_network(monkeypatch) assert counter["calls"] == 0 +@pytest.mark.parametrize( + ("model", "expected"), + [ + ("minimax/MiniMax-M3", 1_000_000), + ("minimax/MiniMax-M2.7", 204_800), + ("minimax-global/MiniMax-M3", 1_000_000), + ("minimax-cn/MiniMax-M2.7", 204_800), + ], +) +def test_minimax_current_windows_come_from_the_provider_registry(model: str, expected: int) -> None: + assert resolve_context_window(model, allow_fetch=False) == expected + + def test_an_openrouter_window_falls_back_to_the_live_table(monkeypatch): _patch_litellm_info(monkeypatch, _litellm_miss) _patch_openrouter(monkeypatch, lambda req: _models_response(_DEEPSEEK_MODELS))