fix(server): a reply gets the context its prompt leaves instead of a 400

Merged
DA
2d ago
+45-6

Clients send their usual output cap whatever the prompt: Claude Code asks for 32000 tokens on every turn. Once prompt + max_tokens passed the context, the server refused the request with a 400. With a 131072-token context and the model cards' 32768 default, a session stopped working at about 99K tokens of prompt, long before the context was full.

What changes

  • Direct requests: max_tokens is trimmed to the context the prompt leaves (fit_reply_to_context in admission.h), as llama.cpp does with n_predict. The server logs the trim.
  • A prompt that fills the context is still refused with a 400.
  • Routed requests keep the old rule, so the router can still send the request whole to a model with room for it.
  • Thinking budgets follow the trimmed cap.

Measured

Qwen 3.8 27B on an R9700, launched with the model card's 32768 default instead of a fixed 4096:

RequestBeforeAfter
108,802-token prompt, max_tokens 32000400200, cap trimmed to 22,270
reasoning_effort: mediumno thinking under a 4096 cap218 reasoning tokens
Messages API, thinking.budget_tokens 2048no thinking under a 4096 capthinking block, then the answer

The last two rows depend on the launcher dropping its fixed 4096 cap. This change is what makes dropping it safe for long prompts.

Tests

test_admission: a new case covers the trim and the refusal of a prompt that fills the context. test_server_unit passes on a HIP build.

🤖 Generated with Claude Code