mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-08-22 04:32:22 +02:00
[eric] agents: a REPEAT silent quit compacts from a 40% floor; Haik's opus-5 quits at ~149K, under the 180K trigger, so the band fix alone missed his storms (ENG-354)
This commit is contained in:
@@ -139,6 +139,8 @@ class AgentSession(BaseModel):
|
||||
pending_continuation_toolless: bool = False
|
||||
# Silent-quit nudges spent since the user's last real message; hard-capped so an agent that keeps ending empty can't loop.
|
||||
empty_finish_nudges: int = 0
|
||||
# Lifetime silent-quit count for the session; unlike the per-user-message nudge counter this never resets, so a REPEAT quit is distinguishable from a first (Haik's storm sessions logged 130+ quits each).
|
||||
empty_finish_total: int = 0
|
||||
# One transparent expired-token retry per user ask; the second failure earns the honest banner (ENG-294).
|
||||
auth_retry_used: bool = False
|
||||
# Tool-call count at the last nudge: a re-nudge is only earned by NEW tool work since then.
|
||||
|
||||
@@ -57,11 +57,20 @@ def maybe_nudge_empty_finish(session: AgentSession, session_id: str) -> bool:
|
||||
session.empty_finish_progress_mark = p_tool_calls
|
||||
# At high context the silent quit is usually the model choking on the prompt itself, so
|
||||
# re-sending the same bloat just burns a nudge; compact FIRST and retry distilled (ENG-354).
|
||||
# Field data (Haik, 2026-08-19): opus-5 quits from ~149K, BELOW the 180K trigger, and his storm
|
||||
# sessions logged 130+ quits each; a REPEAT quit therefore compacts from a much lower floor,
|
||||
# because one failed nudge is proof the prompt itself is what the model is choking on.
|
||||
import os as p_os
|
||||
from backend.apps.agents.manager.context_budget import compact_trigger_tokens, maybe_compact
|
||||
p_input = int(session.tokens.get("input", 0) or 0)
|
||||
if p_input >= int(0.8 * compact_trigger_tokens(session)) and maybe_compact(session, force=True):
|
||||
p_repeat = getattr(session, "empty_finish_total", 0) >= 1
|
||||
session.empty_finish_total = getattr(session, "empty_finish_total", 0) + 1
|
||||
p_floor = int((0.4 if p_repeat else 0.8) * compact_trigger_tokens(session))
|
||||
# Drill seam: the ENG-354 negative control runs the exp.14 behavior (no compact) on identical bits.
|
||||
p_disabled = p_os.environ.get("OSW_DISABLE_EMPTY_FINISH_COMPACT") == "1"
|
||||
if not p_disabled and p_input >= p_floor and maybe_compact(session, force=True):
|
||||
session.needs_fresh_session = True
|
||||
logger.warning(f"Agent {session_id}: empty finish at {p_input} input tokens; compacted history before the nudge")
|
||||
logger.warning(f"Agent {session_id}: empty finish at {p_input} input tokens (repeat={p_repeat}); compacted history before the nudge")
|
||||
session.empty_finish_nudges += 1
|
||||
session.pending_continuation = True
|
||||
p_final = session.empty_finish_nudges >= NUDGE_HARD_CAP
|
||||
|
||||
@@ -163,3 +163,42 @@ def test_low_context_empty_finish_nudges_without_compacting():
|
||||
assert maybe_nudge_empty_finish(s, "sid-low") is True
|
||||
assert s.compacted_through_msg_id is None
|
||||
assert getattr(s, "needs_fresh_session", False) is False
|
||||
|
||||
|
||||
def test_repeat_quit_compacts_from_the_low_floor():
|
||||
"""ENG-354 field data: opus-5 quits at ~149K, BELOW the 180K trigger; a repeat quit must compact anyway."""
|
||||
from backend.apps.agents.manager.run.empty_finish import maybe_nudge_empty_finish
|
||||
s = p_session(("user", "long task"),
|
||||
("tool_call", {"tool": "Read", "input": {"file_path": "/a"}}),
|
||||
("tool_result", {"text": "x"}),
|
||||
("tool_call", {"tool": "Read", "input": {"file_path": "/b"}}),
|
||||
("tool_result", {"text": "y"}),
|
||||
("tool_call", {"tool": "Read", "input": {"file_path": "/c"}}),
|
||||
("tool_result", {"text": "z"}))
|
||||
s.context_window = 1_000_000
|
||||
s.tokens["input"] = 100_000 # below the 144K first-quit floor, above the 72K repeat floor
|
||||
assert maybe_nudge_empty_finish(s, "sid-r1") is True
|
||||
assert s.compacted_through_msg_id is None, "first quit below the band must NOT compact"
|
||||
# New user message resets the per-message counter but not the lifetime one; the loop consumed the pending continuation.
|
||||
s.pending_continuation = False
|
||||
s.empty_finish_nudges = 0
|
||||
s.empty_finish_progress_mark = 0
|
||||
assert maybe_nudge_empty_finish(s, "sid-r2") is True
|
||||
assert s.compacted_through_msg_id is not None, "repeat quit must compact from the low floor"
|
||||
assert s.needs_fresh_session is True
|
||||
|
||||
|
||||
def test_drill_seam_disables_compaction(monkeypatch):
|
||||
from backend.apps.agents.manager.run.empty_finish import maybe_nudge_empty_finish
|
||||
monkeypatch.setenv("OSW_DISABLE_EMPTY_FINISH_COMPACT", "1")
|
||||
s = p_session(("user", "t"),
|
||||
("tool_call", {"tool": "Read", "input": {}}),
|
||||
("tool_result", {"text": "x"}),
|
||||
("tool_call", {"tool": "Read", "input": {}}),
|
||||
("tool_result", {"text": "y"}),
|
||||
("tool_call", {"tool": "Read", "input": {}}),
|
||||
("tool_result", {"text": "z"}))
|
||||
s.context_window = 1_000_000
|
||||
s.tokens["input"] = 190_000
|
||||
assert maybe_nudge_empty_finish(s, "sid-seam") is True
|
||||
assert s.compacted_through_msg_id is None
|
||||
|
||||
Reference in New Issue
Block a user