Commit graph

2 commits

Author SHA1 Message Date
Tejas Chopra
3145242645
Unify savings attribution across stats, perf, metrics, and dashboard (#2976)
## Summary

Adds a small provider-neutral savings attribution seam. Named sources
can attach realized or projected token/USD deltas to a request without
changing headline arithmetic or introducing private-package inventory
into OSS.

Also fixes the Anthropic buffered lifecycle so normal successful
responses run response hooks, applies stream-safety filtering, includes
tool savings in per-model perf totals, and surfaces the same breakdown
in request logs, `/stats`, `headroom perf`, Prometheus, OTEL, and the
dashboard.

## Why

Request-local savings were split between canonical token deltas,
process-global extension counters, and tool-only tags. This made correct
headline totals possible while losing attribution in perf, recent
requests, metrics, and the dashboard. Normal Anthropic responses also
skipped response hooks unless CCR ran.

## Validation

- 74 focused tests passed: turn hooks, OpenAI hook lifecycle, outcome
funnel, perf formats, and tool-search repair
- Ruff passes on all changed Python files
- Existing compression-observability suite: 11 passed; 2 tokenizer-cache
tests require network access to fetch the tiktoken vocabulary

## Compatibility

No named private packages or private inventory are encoded in OSS.
Existing hooks remain source-compatible because all new TurnContext
fields are optional.

---------

Co-authored-by: Tejas Chopra <tejas@Tejass-MacBook-Pro.local>
2026-08-13 17:13:23 -07:00
Rod Boev
54ea28d983
fix(openai): skip Responses tool-search deferral for clients that cannot execute it (#2696)
## Description

OpenCode rejects the proxy-injected Responses `tool_search` tool because
it resolves tool calls against its local registry. This PR now uses the
shared client policy from current `main` and leaves OpenCode tools
resident, alongside the existing Codex exclusion. Other clients retain
tool-search deferral.

Closes #2660.

## Type of Change

- [x] Bug fix (non-breaking change that fixes an issue)
- [ ] Documentation update

## Changes Made

- Add `opencode` to the shared exact-match unsupported-client set in
`headroom.proxy.helpers`.
- Carry the already-classified `client` through native HTTP, WebSocket,
and custom-base Responses paths.
- Preserve `main`'s compatibility loop, which retries only exact
unsupported `client` or `timing` keyword errors and re-raises internal
`TypeError`s.
- Add focused helper, compressor, HTTP, passthrough, and WebSocket
coverage.

## Testing

- [x] Unit tests pass
- [x] Ruff check and format pass
- [x] New tests added
- [ ] Live OpenCode session tested

```text
uv run --extra dev pytest tests/test_openai_tool_search_deferral.py tests/test_proxy_openai.py -q
57 passed

uv run --extra dev ruff check headroom/proxy/handlers/openai.py headroom/proxy/helpers.py tests/test_openai_tool_search_deferral.py tests/test_proxy_openai.py
All checks passed
```

## Real Behavior Proof

The focused route tests classify OpenCode from both `User-Agent` and
`X-Client`, verify its tools remain untouched, and verify the decision
reaches all three Responses ingresses. Supported clients continue to
receive deferral. Codex remains excluded by the policy already on
`main`.

Not tested: a live OpenCode instance; the incompatibility itself remains
based on the reporter's reproduction in #2660.

## Review Readiness

- [x] Updated from current upstream `main`
- [x] Merge conflicts resolved
- [x] Focused tests pass locally
- [x] Ready for human review

## Additional Notes

No user configuration or documentation change is required. Vercel
authorization failures are external integration noise, not a source
check.

---------

Co-authored-by: JD Davis <jd@jds-macbook-air.tail2a279.ts.net>
2026-08-08 01:32:54 -05:00