Import AITURK IDE 1.0.0-beta.1 from Hermes 63279301; preserve MIT license
This commit is contained in:
@@ -0,0 +1,307 @@
|
||||
"""Regression tests for gateway preservation of provider-derived request_overrides.
|
||||
|
||||
Named custom providers can return request_overrides (for example
|
||||
``extra_body.text.verbosity`` for OpenAI Responses). The gateway must preserve
|
||||
those overrides on the runtime path and merge fast-mode overrides on top rather
|
||||
than replacing them with an empty dict.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import sys
|
||||
import threading
|
||||
import types
|
||||
from types import SimpleNamespace
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
import gateway.run as gateway_run
|
||||
from gateway.config import Platform
|
||||
from gateway.session import SessionSource
|
||||
|
||||
|
||||
class _CapturingAgent:
|
||||
last_init = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
type(self).last_init = dict(kwargs)
|
||||
self.tools = []
|
||||
self.request_overrides = dict(kwargs.get("request_overrides") or {})
|
||||
|
||||
def run_conversation(self, user_message: str, conversation_history=None, task_id=None):
|
||||
return {
|
||||
"final_response": "ok",
|
||||
"messages": [],
|
||||
"api_calls": 1,
|
||||
}
|
||||
|
||||
|
||||
def _install_fake_agent(monkeypatch):
|
||||
fake_run_agent = types.ModuleType("run_agent")
|
||||
fake_run_agent.AIAgent = _CapturingAgent
|
||||
monkeypatch.setitem(sys.modules, "run_agent", fake_run_agent)
|
||||
|
||||
|
||||
def _make_runner():
|
||||
runner = object.__new__(gateway_run.GatewayRunner)
|
||||
runner.adapters = {}
|
||||
runner.session_store = None
|
||||
runner.config = None
|
||||
runner._voice_mode = {}
|
||||
runner._ephemeral_system_prompt = ""
|
||||
runner._prefill_messages = []
|
||||
runner._reasoning_config = None
|
||||
runner._show_reasoning = False
|
||||
runner._provider_routing = {}
|
||||
runner._fallback_model = None
|
||||
runner._service_tier = None
|
||||
runner._running_agents = {}
|
||||
runner._running_agents_ts = {}
|
||||
runner._background_tasks = set()
|
||||
runner._session_db = None
|
||||
runner._session_model_overrides = {}
|
||||
runner._session_reasoning_overrides = {}
|
||||
runner._pending_model_notes = {}
|
||||
runner._pending_approvals = {}
|
||||
runner._agent_cache = {}
|
||||
runner._agent_cache_lock = threading.Lock()
|
||||
runner._get_or_create_gateway_honcho = lambda session_key: (None, None)
|
||||
runner.hooks = MagicMock()
|
||||
runner.hooks.emit = AsyncMock()
|
||||
runner.hooks.loaded_hooks = []
|
||||
return runner
|
||||
|
||||
|
||||
def _make_source() -> SessionSource:
|
||||
return SessionSource(
|
||||
platform=Platform.FEISHU,
|
||||
chat_id="ou_test",
|
||||
chat_type="dm",
|
||||
user_id="user-1",
|
||||
user_name="tester",
|
||||
)
|
||||
|
||||
|
||||
def test_resolve_runtime_agent_kwargs_preserves_request_overrides(monkeypatch):
|
||||
monkeypatch.setattr(
|
||||
"hermes_cli.runtime_provider.resolve_runtime_provider",
|
||||
lambda: {
|
||||
"api_key": "***",
|
||||
"base_url": "https://example.test/v1",
|
||||
"provider": "custom",
|
||||
"api_mode": "codex_responses",
|
||||
"command": None,
|
||||
"args": [],
|
||||
"credential_pool": None,
|
||||
"request_overrides": {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
result = gateway_run._resolve_runtime_agent_kwargs()
|
||||
|
||||
assert result["request_overrides"] == {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
}
|
||||
|
||||
|
||||
def test_turn_route_preserves_provider_request_overrides_without_fast_mode():
|
||||
runner = _make_runner()
|
||||
runner._service_tier = None
|
||||
runtime_kwargs = {
|
||||
"api_key": "***",
|
||||
"base_url": "https://example.test/v1",
|
||||
"provider": "custom",
|
||||
"api_mode": "codex_responses",
|
||||
"command": None,
|
||||
"args": [],
|
||||
"credential_pool": None,
|
||||
"request_overrides": {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
},
|
||||
}
|
||||
|
||||
route = gateway_run.GatewayRunner._resolve_turn_agent_config(
|
||||
runner,
|
||||
"hi",
|
||||
"gpt-5.4",
|
||||
runtime_kwargs,
|
||||
)
|
||||
|
||||
assert route["request_overrides"] == {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
}
|
||||
|
||||
|
||||
def test_turn_route_merges_fast_mode_with_provider_request_overrides():
|
||||
runner = _make_runner()
|
||||
runner._service_tier = "priority"
|
||||
runtime_kwargs = {
|
||||
"api_key": "***",
|
||||
"base_url": "https://example.test/v1",
|
||||
"provider": "custom",
|
||||
"api_mode": "codex_responses",
|
||||
"command": None,
|
||||
"args": [],
|
||||
"credential_pool": None,
|
||||
"request_overrides": {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
},
|
||||
}
|
||||
|
||||
with patch(
|
||||
"hermes_cli.models.resolve_fast_mode_overrides",
|
||||
return_value={"service_tier": "priority"},
|
||||
):
|
||||
route = gateway_run.GatewayRunner._resolve_turn_agent_config(
|
||||
runner,
|
||||
"hi",
|
||||
"gpt-5.4",
|
||||
runtime_kwargs,
|
||||
)
|
||||
|
||||
assert route["request_overrides"] == {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
"service_tier": "priority",
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_run_agent_preserves_provider_request_overrides_on_gateway_path(monkeypatch):
|
||||
monkeypatch.setattr(gateway_run, "_load_gateway_config", lambda: {})
|
||||
monkeypatch.setattr(gateway_run, "load_dotenv", lambda *args, **kwargs: None)
|
||||
monkeypatch.setattr(gateway_run, "_load_gateway_runtime_config", lambda: {})
|
||||
monkeypatch.setattr(gateway_run, "_resolve_gateway_model", lambda config=None: "gpt-5.4")
|
||||
monkeypatch.setattr(
|
||||
gateway_run,
|
||||
"_resolve_runtime_agent_kwargs",
|
||||
lambda: {
|
||||
"provider": "custom",
|
||||
"api_mode": "codex_responses",
|
||||
"base_url": "https://example.test/v1",
|
||||
"api_key": "***",
|
||||
"request_overrides": {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
},
|
||||
},
|
||||
)
|
||||
_install_fake_agent(monkeypatch)
|
||||
|
||||
import hermes_cli.tools_config as tools_config
|
||||
|
||||
monkeypatch.setattr(tools_config, "_get_platform_tools", lambda user_config, platform_key: {"core"})
|
||||
|
||||
runner = _make_runner()
|
||||
source = _make_source()
|
||||
session_key = "agent:main:feishu:dm:ou_test"
|
||||
|
||||
runner.session_store = SimpleNamespace(
|
||||
get_or_create_session=lambda _source: SimpleNamespace(session_id="session-1"),
|
||||
load_transcript=lambda _session_id: [],
|
||||
)
|
||||
|
||||
_CapturingAgent.last_init = None
|
||||
result = await runner._run_agent(
|
||||
message="hi",
|
||||
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["request_overrides"] == {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
}
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reused_agent_turn_merges_request_overrides_not_overwrite(monkeypatch):
|
||||
"""Merge-not-overwrite regression (salvaged from PR #52432).
|
||||
|
||||
A cached/reused gateway agent must keep its init-time request_overrides
|
||||
(custom-provider extra_body) across turns: a /fast turn layers
|
||||
service_tier ON TOP, and the following normal turn drops only the stale
|
||||
fast-mode key while the provider extra_body survives.
|
||||
"""
|
||||
monkeypatch.setattr(gateway_run, "_load_gateway_config", lambda: {})
|
||||
monkeypatch.setattr(gateway_run, "load_dotenv", lambda *args, **kwargs: None)
|
||||
monkeypatch.setattr(gateway_run, "_load_gateway_runtime_config", lambda: {})
|
||||
monkeypatch.setattr(gateway_run, "_resolve_gateway_model", lambda config=None: "gpt-5.4")
|
||||
monkeypatch.setattr(
|
||||
gateway_run,
|
||||
"_resolve_runtime_agent_kwargs",
|
||||
lambda: {
|
||||
"provider": "custom",
|
||||
"api_mode": "codex_responses",
|
||||
"base_url": "https://example.test/v1",
|
||||
"api_key": "***",
|
||||
"request_overrides": {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
},
|
||||
},
|
||||
)
|
||||
_install_fake_agent(monkeypatch)
|
||||
|
||||
import hermes_cli.tools_config as tools_config
|
||||
|
||||
monkeypatch.setattr(tools_config, "_get_platform_tools", lambda user_config, platform_key: {"core"})
|
||||
|
||||
runner = _make_runner()
|
||||
source = _make_source()
|
||||
session_key = "agent:main:feishu:dm:ou_test"
|
||||
|
||||
runner.session_store = SimpleNamespace(
|
||||
get_or_create_session=lambda _source: SimpleNamespace(session_id="session-1"),
|
||||
load_transcript=lambda _session_id: [],
|
||||
)
|
||||
|
||||
seen_agents = []
|
||||
orig_init = _CapturingAgent.__init__
|
||||
|
||||
def _tracking_init(self, *args, **kwargs):
|
||||
orig_init(self, *args, **kwargs)
|
||||
seen_agents.append(self)
|
||||
|
||||
monkeypatch.setattr(_CapturingAgent, "__init__", _tracking_init)
|
||||
|
||||
async def run_turn():
|
||||
return await runner._run_agent(
|
||||
message="hi",
|
||||
context_prompt="",
|
||||
history=[],
|
||||
source=source,
|
||||
session_id="session-1",
|
||||
session_key=session_key,
|
||||
)
|
||||
|
||||
# Turn 1: /fast active — provider extra_body AND service_tier both present.
|
||||
# The turn path re-resolves the tier per session, so stub the resolver.
|
||||
tier_box = {"tier": "priority"}
|
||||
runner._resolve_session_service_tier = lambda *a, **k: tier_box["tier"]
|
||||
with patch(
|
||||
"hermes_cli.models.resolve_fast_mode_overrides",
|
||||
return_value={"service_tier": "priority"},
|
||||
):
|
||||
result = await run_turn()
|
||||
assert result["final_response"] == "ok"
|
||||
assert len(seen_agents) == 1
|
||||
agent = seen_agents[0]
|
||||
assert agent.request_overrides == {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
"service_tier": "priority",
|
||||
}
|
||||
|
||||
# Turn 2: back to normal — the SAME cached agent must drop only the stale
|
||||
# fast-mode key; the init-time provider extra_body survives the refresh.
|
||||
tier_box["tier"] = None
|
||||
result = await run_turn()
|
||||
assert result["final_response"] == "ok"
|
||||
assert len(seen_agents) == 1, "agent should be reused from the gateway cache"
|
||||
assert agent.request_overrides == {
|
||||
"extra_body": {"text": {"verbosity": "low"}},
|
||||
}
|
||||
Reference in New Issue
Block a user