bb9bed82e1
ModelRole collapses to "primary": every role (main, tool selector, memory
agents, subagents, summarizer) resolves to the snapshot's frozen primary
model, per design 6.1/8.3 — users typically configure a single usable LLM,
so compile-time auxiliary bindings were bypassing run snapshots and
mis-attributing usage. Legacy auxiliary keys in stored snapshots, registry
JSON, and thread metadata are tolerated on read and dropped.
BREAKING CHANGE: ThreadModelSelection no longer carries an auxiliary ref;
snapshot selection_hash is computed over {primary, reasoning_effort} only;
ConfigurableModelMiddleware(role="auxiliary") is rejected.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
114 lines
3.5 KiB
Python
114 lines
3.5 KiB
Python
from __future__ import annotations
|
|
|
|
from datetime import UTC, datetime
|
|
from types import SimpleNamespace
|
|
from unittest.mock import MagicMock, patch
|
|
|
|
from langchain_core.messages import SystemMessage
|
|
|
|
from EvoScientist.middleware.runtime_context import (
|
|
RuntimeContextMiddleware,
|
|
create_runtime_context_middleware,
|
|
)
|
|
|
|
|
|
def _request():
|
|
request = SimpleNamespace(
|
|
state={},
|
|
runtime=object(),
|
|
system_message=SystemMessage(content="base system"),
|
|
)
|
|
request.override = lambda **kwargs: SimpleNamespace(
|
|
**{
|
|
"state": request.state,
|
|
"runtime": request.runtime,
|
|
"system_message": kwargs.get("system_message", request.system_message),
|
|
}
|
|
)
|
|
return request
|
|
|
|
|
|
def _system_text(modified) -> str:
|
|
system_message = modified.system_message
|
|
assert system_message is not None
|
|
return str(system_message.content)
|
|
|
|
|
|
def _mock_config():
|
|
cfg = MagicMock()
|
|
cfg.enable_ask_user = False
|
|
cfg.auto_mode = False
|
|
cfg.auto_approve = False
|
|
cfg.model_fallbacks = None
|
|
cfg.code_interpreter_timeout = 60
|
|
cfg.code_interpreter_max_result_chars = 6000
|
|
return cfg
|
|
|
|
|
|
def test_runtime_context_injects_current_date_and_timezone():
|
|
middleware = RuntimeContextMiddleware(
|
|
now_fn=lambda: datetime(2026, 6, 2, 12, 0, tzinfo=UTC)
|
|
)
|
|
|
|
modified = middleware.modify_request(_request())
|
|
system_text = _system_text(modified)
|
|
|
|
assert "<runtime_context>" in system_text
|
|
assert "Current date: 2026-06-02" in system_text
|
|
assert "Local timezone: UTC (UTC+00:00)" in system_text
|
|
assert "today" in system_text
|
|
|
|
|
|
@patch(
|
|
"EvoScientist.middleware.create_tool_selector_middleware",
|
|
return_value=[MagicMock(), MagicMock()],
|
|
)
|
|
@patch("EvoScientist.EvoScientist._compile_time_role_model")
|
|
@patch("EvoScientist.EvoScientist._ensure_config")
|
|
def test_default_middleware_includes_runtime_context(
|
|
mock_config, mock_model, mock_tool_selector
|
|
):
|
|
mock_config.return_value = _mock_config()
|
|
mock_model.return_value = MagicMock(profile={"max_input_tokens": 200_000})
|
|
|
|
from EvoScientist.EvoScientist import _get_default_middleware
|
|
|
|
middleware = _get_default_middleware()
|
|
|
|
assert any(isinstance(m, RuntimeContextMiddleware) for m in middleware)
|
|
|
|
|
|
@patch(
|
|
"EvoScientist.middleware.create_tool_selector_middleware",
|
|
return_value=[MagicMock(), MagicMock()],
|
|
)
|
|
@patch("EvoScientist.EvoScientist._compile_time_role_model")
|
|
@patch("EvoScientist.EvoScientist._ensure_config")
|
|
def test_async_subagent_middleware_includes_runtime_context(
|
|
mock_config, mock_model, mock_tool_selector
|
|
):
|
|
mock_config.return_value = _mock_config()
|
|
mock_model.return_value = MagicMock(profile={"max_input_tokens": 200_000})
|
|
|
|
from EvoScientist.EvoScientist import _get_default_middleware
|
|
|
|
middleware = _get_default_middleware(for_async_subagent=True)
|
|
|
|
assert any(isinstance(m, RuntimeContextMiddleware) for m in middleware)
|
|
|
|
|
|
@patch("EvoScientist.EvoScientist._ensure_chat_model")
|
|
def test_configured_subagent_middleware_includes_runtime_context(mock_model):
|
|
mock_model.return_value = MagicMock(profile={"max_input_tokens": 200_000})
|
|
|
|
from EvoScientist.EvoScientist import _inject_subagent_middleware
|
|
|
|
subs = [{"name": "test-agent"}]
|
|
_inject_subagent_middleware(subs)
|
|
|
|
assert any(isinstance(m, RuntimeContextMiddleware) for m in subs[0]["middleware"])
|
|
|
|
|
|
def test_runtime_context_factory_returns_middleware():
|
|
assert isinstance(create_runtime_context_middleware(), RuntimeContextMiddleware)
|