63969b596d
* feat(middleware): reposition code interpreter middleware in the stack * feat(models): add qwen3.7-plus model entry and update context window comment * feat(models): add qwen3.7-max and qwen3.7-plus model entries for DashScope * feat(auxiliary): implement auxiliary model support for background tasks and tool selection - Added auxiliary model configuration to EvoScientistConfig. - Introduced _ensure_auxiliary_chat_model function to manage auxiliary model instances. - Updated onboarding steps to include auxiliary model selection. - Modified middleware to route tool selection to the auxiliary model when applicable. - Enhanced tests to cover auxiliary model functionality and configuration. * feat(steps): update UI backend selection options and descriptions * Refactor code structure for improved readability and maintainability * feat(patches): implement OpenRouter response reasoning item stripping to prevent multi-turn errors * feat: update version to v0.1.4 in badges, README, and pyproject.toml; adjust skill counts in steps.py * feat(config): add auxiliary model and provider environment variables to test setup
116 lines
3.5 KiB
Python
116 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.auxiliary_model = ""
|
|
cfg.auxiliary_provider = ""
|
|
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._ensure_chat_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._ensure_chat_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)
|