Skip to content

gateway: hold a translated response's heap in the body budget; look up the walk's candidate once - #128

Merged
jaredLunde merged 3 commits into
mainfrom
gateway-candidate-once
Oct 4, 2026
Merged

jaredLunde merged 3 commits into
mainfrom
gateway-candidate-once

Conversation

@jaredLunde

@jaredLunde jaredLunde commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Summary

  • Translated responses now count against the body memory budget (D216). When a response is translated onto another wire, the gateway parses it into a serde_json::Value, just as it does for a translated request. For bodies made of many tiny objects, that costs up to ~250 bytes of heap per byte of JSON. Requests already reserve translate::translation_heap's estimate in the body budget before translating. Non-streaming responses were only limited by raw size (MAX_TRANSLATE_BUFFER, 32 MiB), so in the worst case one response could take several GB. They now make the same reservation at end of stream. If it doesn't fit, the response is aborted (its status line has already gone downstream), as translate_overflow already does. A same-wire 2xx is relayed as bytes and reserves nothing.
  • Cleanup: request_body_filter looked up a.candidate_at(a.candidate) six times in one block. It's now looked up once.

Realistic exposure is smaller than the 32 MiB cap implies. Catalog output limits keep a model's own answer to a few hundred MB of heap at worst. Reaching the multi-GB case needs a broken or hostile upstream.

Test plan

  • New body_memory::a_response_too_costly_to_translate_is_aborted: a 4 MiB Claude tool input of tiny objects, translated for a Chat client, is aborted and peak RSS stays within a few copies of the body. Without the fix it fails, because the response is translated in full.
  • cargo clippy -p beyond-ai --all-targets
  • cargo test -p beyond-ai (all 71 test binaries pass)

🤖 Generated with Claude Code

https://claude.ai/code/session_0152kQKDdP8XRhoeqisJYpWr

`request_body_filter` resolved `a.candidate_at(a.candidate)` six times in one block; the slot does
not move inside it, so bind it once.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152kQKDdP8XRhoeqisJYpWr
@jaredLunde
jaredLunde enabled auto-merge (squash) October 4, 2026 20:15
A non-streaming JSON response translated onto another wire builds the same `Value`s a request does,
up to ~250 bytes of heap per byte for many tiny objects, but only its raw size was capped (32 MiB).
It is now held in the body budget before it is mapped, as a request is; one that does not fit is
aborted, since its status line is already downstream.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0152kQKDdP8XRhoeqisJYpWr
@jaredLunde jaredLunde changed the title gateway: look up the walk's candidate once when shaping the body gateway: hold a translated response's heap in the body budget; look up the walk's candidate once Oct 4, 2026
@jaredLunde
jaredLunde disabled auto-merge October 4, 2026 20:56
@jaredLunde
jaredLunde enabled auto-merge (squash) October 4, 2026 20:56
@jaredLunde
jaredLunde merged commit 1f5edeb into main Oct 4, 2026
16 of 19 checks passed
@jaredLunde
jaredLunde deleted the gateway-candidate-once branch October 4, 2026 20:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant