fix: preserve exploration tool history and cap context budget

This commit is contained in:
chenmingxuan committed 2026-10-16 19:05:35 +08:00
1 parent ee256ad7ce
commit 621f30e437
17 files changed
+211 -23

No files matched your search

+62 -1
View File
@@ -30,7 +30,7 @@ from types import SimpleNamespace
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, ToolMessage
from artemis.agents.operator.operator import OperatorNode
from artemis.agents.operator.prompts import load_operator_prompts
@@ -141,6 +141,67 @@ def _transcript_state(**overrides):
return state
@pytest.mark.asyncio
@pytest.mark.parametrize(
"exploration,plugins", [(False, False), (True, False), (False, True), (True, True)]
)
async def test_injected_prefix_and_compression_preserve_tool_pairs(
monkeypatch, exploration, plugins
):
from langchain_openai import ChatOpenAI
ctx = _transcript_ctx()
ctx.exploration_id = "test-exploration" if exploration else None
ctx.plugins = SimpleNamespace(prompt=lambda: "Plugin instructions") if plugins else None
monkeypatch.setattr(
"artemis.exploration.runtime.exploration_messages",
lambda _: [
SystemMessage(content="Exploration instructions"),
HumanMessage(content="User guidance"),
],
)
monkeypatch.setattr("artemis.agents.operator.operator.get_llm", lambda **_: MagicMock())
node = OperatorNode(ctx, transcript_config=MemoryTranscriptConfig(enabled=True))
requests = []
wire_model = ChatOpenAI(model="test", api_key="test", use_responses_api=True, store=False)
async def tool_loop(current_messages, **_kwargs):
messages = current_messages
payload = wire_model._get_request_payload(messages)
requests.append(payload["input"])
call_id = f"call_{len(requests)}"
messages.extend(
[
AIMessage(
content="", tool_calls=[{"name": "click", "args": {"target": 1}, "id": call_id}]
),
ToolMessage(content="Action Recorded", tool_call_id=call_id),
]
)
return [{"action": "tap"}], None, None, False
monkeypatch.setattr(node, "_invoke_llm_loop", tool_loop)
for turn in range(8):
await node(_transcript_state(current_step_id=f"step-{turn}"))
if turn == 4:
ctx.transcript_ledger.freeze_turns(2, [HumanMessage(content="Summarized first turns")])
for items in requests:
pending, seen = set(), set()
for item in items:
if item.get("type") == "function_call":
assert item["call_id"] not in seen
pending.add(item["call_id"])
seen.add(item["call_id"])
elif item.get("type") == "function_call_output":
assert item["call_id"] in pending
pending.remove(item["call_id"])
assert not pending
assert {item["call_id"] for item in requests[-1] if item.get("type") == "function_call"} == {
f"call_{n}" for n in range(3, 8)
}
def _action_llm(captured: list):
mock_llm = MagicMock()
mock_response = MagicMock()