Skip to content

GLM-5.2 vLLM Thinking Budget: Keep a Final Answer

Independent research — not an official Z.ai publication.Identity and provider disclosure

A cold-blue technical diagram shows a GLM-5.2 vLLM completion rail split into indigo reasoning tokens, one bright cyan reasoning-end marker, a teal final-answer reserve, and a gray stop token; a red unsafe path fills the rail with reasoning and fails before structured output, while a green safe path ends thinking early and reaches a valid tool-call node

Original editorial diagram. It explains capacity and parser gates; it is not a vLLM screenshot, model trace, benchmark, or proof that a particular budget is correct for your prompts.

A short GLM-5.2 request can end with finish_reason: "length", a full completion allowance, and no visible answer when thinking consumes the whole output. Raising max_tokens can postpone that failure, but it does not create an independent ceiling for reasoning. Disabling thinking can work for a simple dispatch task, but it also changes the model behavior being evaluated.

vLLM exposes a third option: cap only the thinking span. Its current documentation says token counting starts after the reasoning-start marker and, at the configured limit, vLLM forces the reasoning-end marker. That mechanism can preserve capacity for prose or a tool call. It cannot guarantee that the remaining tokens contain a correct answer, a valid call, or a natural stop.

This guide pins the official GLM-5.2 checkpoint at b4734de4…, the vLLM v0.28.0 tag at 2cf0a691…, and the official GLM recipe at 5943215a…. The machine-readable evidence receipt hashes 21 public artifacts, verifies eight input-value cases, reconstructs one reported 87-token layout, and calculates seven answer-reserve cells.

  1. Separate the three output controls
  2. Start from a pinned vLLM launch
  3. Verify the GLM-5.2 marker contract
  4. Send a bounded request
  5. Reserve answer capacity explicitly
  6. Interpret the upstream GLM-5.2 runs
  7. Validate accepted budget values
  8. Count token IDs, not characters or chunks
  9. Gate streaming tool calls separately
  10. Use the qualified version boundary
  11. Run a one-variable acceptance matrix
  12. Diagnose failures without hiding them
  13. Rent a canary cluster only after source checks
  14. Frequently asked questions
  15. Sources and method

The similar names hide different contracts. Decide which surface you operate before copying a field.

Control Audited surface What it changes What it does not prove
max_tokens Z.AI hosted API and vLLM-compatible requests Maximum generated completion capacity How much is thinking versus visible content
reasoning_effort Z.AI’s documented GLM-5.2 API A hosted reasoning-effort level such as low, high, or max A fixed number of reasoning tokens
thinking_token_budget vLLM sampling extension in this audit Maximum tokens counted inside the configured reasoning span Hosted Z.AI acceptance, answer correctness, or adequate reasoning
enable_thinking: false GLM chat-template argument Starts generation with an empty thinking block Equivalent behavior to a small numeric budget

The GLM-5.2 max-tokens guide sizes the outer completion and context envelope. The reasoning-effort guide covers the hosted effort labels. This page begins only after you have chosen self-hosted vLLM and need a numeric ceiling inside that outer output allowance.

Do not send thinking_token_budget blindly through a generic provider proxy. The current Z.AI parameter documentation describes max_tokens, thinking, and reasoning_effort, but this audit did not find the vLLM field in that hosted contract. A proxy may reject it, remove it, forward it to a different engine, or accept it without enforcement. Record the final route and response, not merely the request object your application created.

The official vLLM recipe pairs GLM-5.2 with the glm45 reasoning parser. Tool work adds the glm47 tool parser and automatic tool choice. Pin the image and checkpoint rather than using an unqualified nightly:

Reasoning-only source profile
vllm serve zai-org/GLM-5.2-FP8 \
--served-model-name glm-5.2-fp8 \
--tensor-parallel-size 8 \
--reasoning-parser glm45 \
--chat-template-content-format string \
--max-model-len 131072

For a tool-call canary, add the two documented recipe controls:

Additional tool parser controls
--tool-call-parser glm47 \
--enable-auto-tool-choice

The launch is incomplete without an exact image digest, vLLM version and commit, model repository and revision, Transformers version, GPU topology, driver/runtime, served context, MTP configuration, and the final parsed arguments. Preserve those values beside each result. A copied command on a different runner, vendor fork, or checkpoint can exercise a different parser and sampler path.

