mirror of
https://github.com/openswarm-ai/openswarm.git
synced 2026-09-11 04:07:44 +02:00
[eric] browser-agent: cap result summary+action-log (CLI hard-rejects >25K-token MCP results)
This commit is contained in:
@@ -190,6 +190,18 @@ def call_backend(tasks: list[dict]) -> dict:
|
||||
|
||||
|
||||
MAX_IMAGE_B64_BYTES = 400_000
|
||||
MAX_SUMMARY_CHARS = 16_000
|
||||
MAX_ACTION_LOG_ENTRIES = 40
|
||||
|
||||
|
||||
def p_cap_summary(text: str) -> str:
|
||||
"""Head+tail split: the CLI hard-rejects tool results past ~25K tokens, and a vanished report is worse than a trimmed one."""
|
||||
if len(text) <= MAX_SUMMARY_CHARS:
|
||||
return text
|
||||
head = text[: MAX_SUMMARY_CHARS - 4_000]
|
||||
tail = text[-3_500:]
|
||||
omitted = len(text) - len(head) - len(tail)
|
||||
return f"{head}\n\n[... {omitted} chars of the report omitted ...]\n\n{tail}"
|
||||
|
||||
|
||||
def p_sniff_image_mime(b64: str) -> str:
|
||||
@@ -233,12 +245,16 @@ def format_result(result: dict) -> dict:
|
||||
action_log = result.get("action_log", [])
|
||||
|
||||
lines = [f"**Browser Agent Result** (browser: {browser_id}, session: {session_id})", ""]
|
||||
lines.append(f"**Summary:** {summary}")
|
||||
lines.append(f"**Summary:** {p_cap_summary(summary)}")
|
||||
|
||||
if action_log:
|
||||
lines.append("")
|
||||
lines.append("**Actions taken:**")
|
||||
for i, entry in enumerate(action_log, 1):
|
||||
entries = action_log[-MAX_ACTION_LOG_ENTRIES:]
|
||||
omitted = len(action_log) - len(entries)
|
||||
if omitted > 0:
|
||||
lines.append(f" (... {omitted} earlier actions omitted ...)")
|
||||
for i, entry in enumerate(entries, omitted + 1):
|
||||
tool = entry.get("tool", "?")
|
||||
inp = entry.get("input", {})
|
||||
ms = entry.get("elapsed_ms", 0)
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
"""Browser-agent MCP result payload caps.
|
||||
|
||||
The bug: format_result forwarded the sub-agent's summary and full action log
|
||||
uncapped. The bundled Claude CLI rejects any MCP tool result past ~25K tokens
|
||||
(the model never sees the report at all), and repeated near-cap results were
|
||||
the refill mass behind the CLI's "Autocompact is thrashing" turn-killer seen
|
||||
on 1.5.4 installs.
|
||||
|
||||
The seal: summary is head+tail capped at MAX_SUMMARY_CHARS and the action log
|
||||
keeps only the last MAX_ACTION_LOG_ENTRIES entries, so one delegation result
|
||||
can never approach the CLI rejection threshold on the text side.
|
||||
"""
|
||||
|
||||
from backend.apps.agents.browser_agent_mcp_server import (
|
||||
MAX_ACTION_LOG_ENTRIES,
|
||||
MAX_SUMMARY_CHARS,
|
||||
format_result,
|
||||
)
|
||||
|
||||
|
||||
def result_text(result: dict) -> str:
|
||||
blocks = [b for b in result["content"] if b.get("type") == "text"]
|
||||
return "\n".join(b["text"] for b in blocks)
|
||||
|
||||
|
||||
def test_small_summary_passes_through_unchanged() -> None:
|
||||
text = result_text(format_result({"summary": "all done"}))
|
||||
assert "**Summary:** all done" in text
|
||||
assert "omitted" not in text
|
||||
|
||||
|
||||
def test_giant_summary_keeps_head_and_tail() -> None:
|
||||
summary = "HEADSTART " + ("x" * 60_000) + " TAILEND"
|
||||
text = result_text(format_result({"summary": summary}))
|
||||
assert len(text) < MAX_SUMMARY_CHARS + 300
|
||||
assert "HEADSTART" in text
|
||||
assert text.endswith("TAILEND")
|
||||
assert "omitted" in text
|
||||
|
||||
|
||||
def test_action_log_keeps_last_entries_with_original_numbering() -> None:
|
||||
log = [{"tool": f"Act{i}", "input": {}, "elapsed_ms": i} for i in range(100)]
|
||||
text = result_text(format_result({"summary": "ok", "action_log": log}))
|
||||
assert "(... 60 earlier actions omitted ...)" in text
|
||||
assert "Act59" not in text
|
||||
assert "61. Act60(" in text
|
||||
assert "100. Act99(" in text
|
||||
|
||||
|
||||
def test_short_action_log_has_no_omission_line() -> None:
|
||||
log = [{"tool": "Click", "input": {"x": 1}, "elapsed_ms": 5}]
|
||||
text = result_text(format_result({"summary": "ok", "action_log": log}))
|
||||
assert "omitted" not in text
|
||||
assert "1. Click(" in text
|
||||
|
||||
|
||||
def test_pathological_result_stays_far_under_cli_rejection_cap() -> None:
|
||||
log = [{"tool": "T", "input": {"v": "y" * 500}, "elapsed_ms": 1} for i in range(500)]
|
||||
out = format_result({"summary": "z" * 200_000, "action_log": log})
|
||||
total = len(result_text(out))
|
||||
assert total < MAX_SUMMARY_CHARS + MAX_ACTION_LOG_ENTRIES * 160 + 500
|
||||
|
||||
|
||||
def test_error_result_untouched() -> None:
|
||||
out = format_result({"error": "boom"})
|
||||
assert out["isError"] is True
|
||||
assert "boom" in result_text(out)
|
||||
Reference in New Issue
Block a user