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)
|