--reasoning-parser matters because vLLM documents budget enforcement only when it knows the reasoning boundaries. The field being accepted by the HTTP schema is not proof that a usable start marker, end marker, and natural end marker were resolved. Treat a startup warning about reasoning configuration as a failed preflight, not as harmless logging.

The pinned official tokenizer and template make the GLM-5.2 boundary concrete:

Role Text Token ID Why it matters
Thinking starts <think> 154841 The prompt template primes this marker when thinking is enabled.
Thinking ends </think> 154842 vLLM forces this boundary after the numeric budget is exhausted.
Reported stop &lt;|user|&gt; 154827 It is one of three EOS IDs in the pinned generation config and closes the attributed token layout.

The official chat template ends an enabled generation prompt with <think>. When enable_thinking is false, it ends with <think></think> instead. That is why thinking_token_budget: 0 and disabled thinking are related but not identical: zero remains a valid vLLM budget on a thinking-primed request, whereas the template switch closes the block before decoding starts.

Audit the files actually installed in your image without initializing CUDA:

Inspect the installed contract as text
from importlib.metadata import distribution
from pathlib import Path
dist = distribution("vllm")
root = Path(dist.locate_file(""))
sampling = (root / "vllm/sampling_params.py").read_text()
protocol = (
root / "vllm/entrypoints/openai/chat_completion/protocol.py"
).read_text()
print("vllm:", dist.version)
print("budget_validation:", "validate_thinking_token_budget" in sampling)
print("chat_field:", "thinking_token_budget" in protocol)

Hash both files and record the container digest. Those booleans confirm two source surfaces only; they do not execute the GPU sampler, resolve GLM markers, or validate a model response. A fork can rename equivalent code, so inspect the diff when a boolean is false rather than changing it to force a pass.

Begin with a short, acceptance-test prompt and enough outer capacity to observe both reasoning and content. The value 32 below is a fixture, not a general recommendation:

Direct vLLM Chat Completions fixture
curl -sS http://127.0.0.1:8000/v1/chat/completions \
-H 'Content-Type: application/json' \
-d '{
"model": "glm-5.2-fp8",
"messages": [
{"role": "user", "content": "Return exactly two sentences: state the decision, then one reason."}
],
"temperature": 0,
"max_tokens": 192,
"thinking_token_budget": 32,
"return_token_ids": true
}'

If your OpenAI client does not model the extension, put it in extra_body:

OpenAI Python client with the vLLM extension
from openai import OpenAI
client = OpenAI(base_url="http://127.0.0.1:8000/v1", api_key="local")
response = client.chat.completions.create(
model="glm-5.2-fp8",
messages=[{
"role": "user",
"content": "Return exactly two sentences: state the decision, then one reason.",
}],
temperature=0,
max_tokens=192,
extra_body={
"thinking_token_budget": 32,
"return_token_ids": True,
},
)
choice = response.choices[0]
assert choice.message.content and choice.message.content.strip()
assert choice.finish_reason in {"stop", "tool_calls"}

Do not print or persist private reasoning text just to prove the cap. Preserve token counts, marker positions, finish reason, response shape, latency, and a hash or task-specific acceptance result. If your policy permits reasoning retention, store it behind the same access, deletion, and incident controls as the prompt and answer.

Choose a minimum visible-answer allowance from the application contract. For the attributed GLM-5.2 token-inspected layout, one </think> token and one terminal stop token also consumed completion capacity. A conservative planning cell is therefore:

Source-derived capacity reservation
visible_capacity = max(
0,
max_tokens - thinking_token_budget - 1 end marker - 1 stop token
)

This is capacity math, not a content guarantee. A response can stop early, repeat, call a tool, emit more protocol tokens, or fail validation. Tool XML or JSON needs its own reserve. Still, the cell catches configurations that cannot possibly hold the required answer under the audited layout.

max_tokens Thinking cap Required visible reserve Calculated capacity Gate
192 16 64 174 Pass with 110-token margin
192 32 64 158 Pass with 94-token margin
192 126 64 64 Exact boundary; operationally fragile
192 127 64 63 Fail before a model call
4,096 512 1,024 3,582 Pass with 2,558-token margin
4,096 3,070 1,024 1,024 Exact boundary; operationally fragile
4,096 3,071 1,024 1,023 Fail before a model call

