fix: close provider-anthropic MiniMax proxy bypass + rework cache observability

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).
This commit is contained in:
kshitij 2026-08-12 13:39:39 +05:30
parent c1e2529ae2
commit 223f703012
4 changed files with 80 additions and 76 deletions

View File

@ -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

View File

@ -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,

View File

@ -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

View File

@ -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