Commit a6d4921
authored
feat(proxy/hooks): run fold-only (stream-safe) turn hooks on streaming OpenAI chat (headroomlabs-ai#2549)
## Description
Fixes the last harness gap in the turn-hook seam (the "B4" finding from
the savings audit). The OpenAI chat handler gated hooks on `not stream`,
so **streamed** `/v1/chat/completions` requests ran **no** turn hooks —
the lossless-guard plugin's on_request fold and tool-schema shrink were
skipped, unlike the Anthropic path (hooks run unconditionally). Affects
opencode / Cursor / older OpenAI SDKs / some Copilot flows; **not**
Claude Code (Anthropic path).
The gate existed for a real reason: hooks that **re-drive** the model in
`on_response` (defer a tool, reload it when asked) can't run mid-stream.
But an **on_request fold** mutates the outbound request before the send
— safe on a stream.
## Change
- Add an opt-in `stream_safe` hook attribute (fold-only hooks set it).
`run_request_hooks(ctx, stream_safe_only=…)` filters to stream-safe
hooks when set.
- OpenAI chat handler runs `on_request` on streaming with
`stream_safe_only=stream`; buffered runs all hooks; the `on_response`
re-drive (buffered response path) is untouched.
- **Default off = conservative:** a hook is buffered-only unless it
declares `stream_safe`, so **no behavior change** until a hook opts in.
## Type of Change
- [x] Bug fix / feature (opt-in, backward-compatible)
## Testing
```text
pytest tests/test_turn_hooks.py tests/test_openai_chat_turn_hooks.py -q → 25 passed
ruff + mypy → clean
```
New test pins the filter: streaming runs only stream-safe hooks'
on_request; buffered runs all.
## Notes
The companion plugin PR (headroom-lossless-guard) sets `stream_safe =
True` on its fold-only hook to actually claim the streaming savings.
Anthropic path already ran hooks on streaming, so it's unaffected.
## Checklist
- [x] Self-reviewed; tests pass; no CHANGELOG edit1 parent c990cfb commit a6d4921
3 files changed
Lines changed: 59 additions & 8 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
3471 | 3471 | | |
3472 | 3472 | | |
3473 | 3473 | | |
3474 | | - | |
3475 | | - | |
3476 | | - | |
3477 | | - | |
| 3474 | + | |
| 3475 | + | |
| 3476 | + | |
| 3477 | + | |
| 3478 | + | |
| 3479 | + | |
3478 | 3480 | | |
3479 | 3481 | | |
3480 | 3482 | | |
3481 | 3483 | | |
3482 | 3484 | | |
3483 | 3485 | | |
3484 | | - | |
| 3486 | + | |
3485 | 3487 | | |
3486 | 3488 | | |
3487 | 3489 | | |
| |||
3502 | 3504 | | |
3503 | 3505 | | |
3504 | 3506 | | |
3505 | | - | |
| 3507 | + | |
3506 | 3508 | | |
3507 | 3509 | | |
3508 | 3510 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
56 | 56 | | |
57 | 57 | | |
58 | 58 | | |
| 59 | + | |
| 60 | + | |
| 61 | + | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
59 | 65 | | |
60 | 66 | | |
61 | 67 | | |
| |||
86 | 92 | | |
87 | 93 | | |
88 | 94 | | |
89 | | - | |
90 | | - | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
| 98 | + | |
| 99 | + | |
| 100 | + | |
| 101 | + | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
91 | 108 | | |
| 109 | + | |
| 110 | + | |
92 | 111 | | |
93 | 112 | | |
94 | 113 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
| 62 | + | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
| 84 | + | |
| 85 | + | |
| 86 | + | |
| 87 | + | |
| 88 | + | |
| 89 | + | |
| 90 | + | |
| 91 | + | |
62 | 92 | | |
63 | 93 | | |
64 | 94 | | |
| |||
0 commit comments