Do not deploy an exact-boundary row. Leave margin for protocol variation and then measure actual accepted-task quality. If the task genuinely needs longer reasoning, raise the outer cap or reduce the required answer size deliberately; do not silently steal tokens from the answer.

Open vLLM issue #48201 reports a GLM-5.2 test on v0.24.0 with max_tokens: 192. These are reporter observations, not our benchmark and not a vLLM default recommendation:

Reported arm Reasoning observation Visible content Finish state
No explicit budget 344 characters; completion consumed all 192 tokens 0 characters length
Budget 16 31 reasoning characters 287 content characters length
Budget 32, token-inspected run 32 reasoning tokens 53 content tokens stop at 87 completion tokens

The token-inspected row reconstructs exactly:

32 reasoning + 1 </think> + 53 content + 1 stop = 87 completion tokens

Characters are included only because that is what two issue rows published; they are not comparable to token counts. The useful result is narrower: one unbounded fixture exhausted the completion with no content, while bounded fixtures crossed the reasoning boundary and produced content. It does not prove that 16 or 32 is optimal for another language, prompt, task, sampler, model revision, or acceptance threshold.

The same issue reports one streaming tool call with a budget of 16 and the expected delta.tool_calls shape. That is a single attributed success, not an availability rate or parser guarantee. Reproduce it against your pinned server before granting any tool permission.

The v0.28.0 sampling source has an explicit input contract. Test it at your API boundary so a serializer cannot turn an intended integer into another type:

Request value v0.28.0 source result Operational meaning
Omitted or null Accepted, normalized to no budget No explicit reasoning limit
-1 Accepted, normalized to no budget Unlimited sentinel, not “disable thinking”
0 Accepted as zero Force the configured end when the thinking span begins
16, 32 Accepted Exact non-negative integer cap
-2 Rejected Other negative values are invalid
1.5 Rejected Floats are invalid even when numerically positive
true Rejected Booleans are invalid even though Python treats bool as an int subclass

Fail before reaching the model when the field is absent after serialization, changes type, exceeds your application limit, or is greater than the outer completion allowance. vLLM’s source accepting a very large integer is not a reason to let untrusted callers reserve it. Apply a service-side maximum tied to cost, latency, queue fairness, and visible-answer capacity.

Also reject an adapter that converts -1 to zero. Those values mean opposite things here: unlimited versus immediate forced close. Log the normalized budget beside max_tokens and the resolved route.

Streaming chunks do not equal tokens. One chunk can carry several token IDs, and Unicode character length depends on content. The current vLLM E2E test for other reasoning models counts IDs between markers for exactly this reason.

For GLM-5.2, concatenate prompt and decode IDs, find the last start marker, and then find the first end marker after it:

Verify the pinned GLM-5.2 reasoning boundary
THINK_START = 154841
THINK_END = 154842
EOS_IDS = {154820, 154827, 154829}
def inspect_layout(prompt_ids: list[int], decode_ids: list[int]) -> dict:
all_ids = prompt_ids + decode_ids
start = max(i for i, token in enumerate(all_ids) if token == THINK_START)
try:
end = next(
i for i in range(start + 1, len(all_ids))
if all_ids[i] == THINK_END
)
except StopIteration as exc:
raise AssertionError("missing </think> marker") from exc
reasoning_tokens = end - start - 1
terminal_is_eos = bool(decode_ids and decode_ids[-1] in EOS_IDS)
return {
"reasoning_tokens": reasoning_tokens,
"end_marker_index": end,
"terminal_is_eos": terminal_is_eos,
}

Require reasoning_tokens <= thinking_token_budget; for the current forced boundary path, test equality on a prompt known to exceed the budget. A smaller count may mean the model ended thinking naturally, which is valid. Missing markers, marker text inside final content, or a budgeted response that never crosses into content is a failed canary.

Do not hard-code the IDs across checkpoint revisions. Resolve them from the exact tokenizer, compare with the stored revision, and stop if they change. The same token number can have a different meaning in another tokenizer.

Tool calls add a second transition: reasoning must close before the tool parser can emit a structured function. A historical GLM-5.2 issue #46040 reported <tool_call> markup appearing before </think>. The reasoning parser consumed the markup, leaving no usable tool_calls. Another reporter reproduced it on NVIDIA as well as the original ROCm setup, so the incident was not safely classified as one hardware backend. The issue was closed after a maintainer pointed to merged streaming-parser PR #45915.

