Files
hermes-agent/tests/agent/test_auxiliary_openrouter_max_tokens.py
T
Teknium 13c3958df5 fix(auxiliary): credit-limited 402s clamp to the affordable budget instead of failing
Second leg of the masoria 20-minute 'Summarizing' stall (Aug 31 2026
bundle): after the Codex timeout, compression fell back to OpenRouter,
which defaulted the omitted output cap to the model's full 65,536-token
window and rejected with '402 ... can only afford 7117' — on an account
whose balance easily covered a summary. Three fallbacks, three 402s,
zero summaries.

- _create_with_progress: when a 402 names an affordable budget, retry
  ONCE with that cap (minus 64-token margin, 512-token floor); plain
  exhaustion 402s and within-budget 402s re-raise unchanged. Single
  funnel covers primary, fallback, and retry call sites.
- extends the #41055 OpenRouter max_tokens preservation onto the current
  _build_call_kwargs gate (explicit caps survive; None still omitted).

Live A/B against a mock OpenRouter enforcing a 7117-token budget with
the real SDK + real adapter: main fails with the exact bundle 402;
fixed branch retries clamped and returns the summary.
2026-08-31 13:00:24 -07:00

73 lines
2.7 KiB
Python

"""Test that _build_call_kwargs preserves max_tokens for OpenRouter endpoints.
Regression test for #41035: OpenRouter free-tier credits exhausted when
max_tokens was stripped, causing HTTP 402 and fallback to text-only model.
"""
import pytest
from agent.auxiliary_client import _build_call_kwargs
class TestOpenRouterMaxTokens:
"""max_tokens must be included for OpenRouter to prevent free-tier 402."""
def test_openrouter_provider_includes_max_tokens(self):
"""Direct openrouter provider keeps max_tokens."""
kwargs = _build_call_kwargs(
provider="openrouter",
model="openai/gpt-4o-mini",
messages=[{"role": "user", "content": "test"}],
max_tokens=2000,
)
assert kwargs.get("max_tokens") == 2000 or kwargs.get("max_completion_tokens") == 2000
def test_openrouter_base_url_includes_max_tokens(self):
"""Custom endpoint with openrouter.ai base_url keeps max_tokens."""
kwargs = _build_call_kwargs(
provider="openai",
model="gpt-4o-mini",
messages=[{"role": "user", "content": "test"}],
max_tokens=2000,
base_url="https://openrouter.ai/api/v1",
)
assert kwargs.get("max_tokens") == 2000 or kwargs.get("max_completion_tokens") == 2000
def test_generic_provider_omits_max_tokens(self):
"""Generic OpenAI-compatible provider still omits max_tokens."""
kwargs = _build_call_kwargs(
provider="openai",
model="gpt-4o-mini",
messages=[{"role": "user", "content": "test"}],
max_tokens=2000,
)
assert "max_tokens" not in kwargs
def test_anthropic_compat_still_includes_max_tokens(self):
"""Anthropic-compatible endpoints still include max_tokens."""
kwargs = _build_call_kwargs(
provider="minimax",
model="MiniMax-Text-01",
messages=[{"role": "user", "content": "test"}],
max_tokens=4000,
)
assert kwargs["max_tokens"] == 4000
def test_none_max_tokens_never_included(self):
"""max_tokens=None is never added regardless of provider."""
for provider, base_url in [
("openrouter", None),
("openai", "https://openrouter.ai/api/v1"),
("minimax", None),
]:
kwargs = _build_call_kwargs(
provider=provider,
model="test-model",
messages=[{"role": "user", "content": "test"}],
max_tokens=None,
base_url=base_url,
)
assert "max_tokens" not in kwargs, (
f"max_tokens should not be set when None for {provider}"
)