headroom/tests/test_responses_cross_turn_dedup.py

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

141 lines
4.9 KiB
Python
Raw Normal View History

fix(dedup): shorter fold pointer + MIN_LINES=3, dedup Anthropic list-… (#1932) …content tool_results, line-number reanchoring cross_turn_dedup: - Compact the fold pointer (~35c vs ~100c) and lower MIN_LINES=3 / MIN_CHARS=40 so the abundant short (2-4 line) re-read repeats pay off (~4.3% -> ~6% lossless observation-byte reduction on Opus SWE-bench, measured on 73 real trajectories). - Add uniform-offset line-number reanchoring: a re-read whose line numbers all shifted by a constant after an edit now still folds. Matching keys on the number-stripped content; the offset is carried in the pointer so recovery of the original numbered bytes stays exact (unpadded grep/sed/rg -n form only). Cache-safe (prefix-monotonic) and information-preserving (earliest copy kept verbatim). Small real-data gain, but closes the renumbering blind spot. content_router: - Dedup Anthropic list-content tool_result blocks (the single {"type":"text"} sub-block), which the string-only extraction path previously skipped entirely, so cross-turn dedup never fired on Anthropic-native tool results. ## Description <!-- Briefly explain the change and why it is needed. --> Closes # ## Type of Change - [ ] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - ## Testing <!-- Check what you actually ran, then paste the real command output below. --> - [ ] Unit tests pass (`pytest`) - [ ] Linting passes (`ruff check .`) - [ ] Type checking passes (`mypy headroom`) - [ ] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text # Paste relevant command output or artifact links here ``` ## Real Behavior Proof - Environment: - Exact command / steps: - Observed result: - Not tested: ## Review Readiness - [ ] I have performed a self-review - [ ] This PR is ready for human review ## Checklist - [ ] My code follows the project's style guidelines - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) Add screenshots to help explain your changes. ## Additional Notes <!-- Mention any N/A checklist items, tradeoffs, follow-ups, or maintainer context. --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:38:08 -04:00
"""Cross-turn dedup on the OpenAI Responses path (Codex ``function_call_output``).
Fixtures mirror a REAL Codex run captured through the headroom proxy: a file read
returns as ``{"type":"function_call_output","call_id":...,"output":"Chunk ID: …\\n
Wall time: \\nProcess exited with code 0\\nOriginal token count: \\nOutput:\\n
<FILE BODY>\\n"}``. The ``Chunk ID`` / ``Wall time`` header varies per call, so a
whole-block match never fires longest-span matching must fold the identical
body and leave the varying header verbatim.
"""
from __future__ import annotations
from headroom.proxy.handlers.openai import (
_RESPONSES_OUTPUT_ITEM_TYPES,
_dedup_responses_output_items,
)
BODY = (
"def paginate_orders(items, page, page_size):\n"
' """Return one page of orders."""\n'
" start = page * page_size\n"
" end = start + page_size + 1 # off-by-one: should be start + page_size\n"
" return items[start:end]\n"
"\n\n"
'SERVICE_TAG = "svc-03e8-tag"\n'
"\n\n"
"def compute_overdraft(business_id, amount):\n"
" fee = amount * 0.05\n"
' return {"business_id": business_id, "fee": fee, "tag": SERVICE_TAG}\n'
)
def _wrap(chunk_id: str, wall: str) -> str:
# Codex's exec_command wrapper — the header lines vary call to call.
return (
f"Chunk ID: {chunk_id}\n"
f"Wall time: {wall} seconds\n"
"Process exited with code 0\n"
"Original token count: 97\n"
"Output:\n"
)
def _read_output(call_id: str, chunk_id: str, wall: str) -> dict:
return {
"type": "function_call_output",
"call_id": call_id,
"output": _wrap(chunk_id, wall) + BODY,
}
def _read_call(call_id: str) -> dict:
return {
"type": "function_call",
"name": "exec_command",
"arguments": '{"cmd":"cat buggy.py","workdir":"/tmp"}',
"call_id": call_id,
}
def test_repeated_codex_read_folds_body_keeps_varying_header():
items = [
{"role": "user", "content": "find the bug"},
_read_call("c1"),
_read_output("c1", "492f0f", "0.0000"), # read #1 (reference)
{
"type": "message",
"role": "assistant",
"content": [{"type": "output_text", "text": "re-reading"}],
},
_read_call("c2"),
_read_output("c2", "a1b2c3", "0.0100"), # read #2 (duplicate) -> body folds
]
folded, saved = _dedup_responses_output_items(
items, _RESPONSES_OUTPUT_ITEM_TYPES, count_tokens=len
)
assert folded == 1
assert saved > 0
# earliest read: byte-identical (reference target, sits in the cached prefix)
assert items[2]["output"] == _wrap("492f0f", "0.0000") + BODY
# later read: identical body folded to a pointer; the per-call header stays verbatim
later = items[5]["output"]
assert "[↑" in later
assert later.startswith("Chunk ID: a1b2c3\nWall time: 0.0100")
assert "def paginate_orders" not in later # body folded away
# lossless: the folded body is still fully present earlier in the request
assert "def paginate_orders" in items[2]["output"]
def test_single_read_does_not_fold():
items = [_read_call("c1"), _read_output("c1", "492f0f", "0.0000")]
folded, saved = _dedup_responses_output_items(
items, _RESPONSES_OUTPUT_ITEM_TYPES, count_tokens=len
)
assert folded == 0 and saved == 0
assert items[1]["output"] == _wrap("492f0f", "0.0000") + BODY
fix(proxy): protect WebSearch/WebFetch tool results from lossy compression (#2115) ## Description `WebSearch` and `WebFetch` tool results can be large reference payloads whose exact formatting matters. This PR keeps those web-tool outputs verbatim through both the chat/router path and the OpenAI Responses path, including cross-turn dedup, while leaving ordinary compressible tools such as `Bash` unchanged by default. Closes #1810 ## Changes Made - Added `WebSearch`, `WebFetch`, `web_search`, and `web_fetch` to the default excluded tools. - Added a verbatim-only excluded-tool subset for web payloads so those outputs bypass lossy compression, lossless JSON rewriting, and cross-turn dedup folding. - Updated the OpenAI Responses adapter to track protected call IDs for verbatim web outputs. - Added regressions for Anthropic-style tool results, OpenAI Responses tool outputs, cross-turn dedup, and unchanged `Bash` compression behavior. - Merged current `main` and removed unrelated dependency floor changes from the PR diff. ## Testing ```text uv run --extra dev python -m pytest tests/test_websearch_tool_result_protection.py tests/test_content_router_exclude_tools.py tests/test_openai_responses_compression_units.py::test_openai_responses_adapter_keeps_websearch_output_verbatim tests/test_responses_cross_turn_dedup.py::test_protected_websearch_outputs_do_not_fold -q 13 passed uv run --extra dev mypy headroom/transforms/content_router.py headroom/proxy/handlers/openai.py Success: no issues found in 2 source files git diff --check headroomlabs/main...HEAD # no output ``` The local pre-commit hook also passed on the pushed cleanup/type-fix commit. ## Review Readiness - [x] Ready for review - [x] Regression tests added --------- Co-authored-by: JerrettDavis <mxjerrett@gmail.com>
2026-07-14 11:52:26 -04:00
def test_protected_websearch_outputs_do_not_fold():
items = [
{
"type": "function_call_output",
"call_id": "c1",
"output": '{\n "results": [\n {"title": "Headroom"}\n ]\n}',
},
{
"type": "function_call_output",
"call_id": "c2",
"output": '{\n "results": [\n {"title": "Headroom"}\n ]\n}',
},
]
folded, saved = _dedup_responses_output_items(
items,
_RESPONSES_OUTPUT_ITEM_TYPES,
count_tokens=len,
protected_call_ids={"c1", "c2"},
)
assert folded == 0
assert saved == 0
assert items[0]["output"].endswith('{"title": "Headroom"}\n ]\n}')
assert items[1]["output"].endswith('{"title": "Headroom"}\n ]\n}')
fix(dedup): shorter fold pointer + MIN_LINES=3, dedup Anthropic list-… (#1932) …content tool_results, line-number reanchoring cross_turn_dedup: - Compact the fold pointer (~35c vs ~100c) and lower MIN_LINES=3 / MIN_CHARS=40 so the abundant short (2-4 line) re-read repeats pay off (~4.3% -> ~6% lossless observation-byte reduction on Opus SWE-bench, measured on 73 real trajectories). - Add uniform-offset line-number reanchoring: a re-read whose line numbers all shifted by a constant after an edit now still folds. Matching keys on the number-stripped content; the offset is carried in the pointer so recovery of the original numbered bytes stays exact (unpadded grep/sed/rg -n form only). Cache-safe (prefix-monotonic) and information-preserving (earliest copy kept verbatim). Small real-data gain, but closes the renumbering blind spot. content_router: - Dedup Anthropic list-content tool_result blocks (the single {"type":"text"} sub-block), which the string-only extraction path previously skipped entirely, so cross-turn dedup never fired on Anthropic-native tool results. ## Description <!-- Briefly explain the change and why it is needed. --> Closes # ## Type of Change - [ ] Bug fix (non-breaking change that fixes an issue) - [ ] New feature (non-breaking change that adds functionality) - [ ] Breaking change (fix or feature that would cause existing functionality to change) - [ ] Documentation update - [ ] Performance improvement - [ ] Code refactoring (no functional changes) ## Changes Made - ## Testing <!-- Check what you actually ran, then paste the real command output below. --> - [ ] Unit tests pass (`pytest`) - [ ] Linting passes (`ruff check .`) - [ ] Type checking passes (`mypy headroom`) - [ ] New tests added for new functionality - [ ] Manual testing performed ### Test Output ```text # Paste relevant command output or artifact links here ``` ## Real Behavior Proof - Environment: - Exact command / steps: - Observed result: - Not tested: ## Review Readiness - [ ] I have performed a self-review - [ ] This PR is ready for human review ## Checklist - [ ] My code follows the project's style guidelines - [ ] I have performed a self-review of my code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] I have updated the CHANGELOG.md if applicable ## Screenshots (if applicable) Add screenshots to help explain your changes. ## Additional Notes <!-- Mention any N/A checklist items, tradeoffs, follow-ups, or maintainer context. --> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 11:38:08 -04:00
def test_non_output_items_untouched():
# A duplicated MESSAGE (not a tool output) must never fold — only output
# items are eligible.
msg = {"role": "user", "content": BODY}
items = [dict(msg), {"type": "message", "role": "assistant", "content": "ok"}, dict(msg)]
folded, _ = _dedup_responses_output_items(items, _RESPONSES_OUTPUT_ITEM_TYPES)
assert folded == 0
assert items[2]["content"] == BODY
def test_never_raises_on_malformed():
# Defensive: junk items must not blow up the request path.
items = [{"type": "function_call_output"}, {"type": "function_call_output", "output": None}, 42]
folded, saved = _dedup_responses_output_items(items, _RESPONSES_OUTPUT_ITEM_TYPES) # type: ignore[arg-type]
assert (folded, saved) == (0, 0)