That history does not justify skipping a current test. For each pinned build, run at least these arms:

Arm Thinking Budget Tools Required result
Plain answer control Enabled 32 None Reasoning ends, non-empty content, no raw markers
Natural-end control Enabled Large bounded value None Model may end below cap; content remains valid
Tool transition Enabled 16 or tested value One deterministic tool Reasoning ends before delta.tool_calls; arguments parse as JSON
No-thinking tool Disabled in template None Same tool Valid tool call without reasoning output
Non-streaming parity Same as tool transition Same Same Same function and schema contract as the stream

For the tool arm, require exactly one allowed function name, valid schema, bounded arguments, finish_reason: "tool_calls", no </think> inside the argument string, and no raw <tool_call> markup in visible content or reasoning. Execute only a synthetic read-only function. A well-shaped call is still untrusted input and grants no authority.

The complete GLM-5.2 tool-calling guide covers argument validation, tool-result messages, retry limits, and idempotency. A thinking budget fixes neither authorization nor the application loop.

Do not compress three different evidence levels into “supported since v0.23.0.”

vLLM version What the source establishes What remains unproven
v0.23.0 Official GLM-5.2 model card and recipe set this as the general serving floor. This audit does not establish the GLM budget field on that tag.
v0.24.0 Issue #48201 reports successful numeric-budget and streaming-tool fixtures. The report is not a merged GLM-specific regression test or default.
v0.28.0 Tagged source accepts, forwards, and enforces the generic budget; its current E2E suite covers other reasoning models. The GLM-specific defaults/regression RFC remains open.

For a new deployment, start evaluation from v0.28.0 or a later pinned stable release because that is the stable source audited here—not because this page proves every GLM-5.2 topology on v0.28.0. If you operate v0.24.0, reproduce the exact field and parser behavior before considering an upgrade; do not treat an issue report as your receipt. If you operate v0.23.0, do not infer a numeric budget from the general model-support floor.

A vendor image can backport or remove behavior. Compare its exact source, resolve the running commit, and test the endpoint. Tags and package metadata are evidence only when they describe the code that actually serves requests.

Freeze a small corpus with distinct answer contracts: short factual response, two-sentence decision, bounded code edit explanation, one deterministic tool call, and one task that genuinely benefits from longer reasoning. Keep prompt, checkpoint, sampler, outer cap, server flags, and concurrency constant while changing only the budget.

Gate Record Reject when
Marker Start/end IDs and positions End marker missing, duplicated unexpectedly, or leaks into content
Capacity Prompt, completion, reasoning, content token counts Required answer reserve is impossible or actual content is empty
Semantics Task-specific validator A lower cap changes an accepted answer into a wrong or incomplete one
Stop finish_reason and terminal token Unexpected length, missing tool transition, or repeated scaffolding
Stream Ordered delta types and token IDs Tool content arrives inside reasoning or chunks cannot reconstruct the response
Service TTFT, TPOT, total latency, queue time, errors Tail regression, worker restart, 5xx, or stuck request exceeds the gate
Cost Generated tokens and retries Savings are erased by retries or human repair

Use at least four arms: omitted budget, low bounded budget, higher bounded budget, and thinking disabled. Add MTP on/off and streaming/non-streaming only after the simple matrix passes; otherwise too many variables change at once.

Select by cost and latency per accepted result, not shortest reasoning. A low cap that forces three retries is worse than one longer accepted call. A high cap that consistently leaves the required content reserve can be correct for complex tasks. The output contract, not a fashionable integer, is the decision boundary.

Symptom First check Safer next action
HTTP rejects the field Type, exact endpoint, installed protocol source Stop; do not remove the field and silently run unbounded.
Field accepted but reasoning exceeds it Reasoning parser/config, marker IDs, running commit Fail the source preflight and inspect the resolved parser.
finish_reason: length, no content Outer cap and observed reasoning span Lower a tested budget or raise the outer cap while preserving a written answer reserve.
Empty content with an end marker Template, stop IDs, output parser Compare raw token IDs; do not manufacture content from private reasoning.
Raw <tool_call> inside reasoning Parser transition and version/source Stop tool execution; reproduce with one synthetic function and inspect PR #45915 lineage.
Valid call but wrong arguments Schema and task acceptance Reject arguments; the budget is not an argument validator.
Budget 0 behaves unlike disabled thinking Template prefix Test enable_thinking: false as a separate arm.
Hosted API ignores or rejects the field Route contract Use documented hosted controls; do not describe the vLLM extension as portable.

