Files
openworker/tests/test_compaction_smoke.py
T
Devika Verma 0bf9b87800 compaction: repeated-compaction smoke through the manager (OPE-27 4/4)
A long multi-turn session driven through the real SessionManager with a
forced 3k-token cap: repeated compactions advance the boundary, later
summaries fold the previous one in, the provider verifiably receives the
compacted view (summary block + verbatim tail, bounded) while the
canonical transcript keeps every turn, state survives a mid-conversation
rebuild, and the persisted record round-trips the final boundary.

Scripted stand-in for the live-model smoke: intent survival across a
real summarizer (prompt tuning) still needs a configured provider key.
2026-07-29 16:23:26 +05:30

101 lines
4.6 KiB
Python

"""OPE-27 smoke (4/4) — a long multi-turn session driven through the real SessionManager
across REPEATED forced compactions: the provider must actually receive the compacted
view (summary block + verbatim tail), user intent must survive every compaction, and the
state must survive a save/rebuild mid-conversation. This is the scripted stand-in for
the live-model smoke (which needs a configured provider key)."""
import json
import asyncio
from coworker.providers import AssistantTurn, ModelCapabilities, ProviderClient
from coworker.providers.base import TokenUsage
from coworker.server.manager import SessionManager
BULK = "analysis paragraph " * 400 # ~7.6k chars (~1.9k tokens) per turn → triggers by turn 2
class LongSessionProvider(ProviderClient):
"""Main turns: bulky text answers with realistic (growing) usage reporting.
Summarizer turns: a structured summary echoing the required sections."""
def __init__(self):
self.main_messages_seen: list[list[dict]] = []
self.summary_prompts: list[str] = []
def complete(self, *, model, messages, tools=None, **settings):
if messages and "compacting an AI coworker" in str(
messages[0].get("content", "")
):
self.summary_prompts.append(str(messages[1]["content"]))
return AssistantTurn(
text=(
"## Primary request and intent\nBuild the Q3 report; never email "
"it without approval.\n## Current work\nDrafting section "
f"{len(self.summary_prompts)}.\n## Next step\nContinue drafting."
),
finish_reason="stop",
)
self.main_messages_seen.append([dict(m) for m in messages])
# Usage mirrors the outbound size (chars/4), like a real provider would bill it.
prompt_tokens = sum(len(json.dumps(m, default=str)) for m in messages) // 4
return AssistantTurn(
text=f"turn {len(self.main_messages_seen)}: {BULK}",
finish_reason="stop",
usage=TokenUsage(input=prompt_tokens, output=500),
)
def capabilities(self, model):
return ModelCapabilities()
def test_long_session_survives_repeated_compaction(tmp_path):
provider = LongSessionProvider()
mgr = SessionManager(workspace=tmp_path, provider=provider)
# Force tiny windows straight through the real Settings plumbing.
mgr._prefs["compaction_cap_tokens"] = 3_000
sid = "smoke-long"
async def drive(engine, text):
async for _ in engine.run(text):
pass
engine = mgr.get_engine(sid, agent="cowork", workspace=str(tmp_path))
boundaries = []
for i in range(8):
asyncio.run(drive(engine, f"user step {i}: keep drafting the Q3 report"))
if engine.compaction_state is not None:
if not boundaries or engine.compaction_state.boundary_index != boundaries[-1]:
boundaries.append(engine.compaction_state.boundary_index)
mgr.save(sid, engine)
if i == 4: # mid-conversation restart: state must survive the rebuild
mgr._engines.pop(sid)
engine = mgr.get_engine(sid, agent="cowork", workspace=str(tmp_path))
assert engine.compaction_state is not None
# Repeated compaction actually happened, moving forward each time.
assert len(boundaries) >= 2
assert boundaries == sorted(boundaries)
# Later summarizer calls fold the previous summary in (summary is message zero).
assert any("previous compaction summary" in p for p in provider.summary_prompts)
# What the MODEL actually received after the last compaction: the block + the tail,
# bounded — not the whole ever-growing canonical history.
final_view = provider.main_messages_seen[-1]
assert final_view[0]["role"] == "system"
block = final_view[1]["content"]
assert "<compacted-history>" in block
assert "Q3 report" in block # the summary carries the intent
assert "user step 0" in block # mechanical user-message preservation, from turn 0
assert "do not recap" in block # the continuation contract
assert len(final_view) < len(engine.messages)
# Canonical transcript: untouched (every turn still present) + the divider notices.
texts = [str(m.get("content", "")) for m in engine.messages]
assert all(any(f"user step {i}" in t for t in texts) for i in range(8))
assert sum(1 for m in engine.messages if m.get("kind") == "compacted") >= 2
# The persisted record round-trips the final state.
record = mgr.session_store.load(sid)
assert record.compaction["boundary_index"] == engine.compaction_state.boundary_index