From 223f7030128abf9aaff8e784201f3fe5b424ab68 Mon Sep 17 00:00:00 2001 From: kshitij <82637225+kshitijk4poor@users.noreply.github.com> Date: Wed, 12 Aug 2026 13:39:39 +0530 Subject: [PATCH] fix: close provider-anthropic MiniMax proxy bypass + rework cache observability MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up fixes on top of the salvaged #83678 commit: 1. Hoist the MiniMax-M3 marker exclusion ABOVE the native-Anthropic early return. provider="anthropic" pointed at a MiniMax /anthropic proxy is a supported override (_anthropic_base_url_override_ok), and the is_native_anthropic branch matched on provider alone — returning (True, True) before the M3 exclusion was reached. Two regression tests pin the proxy route (M3 off, M2.7 still on). 2. Reuse the existing _model_name_suggests_minimax_m3() helper from agent/model_metadata.py instead of a second inline substring copy. 3. Drop the debug kwarg on normalize_usage() — it had zero production callers and duplicated standard logging level gating. The cache-observability line is now a plain logger.debug scoped to MiniMax providers on the Anthropic wire only, so the "+128 floor" note can no longer appear for native Anthropic where it is false. Tests updated accordingly (MiniMax logs, native Anthropic does not). --- agent/agent_runtime_helpers.py | 47 ++++++++------- agent/usage_pricing.py | 21 +++---- tests/agent/test_usage_pricing.py | 60 ++++++------------- .../test_anthropic_prompt_cache_policy.py | 28 +++++++++ 4 files changed, 80 insertions(+), 76 deletions(-) diff --git a/agent/agent_runtime_helpers.py b/agent/agent_runtime_helpers.py index 1d74670952f20..50db73c3b35c6 100644 --- a/agent/agent_runtime_helpers.py +++ b/agent/agent_runtime_helpers.py @@ -2181,6 +2181,27 @@ def anthropic_prompt_cache_policy( and (eff_provider == "anthropic" or base_url_hostname(eff_base_url) == "api.anthropic.com") ) + # MiniMax-M3 rides MiniMax's server-side automatic prefix cache on the + # Anthropic wire (content-keyed, no marker needed); explicit cache_control + # is documented for M2.7/M2.5/M2.1/M2 only, so markers on M3 are dead + # weight — never observable (cache_creation always 0) nor billable. + # Checked BEFORE the native-Anthropic return: provider="anthropic" + # pointed at a MiniMax /anthropic proxy is a supported override + # (_anthropic_base_url_override_ok) that would otherwise return + # (True, True) above this exclusion. + # Docs: https://platform.minimax.io/docs/api-reference/text-prompt-caching + is_minimax_provider = provider_lower in {"minimax", "minimax-cn"} + is_minimax_host = ( + base_url_host_matches(eff_base_url, "api.minimax.io") + or base_url_host_matches(eff_base_url, "api.minimaxi.com") + ) + is_minimax_route = is_minimax_provider or is_minimax_host + if is_anthropic_wire and is_minimax_route: + from agent.model_metadata import _model_name_suggests_minimax_m3 + + if _model_name_suggests_minimax_m3(eff_model): + return False, False + if is_native_anthropic: return True, True # Envelope layout is an OpenAI-wire construct. Portal Claude on the native @@ -2213,29 +2234,11 @@ def anthropic_prompt_cache_policy( # explicitly via provider id or host match so users on # provider=minimax / minimax-cn (or custom endpoints pointing at # api.minimax.io/anthropic / api.minimaxi.com/anthropic) get the - # same cost reduction as Claude traffic. + # same cost reduction as Claude traffic. MiniMax-M3 never reaches + # here — it is excluded before the native-Anthropic return above. # Docs: https://platform.minimax.io/docs/api-reference/anthropic-api-compatible-cache - # - # MiniMax-M3 is intentionally excluded: M3 ships server-side automatic - # prefix caching on this wire format (content-keyed, no marker needed — - # see https://platform.minimax.io/docs/api-reference/text-prompt-caching), - # and cache_control markers are NOT on its explicit-cache support list - # (M2.7/M2.5/M2.1/M2 only). Emitting markers on M3 wasted serialization - # overhead, risked perturbing the server-side prefix hash, and gave users - # a false sense of explicit-cache savings. Empirically verified against - # api.minimaxi.com/anthropic/v1/messages with MiniMax-M3[1m]: identical - # system prompt hit-rate with and without markers; cache_read field has - # a +128 floor and cache_creation is always 0, so the marker path is - # neither observable nor billable for M3 users. - if is_anthropic_wire: - is_minimax_provider = provider_lower in {"minimax", "minimax-cn"} - is_minimax_host = ( - base_url_host_matches(eff_base_url, "api.minimax.io") - or base_url_host_matches(eff_base_url, "api.minimaxi.com") - ) - is_minimax_m3 = "minimax-m3" in model_lower - if (is_minimax_provider or is_minimax_host) and not is_minimax_m3: - return True, True + if is_anthropic_wire and is_minimax_route: + return True, True # Qwen/Alibaba on OpenCode (Zen/Go) and native DashScope: OpenAI-wire # transport that accepts Anthropic-style cache_control markers and diff --git a/agent/usage_pricing.py b/agent/usage_pricing.py index 8b6875028d7f3..592f6742b6591 100644 --- a/agent/usage_pricing.py +++ b/agent/usage_pricing.py @@ -1210,7 +1210,6 @@ def normalize_usage( *, provider: Optional[str] = None, api_mode: Optional[str] = None, - debug: bool = False, ) -> CanonicalUsage: """Normalize raw API response usage into canonical token buckets. @@ -1292,20 +1291,18 @@ def normalize_usage( getattr(completion_details, "reasoning_tokens", 0) ) - # NOTE: opt-in cache observability for MiniMax-M3 (and similar providers - # whose usage.cache_read_input_tokens carries a constant +128 floor and - # whose usage.cache_creation_input_tokens is always 0). See - # https://platform.minimax.io/docs/api-reference/text-prompt-caching - # (Automatic Caching table). On M3, the cache_read field is NOT a - # reliable hit signal; the only signal that survives is the input_tokens - # drop between consecutive calls. This debug block logs the - # observable-only fields so an operator can confirm cache is working - # without relying on the misleading cache_read number. - if debug and (mode == "anthropic_messages" or provider_name in {"minimax", "minimax-cn"}): + # Cache observability for MiniMax's Anthropic wire: on MiniMax-M3, + # usage.cache_read_input_tokens carries a constant +128 floor and + # cache_creation_input_tokens is always 0, so cache_read is NOT a + # reliable hit signal — the signal that survives is the input_tokens + # drop between consecutive calls. Standard level-gated logger.debug; + # enable via logging config to confirm cache behavior. + # Docs: https://platform.minimax.io/docs/api-reference/text-prompt-caching + if provider_name in {"minimax", "minimax-cn"} and mode == "anthropic_messages": logger.debug( "cache_observability provider=%s mode=%s input_tokens=%s " "output_tokens=%s cache_read_tokens=%s cache_write_tokens=%s " - "(note: cache_read on this provider carries a +128 constant " + "(note: on MiniMax-M3 cache_read carries a +128 constant " "floor and is not a reliable hit signal — track input_tokens " "drops across calls instead)", provider_name, mode, input_tokens, output_tokens, diff --git a/tests/agent/test_usage_pricing.py b/tests/agent/test_usage_pricing.py index 0ae4187491e6f..7939faaabf312 100644 --- a/tests/agent/test_usage_pricing.py +++ b/tests/agent/test_usage_pricing.py @@ -314,32 +314,13 @@ def test_vertex_default_model_estimates_cached_usage(monkeypatch): assert result.amount_usd is not None and result.amount_usd > 0 -def test_normalize_usage_debug_off_emits_no_log(caplog): - """The opt-in cache observability block must NOT log when debug=False - (the default). Production callers should never see this log line — - it is only useful when an operator explicitly opts in to investigate - cache behavior on MiniMax-M3 or any other provider whose - cache_read_input_tokens carries a misleading constant offset. - """ - usage = SimpleNamespace( - input_tokens=53, - output_tokens=10, - cache_read_input_tokens=128, - cache_creation_input_tokens=0, - ) - - with caplog.at_level("DEBUG", logger="agent.usage_pricing"): - normalize_usage(usage, provider="minimax-cn", api_mode="anthropic_messages") - - assert all("cache_observability" not in rec.message for rec in caplog.records) - - -def test_normalize_usage_debug_on_minimax_logs_cache_observability(caplog): - """When debug=True is passed and the provider is MiniMax/M3, - normalize_usage emits a debug log line that records the observable-only - fields (input_tokens, output_tokens, cache_read_tokens, - cache_write_tokens) so an operator can see real cache behavior - without trusting the misleading cache_read number. +def test_normalize_usage_minimax_logs_cache_observability(caplog): + """MiniMax providers on the Anthropic wire emit a debug-level + cache-observability line recording the observable fields + (input_tokens, output_tokens, cache_read_tokens, cache_write_tokens), + so an operator can see real cache behavior without trusting the + misleading cache_read number (constant +128 floor on MiniMax-M3). + Standard logging level gating applies — no separate opt-in flag. """ usage = SimpleNamespace( input_tokens=1, @@ -353,7 +334,6 @@ def test_normalize_usage_debug_on_minimax_logs_cache_observability(caplog): usage, provider="minimax-cn", api_mode="anthropic_messages", - debug=True, ) cache_obs_records = [r for r in caplog.records if "cache_observability" in r.message] @@ -363,17 +343,13 @@ def test_normalize_usage_debug_on_minimax_logs_cache_observability(caplog): assert "output_tokens=11" in record.message assert "cache_read_tokens=8594" in record.message assert "cache_write_tokens=0" in record.message - assert "+128 constant floor" in record.message -def test_normalize_usage_debug_on_claude_also_logs_cache_observability(caplog): - """The opt-in observability block fires for every anthropic_messages - response — not just MiniMax — because the input_tokens-vs-cache_read - framing is useful diagnostic information on any Anthropic-compatible - wire (e.g. OpenRouter Claude, Bedrock Claude, GLM Claude) where a - provider's cache_read_input_tokens semantics may differ from - Anthropic's native contract. The block is debug-level and off by - default, so emitting it for Claude traffic has zero production cost. +def test_normalize_usage_native_anthropic_no_cache_observability(caplog): + """The MiniMax cache-observability line must NOT fire for native + Anthropic: there cache_read_input_tokens is exact and billable, so + the MiniMax-specific "+128 floor / unreliable hit signal" note would + be false and misleading in the logs. """ usage = SimpleNamespace( input_tokens=100, @@ -383,14 +359,14 @@ def test_normalize_usage_debug_on_claude_also_logs_cache_observability(caplog): ) with caplog.at_level("DEBUG", logger="agent.usage_pricing"): - normalize_usage( + result = normalize_usage( usage, provider="anthropic", api_mode="anthropic_messages", - debug=True, ) - cache_obs_records = [r for r in caplog.records if "cache_observability" in r.message] - assert len(cache_obs_records) == 1 - assert "input_tokens=100" in cache_obs_records[0].message - assert "cache_read_tokens=50" in cache_obs_records[0].message + assert all("cache_observability" not in rec.message for rec in caplog.records) + # Token normalization itself is unaffected. + assert result.input_tokens == 100 + assert result.cache_read_tokens == 50 + assert result.cache_write_tokens == 10 diff --git a/tests/run_agent/test_anthropic_prompt_cache_policy.py b/tests/run_agent/test_anthropic_prompt_cache_policy.py index 2852393e70593..e0a4ae00fa407 100644 --- a/tests/run_agent/test_anthropic_prompt_cache_policy.py +++ b/tests/run_agent/test_anthropic_prompt_cache_policy.py @@ -252,6 +252,34 @@ class TestMiniMaxAnthropicWire: ) assert agent._anthropic_prompt_cache_policy() == (False, False) + def test_minimax_m3_via_provider_anthropic_proxy_does_not_cache(self): + # provider="anthropic" pointed at a MiniMax /anthropic proxy is a + # supported override (_anthropic_base_url_override_ok accepts + # MiniMax-style /anthropic hosts and _resolve_explicit_runtime + # preserves provider="anthropic"). The M3 exclusion must run + # BEFORE the native-Anthropic early return, or this route keeps + # emitting markers while the direct minimax/minimax-cn routes + # don't. + agent = _make_agent( + provider="anthropic", + base_url="https://api.minimax.io/anthropic", + api_mode="anthropic_messages", + model="MiniMax-M3", + ) + assert agent._anthropic_prompt_cache_policy() == (False, False) + + def test_minimax_m27_via_provider_anthropic_proxy_still_caches(self): + # The proxy-route exclusion is M3-only: M2.x through the same + # provider="anthropic" MiniMax proxy keeps explicit cache_control + # (the native-Anthropic return still applies). + agent = _make_agent( + provider="anthropic", + base_url="https://api.minimax.io/anthropic", + api_mode="anthropic_messages", + model="MiniMax-M2.7", + ) + assert agent._anthropic_prompt_cache_policy() == (True, True) + def test_minimax_m27_still_caches_after_m3_opt_out(self): # Regression guard: the M3 substring check must not collide with # M2.7 / M2.5 / M2.1 / M2 model names. "minimax-m3" is not a