Never “fix” an empty answer by copying reasoning into content. That leaks a different data class, breaks downstream semantics, and hides the actual capacity failure. Likewise, never strip malformed tool markup and execute what remains. Stop at the parser boundary and preserve a sanitized token-shape receipt.

Rent a canary cluster only after source checks

Section titled “Rent a canary cluster only after source checks”

Marker lookup, field validation, answer-capacity math, request serialization, and parser-source inspection require no GPU. Spend on a cluster only when the remaining question is whether the pinned GLM-5.2 image passes your production-shaped budget and tool matrix. Write down GPU count/HBM, interconnect, driver, image digest, checkpoint revision, storage, egress, maximum spend, request corpus, pass/fail gates, rollback, and deletion plan before provisioning.

If that test is not economically justified, keep the source audit, use a smaller model to validate the application harness, or use a documented hosted route with its native controls. Smaller-model success verifies the harness, not GLM-5.2 quality or parser parity.

Is thinking_token_budget an official Z.AI API field?

Section titled “Is thinking_token_budget an official Z.AI API field?”

Not in the hosted contract audited here. This guide verifies it as a vLLM sampling extension. Z.AI documents thinking, reasoning_effort, and max_tokens for its hosted route. Check the exact endpoint instead of assuming OpenAI-compatible extensions are portable.

What GLM-5.2 thinking budget should I use?

Section titled “What GLM-5.2 thinking budget should I use?”

There is no universal number established here. Start from the minimum visible answer or tool-call capacity, choose bounded candidate values, and select the lowest cost per accepted result. The 16 and 32 values are attributed issue fixtures, not defaults.

For the audited vLLM behavior, thinking and visible content share the generated completion allowance. That is why a separate reasoning cap can preserve answer capacity. See the outer-limit guide for the context and output distinction.

No. Zero is a valid numeric budget that forces the configured reasoning end as the thinking span begins. Disabling thinking changes the chat-template prefix to an empty <think></think> block. Test them as separate configurations.

No. v0.28.0 normalizes -1 to no explicit budget, meaning unlimited within the ordinary completion constraints. Use the template’s documented disable control when you intend no thinking.

No. It can reserve theoretical capacity and force a boundary. The model can still stop early, produce invalid content, miss the task, or fail the parser. Require a non-empty, semantically accepted result.

No. Count token IDs between the pinned start and end markers. Character counts and stream-chunk counts are not token counts.

It is the official general GLM-5.2 serving floor in the pinned model card and recipe. This audit does not establish the numeric GLM budget on that tag. The runtime report uses v0.24.0; the audited current generic source is v0.28.0.

No. Validate the function allowlist and JSON schema, authorize the user and action, cap spend and retries, and require idempotency or approval for writes. Thinking length does not expand permissions.

Primary and authoritative sources checked on August 27, 2026:

One exact Google query—GLM-5.2 vLLM thinking_token_budget—used the budgeted SerpAPI client with ten requested results, no pagination, and one paid search unit. It returned nine organic results led by the official vLLM recipe, Z.AI release, and a GLM parser issue, but no dedicated guide for the combined budget/reserve/parser task. The approximate result count is not search volume and is not used as a demand claim. The query was recorded as high-value because it confirmed a distinct supply gap and added the parser regression gate.

The site audit covered all 92 prepublication canonicals and 85 registered content pages. No existing page mentioned thinking_token_budget. The nearest pages own different jobs: total output capacity, hosted reasoning effort, and the hosted function loop. The new intent is the numeric self-hosted vLLM reasoning ceiling plus answer-reserve and parser verification.

The evidence generator reproduced only deterministic contracts: 21 source hashes, eight accepted/rejected field values, seven capacity cells, the pinned marker IDs, and the issue’s 32 + 1 + 53 + 1 = 87 token layout. We made zero model calls, zero local GPU runs, zero upstream test runs, and zero Docker runs. A task-owned shallow vLLM source checkout was removed after inspection. Upstream model and hardware results remain attributed to their reporters.