fix(gateway): make reasoning session-scoped by default

This commit is contained in:
Iris Jin 2026-04-25 02:39:01 -04:00 committed by Teknium
parent 489bed6f96
commit 25ba6a4a74
4 changed files with 269 additions and 23 deletions

View file

@ -33,6 +33,7 @@ def _make_runner():
runner._ephemeral_system_prompt = ""
runner._prefill_messages = []
runner._reasoning_config = None
runner._session_reasoning_overrides = {}
runner._show_reasoning = False
runner._provider_routing = {}
runner._fallback_model = None
@ -76,6 +77,10 @@ class TestReasoningCommand:
source = inspect.getsource(gateway_run.GatewayRunner._handle_message)
assert '"reasoning"' in source
def test_parse_reasoning_command_args_accepts_ascii_and_smart_global_flags(self):
assert gateway_run.GatewayRunner._parse_reasoning_command_args("high --global") == ("high", True)
assert gateway_run.GatewayRunner._parse_reasoning_command_args("—global xhigh") == ("xhigh", True)
@pytest.mark.asyncio
async def test_reasoning_command_reloads_current_state_from_config(self, tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes"
@ -111,13 +116,90 @@ class TestReasoningCommand:
runner = _make_runner()
runner._reasoning_config = {"enabled": True, "effort": "medium"}
result = await runner._handle_reasoning_command(_make_event("/reasoning low"))
result = await runner._handle_reasoning_command(_make_event("/reasoning low --global"))
saved = yaml.safe_load(config_path.read_text(encoding="utf-8"))
assert saved["agent"]["reasoning_effort"] == "low"
assert runner._reasoning_config == {"enabled": True, "effort": "low"}
assert "takes effect on next message" in result
@pytest.mark.asyncio
async def test_handle_reasoning_command_defaults_to_session_only(self, tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text("agent:\n reasoning_effort: medium\n", encoding="utf-8")
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
runner = _make_runner()
event = _make_event("/reasoning high")
session_key = runner._session_key_for_source(event.source)
result = await runner._handle_reasoning_command(event)
saved = yaml.safe_load(config_path.read_text(encoding="utf-8"))
assert saved["agent"]["reasoning_effort"] == "medium"
assert runner._session_reasoning_overrides[session_key] == {"enabled": True, "effort": "high"}
assert runner._reasoning_config == {"enabled": True, "effort": "high"}
assert "session only" in result
@pytest.mark.asyncio
async def test_reasoning_global_clears_existing_session_override(self, tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text("agent:\n reasoning_effort: medium\n", encoding="utf-8")
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
runner = _make_runner()
event = _make_event("/reasoning low --global")
session_key = runner._session_key_for_source(event.source)
runner._session_reasoning_overrides[session_key] = {"enabled": True, "effort": "xhigh"}
result = await runner._handle_reasoning_command(event)
saved = yaml.safe_load(config_path.read_text(encoding="utf-8"))
assert saved["agent"]["reasoning_effort"] == "low"
assert session_key not in runner._session_reasoning_overrides
assert "saved to config" in result
@pytest.mark.asyncio
async def test_reasoning_reset_clears_session_override_without_config_write(self, tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes"
hermes_home.mkdir()
config_path = hermes_home / "config.yaml"
config_path.write_text("agent:\n reasoning_effort: medium\n", encoding="utf-8")
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
runner = _make_runner()
event = _make_event("/reasoning reset")
session_key = runner._session_key_for_source(event.source)
runner._session_reasoning_overrides[session_key] = {"enabled": True, "effort": "xhigh"}
result = await runner._handle_reasoning_command(event)
saved = yaml.safe_load(config_path.read_text(encoding="utf-8"))
assert saved["agent"]["reasoning_effort"] == "medium"
assert session_key not in runner._session_reasoning_overrides
assert "cleared" in result
def test_resolve_session_reasoning_prefers_session_override(self, tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes"
hermes_home.mkdir()
(hermes_home / "config.yaml").write_text("agent:\n reasoning_effort: low\n", encoding="utf-8")
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
runner = _make_runner()
source = _make_event("/reasoning").source
session_key = runner._session_key_for_source(source)
runner._session_reasoning_overrides[session_key] = {"enabled": True, "effort": "xhigh"}
assert runner._resolve_session_reasoning_config(source=source) == {"enabled": True, "effort": "xhigh"}
def test_run_agent_reloads_reasoning_config_per_message(self, tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes"
hermes_home.mkdir()
@ -167,6 +249,56 @@ class TestReasoningCommand:
assert _CapturingAgent.last_init is not None
assert _CapturingAgent.last_init["reasoning_config"] == {"enabled": True, "effort": "low"}
def test_run_agent_prefers_session_reasoning_override(self, tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes"
hermes_home.mkdir()
(hermes_home / "config.yaml").write_text("agent:\n reasoning_effort: low\n", encoding="utf-8")
monkeypatch.setattr(gateway_run, "_hermes_home", hermes_home)
monkeypatch.setattr(gateway_run, "_env_path", hermes_home / ".env")
monkeypatch.setattr(gateway_run, "load_dotenv", lambda *args, **kwargs: None)
monkeypatch.setattr(
gateway_run,
"_resolve_runtime_agent_kwargs",
lambda: {
"provider": "openrouter",
"api_mode": "chat_completions",
"base_url": "https://openrouter.ai/api/v1",
"api_key": "***",
},
)
fake_run_agent = types.ModuleType("run_agent")
fake_run_agent.AIAgent = _CapturingAgent
monkeypatch.setitem(sys.modules, "run_agent", fake_run_agent)
_CapturingAgent.last_init = None
runner = _make_runner()
session_key = "agent:main:local:dm"
runner._session_reasoning_overrides[session_key] = {"enabled": True, "effort": "high"}
source = SessionSource(
platform=Platform.LOCAL,
chat_id="cli",
chat_name="CLI",
chat_type="dm",
user_id="user-1",
)
result = asyncio.run(
runner._run_agent(
message="ping",
context_prompt="",
history=[],
source=source,
session_id="session-1",
session_key=session_key,
)
)
assert result["final_response"] == "ok"
assert _CapturingAgent.last_init is not None
assert _CapturingAgent.last_init["reasoning_config"] == {"enabled": True, "effort": "high"}
def test_run_agent_includes_enabled_mcp_servers_in_gateway_toolsets(self, tmp_path, monkeypatch):
hermes_home = tmp_path / "hermes"
hermes_home.mkdir()