Files
openworker/tests/test_edit_tool_guidance.py
Devika Verma 65ba394393 Bound tool results, steer edits, keep compacted turns readable (OPE-186)
Findings from a long multi-task Kimi K3 session: ten long tasks used 57% of
451 M tokens, tool results were 31% of the final context and whole-file
write_file bodies 14%, and compaction fired once. Three harness changes:

1. Tool-result cap. Every tool result is bounded on arrival at 10,000 bytes,
   head + tail, with a marker naming the spill file that holds the full text
   (coworker/toolresult.py; engine `_record_result`; config
   `tool_result_max_bytes`, env COWORKER_TOOL_RESULT_MAX_BYTES, 0 = off).
   The spill folder is the session's scratch root when there is one, else it
   joins the session's directories read-only so read_file can open it.
   shell.py's own 20k-char tail cut becomes a 2M safety net.

2. Edit steering. write_file, replace_in_file, apply_patch and
   apply_unified_diff describe WHEN to use each (catalog.EDIT_TOOL_GUIDANCE),
   and the Cowork prompt says to edit in place rather than rewrite.

3. Compaction. `compaction_cap_tokens` (config / env
   COWORKER_COMPACTION_CAP_TOKENS) lowers the trigger; each compaction writes
   the verbatim transcript of the compacted turns (no reasoning) to the spill
   folder and the compacted block names the file (CompactionState.transcript_path,
   compaction.render_transcript).

Trial on 15 long tasks x 3 attempts (Kimi K3 via Together, cap 60,000):
33/45 passed vs 54/75 for the unchanged harness on the same tasks, tokens per
attempt 3.30 M -> 1.40 M, cost down 44%. Observed and left for a
follow-up: Kimi's summariser reply lands mostly in reasoning within the
3,000-token summary budget (7 proper summaries in 103 calls), and the trial had
16 timeouts vs 9 in the baseline.

Tests: tests/test_tool_result_cap.py, tests/test_edit_tool_guidance.py,
tests/test_compaction_transcript.py; full suite unchanged against the Windows
baseline.
2026-09-15 15:09:47 +05:30

43 lines
1.8 KiB
Python

"""OPE-186 change 2: the file tools tell the model WHEN to use each (rewrite vs edit in
place), and the Cowork prompt says the same. The schema the model sees is built from the
tool's docstring by the registry, so that is what is checked here."""
from __future__ import annotations
from coworker.agents import AgentContext
from coworker.agents.cowork import COWORK_INSTRUCTIONS
from coworker.catalog import EDIT_TOOL_GUIDANCE, expand
from coworker.tools import ToolRegistry
def _descriptions(capability: str, tmp_path) -> dict[str, str]:
context = AgentContext(workspace=str(tmp_path), executor=None, todo=None, roots=None)
registry = ToolRegistry()
registry.register_all(expand([capability], context))
return {
name: registry.get(name).schema["function"]["description"] # type: ignore[union-attr]
for name in registry.names()
}
def test_files_capability_describes_when_to_rewrite_and_when_to_edit(tmp_path):
d = _descriptions("files", tmp_path)
assert "NEW file" in d["write_file"] and "replace_in_file" in d["write_file"]
assert "in place" in d["replace_in_file"] and "Prefer this over write_file" in d["replace_in_file"]
assert "*** Begin Patch" in d["apply_patch"]
assert "unified diff" in d["apply_unified_diff"]
# The generic aisuite one-liner is gone from every edit tool.
for name in EDIT_TOOL_GUIDANCE:
assert d[name] != "Write a UTF-8 text file under the configured root."
def test_code_files_capability_gets_the_same_guidance(tmp_path):
d = _descriptions("code_files", tmp_path)
for name, text in EDIT_TOOL_GUIDANCE.items():
assert d[name] == text
def test_cowork_prompt_steers_edits_to_in_place_tools():
assert "edit it in place with replace_in_file or apply_patch" in COWORK_INSTRUCTIONS
assert "reserve write_file for new files or full rewrites" in COWORK_INSTRUCTIONS