Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion agent/agent_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -1485,7 +1485,14 @@ def init_agent(
# Reject models whose context window is below the minimum required
# for reliable tool-calling workflows (64K tokens).
_ctx = getattr(agent.context_compressor, "context_length", 0)
if _ctx and _ctx < MINIMUM_CONTEXT_LENGTH:
if _config_context_length is not None and _ctx and _ctx < _config_context_length:
raise ValueError(
f"Model {agent.model} has a context window of {_ctx:,} tokens, "
f"which is below the user-configured override of "
f"{_config_context_length:,} tokens. Adjust or remove the "
f"model.context_length setting in config.yaml to proceed."
)
if _config_context_length is None and _ctx and _ctx < MINIMUM_CONTEXT_LENGTH:
raise ValueError(
f"Model {agent.model} has a context window of {_ctx:,} tokens, "
f"which is below the minimum {MINIMUM_CONTEXT_LENGTH:,} required "
Expand Down
74 changes: 74 additions & 0 deletions tests/agent/test_agent_init.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from types import SimpleNamespace

import pytest

import agent.agent_init as agent_init
import agent.auxiliary_client as auxiliary_client
from agent.model_metadata import MINIMUM_CONTEXT_LENGTH
import hermes_cli.config as config_module


class DummyCompressor:
def __init__(self, *args, **kwargs):
self.context_length = self._context_length


def make_agent():
agent = SimpleNamespace()
agent._base_url_hostname = ""
agent._base_url_lower = ""
agent._transport_cache = {}
agent._get_transport = lambda *args, **kwargs: None
agent._is_openrouter_url = lambda *args, **kwargs: False
agent._is_azure_openai_url = lambda *args, **kwargs: False
agent._is_direct_openai_url = lambda *args, **kwargs: False
agent._provider_model_requires_responses_api = lambda *args, **kwargs: False
agent._anthropic_prompt_cache_policy = lambda *args, **kwargs: (False, False)
agent._create_openai_client = lambda *args, **kwargs: SimpleNamespace()
agent._ensure_lmstudio_runtime_loaded = lambda _config_context_length: None
return agent


def test_init_agent_rejects_model_below_user_config_override(monkeypatch):
config = {"model": {"context_length": 60000}}
monkeypatch.setattr(config_module, "load_config", lambda: config)
monkeypatch.setattr(agent_init, "_install_safe_stdio", lambda: None)
monkeypatch.setattr(
auxiliary_client,
"resolve_provider_client",
lambda *args, **kwargs: (SimpleNamespace(api_key="test-key", base_url="https://example.invalid/v1"), "test/model"),
)

context_length = 50000

class Compressor(DummyCompressor):
_context_length = context_length

monkeypatch.setattr(agent_init, "ContextCompressor", Compressor)

agent = make_agent()

with pytest.raises(ValueError, match="user-configured override"):
agent_init.init_agent(agent, model="test/model")


def test_init_agent_rejects_model_below_minimum_without_override(monkeypatch):
monkeypatch.setattr(config_module, "load_config", lambda: {})
monkeypatch.setattr(agent_init, "_install_safe_stdio", lambda: None)
monkeypatch.setattr(
auxiliary_client,
"resolve_provider_client",
lambda *args, **kwargs: (SimpleNamespace(api_key="test-key", base_url="https://example.invalid/v1"), "test/model"),
)

context_length = 50000

class Compressor(DummyCompressor):
_context_length = context_length

monkeypatch.setattr(agent_init, "ContextCompressor", Compressor)

agent = make_agent()

with pytest.raises(ValueError, match=f"below the minimum {MINIMUM_CONTEXT_LENGTH:,}"):
agent_init.init_agent(agent, model="test/model")
Loading