Repository navigation
Conversation
Metering used to hold the model's whole context window for every prompt. The gateway now sends an upper bound of the prompt's tokens: the bytes of the body sent upstream, plus 1,024 for what providers add, with each image counted as 16,384 tokens instead of its bytes. Tokenizers never produce more tokens than UTF-8 bytes, so the bound holds for text. Requests with audio, video, files or unknown content parts, and SystemOne requests, send no estimate, and Metering holds the full context window as before. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
Metering held every request's prompt as the model's full context window: about €2 of input hold per request on a 1M-context model, even for a short chat. With this PR the gateway tells Metering an upper bound of the real prompt. Metering side and full rules: dappnode/dappnode-nexus#151 (
docs/METERING_HOLDS.md).The estimate, exactly
apps/gateway/internal/application/services/input_estimate.go, computed on the body after PII masking (what goes upstream):text,input_text,output_text,refusal) and string/null content count as bytes.image_url,image,input_image. Inline base64 counts as one image, not megabytes./v1/systemone(its provider builds its own prompt). Metering then holds the full context window, as today.Why it's an upper bound: byte-level BPE and SentencePiece-with-byte-fallback tokenizers never produce more tokens than UTF-8 bytes, and the JSON body contains every string at least once, plus syntax. Measured on adversarial text with cl100k, o200k and the Kimi K2 tokenizer, the worst case was 0.95 tokens/byte (random Unicode). English is about 0.22, code 0.44, CJK 0.13–0.33.
A low estimate can't cost Nexus money: Metering charges real usage, and anything uncovered becomes account debt (see #151).
Changes
domain.GenerateRequest.EstimatedInputTokens, set inProxybeforeReserve.estimated_input_tokens(omitted when unknown).Testing
Proxypasses the estimate toReserve, and passes none for audio.estimated_input_tokensandmax_output_tokenson the wire, and omits the estimate when nil.go test ./...passes;GOOS=linux go build -tags nitro_enclave ./...builds.Deploy
After dappnode/dappnode-nexus#151 (its migration must run first), though either order is API-safe. Old metering ignores the field. TEE enclave builds pick this up on their next release.
🤖 Generated with Claude Code