Skip to content

The token ceiling is ours, and a cut reply says so - #786

Merged
WaylandYang merged 2 commits into
devfrom
land/token-ceiling
Sep 19, 2026
Merged

WaylandYang merged 2 commits into
devfrom
land/token-ceiling

Conversation

@WaylandYang

Copy link
Copy Markdown
Contributor

basil-k-aji-dev's #776, rebased onto dev after #764 turned the streaming buffer into bytes, with the number from the review changed. Fixes #760.

Their change, unaltered. The streamed calls send an explicit max_tokens instead of accepting whatever the endpoint defaults to, and chat_at_streaming returns Reply { text, finish_reason } so a reply cut at the ceiling can say so. extraction_open uses it: a chunk that hit the ceiling gets a drop row that says it, instead of one that says only that the reply would not parse. An answer that is too long is a number to change; a reply that does not fit the shape is not.

Both halves were verified to fail on their own. Removing the field:

assertion `left == right` failed: 不送这个字段,答案断在哪里就是端点的默认值说了算
  left: Null
 right: Number(...)

and deleting the one line that records the reason fails the same test from the other side.

What changed: 16,384 became 65,536. The original number was four times the longest answer, which is safe on this endpoint because it ignores the field with reasoning on. But the field now rides every streamed call, and max_tokens is not one thing: Anthropic counts thinking inside it, OpenAI's max_completion_tokens counts reasoning tokens. On an endpoint that counts reasoning, a ceiling sized for the answer is a budget for the thinking — which is exactly the lever #759 measured and rejected, where holding this model to about 16,000 reasoning tokens cost 885 statements against 729 and took table figures reaching no statement from 2% to 16%.

The largest completion measured on this path is 45,428 tokens. 65,536 sits above it, so the field can only ever guard against the endpoint's own 4,096 default and can never quietly become the cap we turned down. The constant's comment and the design page both say which quantity the number is measured against, because "four times the longest answer" would not lead a reader to expect it to bound the thinking somewhere else.

One test needed adapting to the rebase: #764's Unicode assertion now reads answer.text.

Their branch could not be rebased from here, so this carries their commit with authorship intact and #776 closes with it.

27 utopia-llm tests pass, clippy across utopia-llm and utopia-server is clean, fmt clean.

🤖 Generated with Claude Code

basil-k-aji-dev and others added 2 commits September 19, 2026 16:01
Extraction never sent `max_tokens`, so where a long answer stopped was decided
by the endpoint's own default: with reasoning off, a dense passage's answer
stopped at exactly 4,096 completion tokens with `finish_reason: length`, and
that JSON could not be parsed.

Send an explicit 16,384 on the streamed calls — four times the longest answer
measured on this path. This endpoint ignores the field with reasoning on, where
a call capped at 4,096 returned 45,428 completion tokens and finished normally;
it honours it with reasoning off, which is where the truncation was seen. The
field costs nothing where it is not read.

Carry `finish_reason` out with the answer rather than dropping it once the
stream is known to have ended. A reply cut at the ceiling is a valid string and
looks complete to the parser, so the drop row could only say the reply did not
parse. It now says the reply hit the token ceiling, and the two causes stay
apart: an answer that is too long is a number to change, a reply that does not
fit the shape is not.

Fixes #760

Signed-off-by: basil-k-aji-dev <70605804+basil-k-aji-dev@users.noreply.github.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: WaylandYang <wayland0916@gmail.com>
@WaylandYang
WaylandYang merged commit 4960e78 into dev Sep 19, 2026
4 checks passed
@WaylandYang
WaylandYang deleted the land/token-ceiling branch September 19, 2026 08:08
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.

The endpoint decides where a long answer stops

2 participants