mirror of
https://github.com/agent0ai/agent-zero.git
synced 2026-07-09 17:08:29 +00:00
Clear the active Responses provider continuation when automatic history compression or manual chat compaction rewrites local history, while preserving stored response IDs for cleanup. Add focused regressions for both compression paths so compacted chats do not keep stale provider-side context.
130 lines
3.2 KiB
Python
130 lines
3.2 KiB
Python
import sys
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
PROJECT_ROOT = Path(__file__).resolve().parents[1]
|
|
if str(PROJECT_ROOT) not in sys.path:
|
|
sys.path.insert(0, str(PROJECT_ROOT))
|
|
|
|
from extensions.python.message_loop_prompts_before._90_organize_history_wait import (
|
|
MAX_SYNC_COMPRESSION_PASSES,
|
|
OrganizeHistoryWait,
|
|
)
|
|
|
|
|
|
class _StalledHistory:
|
|
def __init__(self):
|
|
self.compress_calls = 0
|
|
|
|
def is_over_limit(self):
|
|
return self.compress_calls < 2
|
|
|
|
def get_tokens(self):
|
|
return 1234
|
|
|
|
async def compress(self):
|
|
self.compress_calls += 1
|
|
return False
|
|
|
|
|
|
class _MaxPassHistory:
|
|
def __init__(self):
|
|
self.compress_calls = 0
|
|
self.tokens = 2000
|
|
|
|
def is_over_limit(self):
|
|
return True
|
|
|
|
def get_tokens(self):
|
|
return self.tokens
|
|
|
|
async def compress(self):
|
|
self.compress_calls += 1
|
|
self.tokens -= 1
|
|
return True
|
|
|
|
|
|
class _CompressOnceHistory:
|
|
def __init__(self):
|
|
self.compress_calls = 0
|
|
self.tokens = 2000
|
|
|
|
def is_over_limit(self):
|
|
return self.compress_calls == 0
|
|
|
|
def get_tokens(self):
|
|
return self.tokens
|
|
|
|
async def compress(self):
|
|
self.compress_calls += 1
|
|
self.tokens -= 1000
|
|
return True
|
|
|
|
|
|
class _FakeLog:
|
|
def __init__(self):
|
|
self.entries = []
|
|
|
|
def set_progress(self, *args, **kwargs):
|
|
pass
|
|
|
|
def log(self, **kwargs):
|
|
self.entries.append(kwargs)
|
|
|
|
|
|
class _FakeAgent:
|
|
def __init__(self, history=None):
|
|
self.data = {}
|
|
self.history = history or _StalledHistory()
|
|
self.context = type("Context", (), {"log": _FakeLog()})()
|
|
|
|
def get_data(self, key):
|
|
return self.data.get(key)
|
|
|
|
def set_data(self, key, value):
|
|
self.data[key] = value
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_history_wait_stops_when_compression_makes_no_progress():
|
|
agent = _FakeAgent()
|
|
|
|
await OrganizeHistoryWait(agent).execute()
|
|
|
|
assert agent.history.compress_calls == 1
|
|
assert agent.context.log.entries
|
|
assert agent.context.log.entries[-1]["heading"] == "History compression stalled"
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_history_wait_stops_after_max_sync_compression_passes():
|
|
history = _MaxPassHistory()
|
|
agent = _FakeAgent(history)
|
|
|
|
await OrganizeHistoryWait(agent).execute()
|
|
|
|
assert history.compress_calls == MAX_SYNC_COMPRESSION_PASSES
|
|
assert agent.context.log.entries
|
|
assert agent.context.log.entries[-1]["heading"] == "History compression stalled"
|
|
assert (
|
|
f"stopped after {MAX_SYNC_COMPRESSION_PASSES} passes"
|
|
in agent.context.log.entries[-1]["content"]
|
|
)
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_history_compression_clears_active_responses_state():
|
|
agent = _FakeAgent(_CompressOnceHistory())
|
|
agent.data["responses_state"] = {
|
|
"response_id": "resp_current",
|
|
"previous_response_id": "resp_previous",
|
|
"response_ids": ["resp_previous", "resp_current"],
|
|
}
|
|
|
|
await OrganizeHistoryWait(agent).execute()
|
|
|
|
state = agent.data["responses_state"]
|
|
assert "response_id" not in state
|
|
assert "previous_response_id" not in state
|
|
assert state["response_ids"] == ["resp_previous", "resp_current"]
|