fix(minimax): drop stale ≤204,800 cache entries for MiniMax-M3 (#36726)
M3 is 1M context, but pre-catalog builds resolved it via the generic 'minimax' catch-all (204,800) and persisted that to the context-length cache. Step 1 of get_model_context_length returned the cached value directly before reaching the 'minimax-m3' (1M) catalog entry, so users who first probed M3 on an older build were stuck at 204K forever (e.g. /new in the Telegram gateway showing 'Context: 204K tokens (detected)'). Mirror the existing Kimi/Codex stale-cache guards: when a cached entry for a minimax-m3 slug is <= 204,800, drop it and re-resolve. M2.x slugs (correctly 204,800) are untouched since they don't match the M3 name.
This commit is contained in:
@@ -1128,6 +1128,18 @@ def _model_name_suggests_kimi(model: str) -> bool:
|
|||||||
return lower.startswith("kimi") or "moonshot" in lower
|
return lower.startswith("kimi") or "moonshot" in lower
|
||||||
|
|
||||||
|
|
||||||
|
def _model_name_suggests_minimax_m3(model: str) -> bool:
|
||||||
|
"""Return True if the model name looks like MiniMax M3.
|
||||||
|
|
||||||
|
Catches ``MiniMax-M3``, ``minimax/minimax-m3``, and similar variants
|
||||||
|
across surfaces (native MiniMax-M3, OpenRouter/Nous minimax/minimax-m3).
|
||||||
|
Used as a guard against stale cache entries seeded by pre-catalog builds
|
||||||
|
that resolved M3 via the generic ``minimax`` catch-all (204,800) before
|
||||||
|
the ``minimax-m3`` (1M) entry existed in DEFAULT_CONTEXT_LENGTHS.
|
||||||
|
"""
|
||||||
|
return "minimax-m3" in model.lower()
|
||||||
|
|
||||||
|
|
||||||
def _query_local_context_length(model: str, base_url: str, api_key: str = "") -> Optional[int]:
|
def _query_local_context_length(model: str, base_url: str, api_key: str = "") -> Optional[int]:
|
||||||
"""Query a local server for the model's context length."""
|
"""Query a local server for the model's context length."""
|
||||||
import httpx
|
import httpx
|
||||||
@@ -1539,6 +1551,19 @@ def get_model_context_length(
|
|||||||
model, base_url, f"{cached:,}",
|
model, base_url, f"{cached:,}",
|
||||||
)
|
)
|
||||||
_invalidate_cached_context_length(model, base_url)
|
_invalidate_cached_context_length(model, base_url)
|
||||||
|
# Invalidate stale ≤204,800 cache entries for MiniMax-M3. Pre-catalog
|
||||||
|
# builds resolved M3 via the generic ``minimax`` catch-all (204,800)
|
||||||
|
# and persisted it before the ``minimax-m3`` (1M) entry existed; that
|
||||||
|
# stale value would otherwise stick forever here at step 1. M3 is 1M,
|
||||||
|
# so any sub-256K cached value for an M3 slug is a leftover — drop it
|
||||||
|
# and fall through to the hardcoded default.
|
||||||
|
elif cached <= 204_800 and _model_name_suggests_minimax_m3(model):
|
||||||
|
logger.info(
|
||||||
|
"Dropping stale MiniMax-M3 cache entry %s@%s -> %s (pre-catalog value); "
|
||||||
|
"re-resolving via hardcoded defaults",
|
||||||
|
model, base_url, f"{cached:,}",
|
||||||
|
)
|
||||||
|
_invalidate_cached_context_length(model, base_url)
|
||||||
# Nous Portal: the portal /v1/models endpoint is authoritative.
|
# Nous Portal: the portal /v1/models endpoint is authoritative.
|
||||||
# Bypass the persistent cache so step 5b can always reconcile
|
# Bypass the persistent cache so step 5b can always reconcile
|
||||||
# against it — this corrects pre-fix entries seeded from the
|
# against it — this corrects pre-fix entries seeded from the
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ from unittest.mock import patch
|
|||||||
|
|
||||||
|
|
||||||
class TestMinimaxContextLengths:
|
class TestMinimaxContextLengths:
|
||||||
"""Verify context length entries match official docs (204,800 for all models).
|
"""Verify context length entries match official docs.
|
||||||
|
|
||||||
|
M2.x series is 204,800; M3 is 1M (max output 512K).
|
||||||
Source: https://platform.minimax.io/docs/api-reference/text-anthropic-api
|
Source: https://platform.minimax.io/docs/api-reference/text-anthropic-api
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@@ -15,11 +16,74 @@ class TestMinimaxContextLengths:
|
|||||||
|
|
||||||
def test_minimax_models_resolve_via_prefix(self):
|
def test_minimax_models_resolve_via_prefix(self):
|
||||||
from agent.model_metadata import get_model_context_length
|
from agent.model_metadata import get_model_context_length
|
||||||
# All MiniMax models should resolve to 204,800 via the "minimax" prefix
|
# M2.x models resolve to 204,800 via the "minimax" catch-all
|
||||||
for model in ("MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"):
|
for model in ("MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1", "MiniMax-M2"):
|
||||||
ctx = get_model_context_length(model, "")
|
ctx = get_model_context_length(model, "")
|
||||||
assert ctx == 204_800, f"{model} expected 204800, got {ctx}"
|
assert ctx == 204_800, f"{model} expected 204800, got {ctx}"
|
||||||
|
|
||||||
|
def test_minimax_m3_resolves_to_1m(self):
|
||||||
|
from agent.model_metadata import get_model_context_length
|
||||||
|
# M3 must beat the generic "minimax" catch-all (204,800) and resolve to
|
||||||
|
# a 1M-class context. The exact value depends on the source: our
|
||||||
|
# hardcoded catalog says 1,000,000; the OpenRouter catalog reports
|
||||||
|
# 1,048,576 (1024²). Either is correct — assert "≥ 1M, not 204,800".
|
||||||
|
for model in ("MiniMax-M3", "minimax/minimax-m3", "minimax-m3"):
|
||||||
|
ctx = get_model_context_length(model, "")
|
||||||
|
assert ctx >= 1_000_000, f"{model} expected 1M-class, got {ctx}"
|
||||||
|
|
||||||
|
|
||||||
|
class TestMinimaxM3StaleCacheGuard:
|
||||||
|
"""Pre-catalog builds resolved M3 via the generic 'minimax' catch-all
|
||||||
|
(204,800) and persisted it before the 'minimax-m3' (1M) catalog entry
|
||||||
|
existed. The step-1 cache guard must drop that stale value and re-resolve
|
||||||
|
to 1M, while leaving correct M2.x entries (204,800) untouched.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def test_suggests_minimax_m3(self):
|
||||||
|
from agent.model_metadata import _model_name_suggests_minimax_m3
|
||||||
|
assert _model_name_suggests_minimax_m3("MiniMax-M3")
|
||||||
|
assert _model_name_suggests_minimax_m3("minimax/minimax-m3")
|
||||||
|
assert not _model_name_suggests_minimax_m3("MiniMax-M2.7")
|
||||||
|
assert not _model_name_suggests_minimax_m3("MiniMax-M2.5")
|
||||||
|
|
||||||
|
def test_stale_m3_cache_dropped_and_reresolves_to_1m(self, tmp_path, monkeypatch):
|
||||||
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||||
|
import importlib
|
||||||
|
import agent.model_metadata as mm
|
||||||
|
importlib.reload(mm)
|
||||||
|
base = "https://api.minimaxi.com/anthropic"
|
||||||
|
mm.save_context_length("MiniMax-M3", base, 204_800)
|
||||||
|
ctx = mm.get_model_context_length(
|
||||||
|
"MiniMax-M3", base_url=base, api_key="", provider="minimax-cn"
|
||||||
|
)
|
||||||
|
assert ctx == 1_000_000
|
||||||
|
|
||||||
|
def test_correct_m3_cache_preserved(self, tmp_path, monkeypatch):
|
||||||
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||||
|
import importlib
|
||||||
|
import agent.model_metadata as mm
|
||||||
|
importlib.reload(mm)
|
||||||
|
base = "https://api.minimaxi.com/anthropic"
|
||||||
|
mm.save_context_length("MiniMax-M3", base, 1_000_000)
|
||||||
|
ctx = mm.get_model_context_length(
|
||||||
|
"MiniMax-M3", base_url=base, api_key="", provider="minimax-cn"
|
||||||
|
)
|
||||||
|
assert ctx == 1_000_000
|
||||||
|
|
||||||
|
def test_m2_cache_not_clobbered(self, tmp_path, monkeypatch):
|
||||||
|
monkeypatch.setenv("HERMES_HOME", str(tmp_path))
|
||||||
|
import importlib
|
||||||
|
import agent.model_metadata as mm
|
||||||
|
importlib.reload(mm)
|
||||||
|
base = "https://api.minimaxi.com/anthropic"
|
||||||
|
# 204,800 is the CORRECT value for M2.x — guard must not touch it.
|
||||||
|
for slug in ("MiniMax-M2.7", "MiniMax-M2.5", "MiniMax-M2.1"):
|
||||||
|
mm.save_context_length(slug, base, 204_800)
|
||||||
|
ctx = mm.get_model_context_length(
|
||||||
|
slug, base_url=base, api_key="", provider="minimax-cn"
|
||||||
|
)
|
||||||
|
assert ctx == 204_800, f"{slug} should stay 204800, got {ctx}"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class TestMinimaxThinkingSupport:
|
class TestMinimaxThinkingSupport:
|
||||||
|
|||||||
Reference in New Issue
Block a user