Reading thinking output
Controlling thinking display
The display field on the thinking configuration controls how thinking content is returned in API responses. display works in both modes: set it alongside type: "adaptive" or type: "enabled". It accepts these values:
"summarized": thinking blocks contain summarized thinking text, a readable summary of Claude's reasoning. This is the default on Claude Opus 4.6, Claude Sonnet 4.6, and earlier models."omitted": thinking blocks are returned with an emptythinkingfield. Thesignaturefield still carries the encrypted full thinking for multi-turn continuity (see Thinking encryption). This is the default on Claude Fable 5.1, Claude Mythos 5.1, Claude Fable 5, Claude Mythos 5, Claude Opus 5, Claude Sonnet 5, Claude Opus 4.8, Claude Opus 4.7, and Claude Mythos Preview."updates"(beta): reasoning blocks are returned with an emptythinkingfield, as with"omitted", and the short progress updates some models write between tool calls come back as readable text. Requires the beta headerthinking-display-updates-2026-08-18.
Set display: "omitted" when your application doesn't surface thinking content to users. The primary benefit is faster time-to-first-text-token when streaming: the server skips streaming thinking tokens entirely and delivers only the signature, so the final text response begins streaming sooner.
With display: "omitted", the response contains thinking blocks with an empty thinking field:
{
"content": [
{
"type": "thinking",
"thinking": "",
"signature": "EosnCkYICxIMMb3LzNrMu..."
},
{
"type": "text",
"text": "The answer is 12,231."
}
]
}Keep the following in mind when working with omitted thinking:
You're still charged for the full thinking tokens. Omitting reduces latency, not cost.
If you pass thinking blocks back in multi-turn conversations, pass them unchanged. The server decrypts the
signatureto reconstruct the original thinking for prompt construction (see Preserving thinking blocks). Any text you place in thethinkingfield of a round-tripped omitted block is ignored.displayis invalid withthinking.type: "disabled"(there is nothing to display).When using
thinking.type: "adaptive"and the model skips thinking for a simple request, no thinking block is produced regardless ofdisplay.When streaming with
display: "omitted", nothinking_deltaevents are emitted. Withdisplay: "updates", only progress-update blocks streamthinking_deltaevents. See Streaming thinking for the event sequence.The
signaturefield is identical whicheverdisplayvalue you set. Switchingdisplayvalues between turns in a conversation is supported.
In the Ruby SDK, plain hashes take display: as the examples show. The typed ThinkingConfigAdaptive class names the parameter display_ (trailing underscore, to avoid shadowing Ruby's Kernel#display). Either way, the wire field is still display.
Summarized thinking
When display is "summarized", the thinking text you receive is a summary of Claude's full thinking process rather than the raw chain of thought. Summarized thinking provides the full intelligence benefits of thinking while preventing misuse. No display setting returns the raw chain of thought.
Keep the following in mind when working with summarized thinking:
You're charged for the full thinking tokens generated by the original request, not the summary tokens. The billed output token count does not match the count of tokens you see in the response.
On Claude Opus 4.6, Claude Sonnet 4.6, and earlier models, the first few lines of thinking output are more verbose, providing detailed reasoning that's particularly helpful for prompt engineering purposes. Claude Mythos Preview summarizes from the first token, so its thinking blocks do not show this verbose preamble.
Summarization preserves the key ideas of Claude's thinking process with minimal added latency, so summaries can stream as they arrive.
Summarization is processed by a different model from the one you target in your requests. The thinking model does not see the summarized output.
As Anthropic seeks to improve the thinking feature, summarization behavior is subject to change.
In rare cases where you need access to full thinking output, contact Anthropic sales.
To see the model's reasoning, read the thinking blocks rather than prompting for reasoning in the response text. On Claude Fable 5.1 and Claude Fable 5, a request that attempts to elicit the model's internal reasoning as part of the response text can be refused with stop_details.category: "reasoning_extraction". See Refusal categories for the field reference and handling guidance.
Streaming thinking
Thinking works with streaming. Thinking blocks stream as thinking_delta events inside content_block_delta events, followed by a single signature_delta event just before the block's content_block_stop. Text blocks stream afterward as usual.
The following examples stream a response with adaptive thinking, printing thinking and text deltas as they arrive:
curl https://api.anthropic.com/v1/messages \
-H "x-api-key: $ANTHROPIC_API_KEY" \
-H "anthropic-version: 2023-06-01" \
-H "content-type: application/json" \
-d '{
"model": "claude-opus-4-8",
"max_tokens": 16000,
"stream": true,
"thinking": {
"type": "adaptive",
"display": "summarized"
},
"messages": [
{
"role": "user",
"content": "What is the greatest common divisor of 1071 and 462?"
}
]
}'ant messages create \
--model claude-opus-4-8 \
--max-tokens 16000 \
--thinking '{type: adaptive, display: summarized}' \
--message '{role: user, content: "What is the greatest common divisor of 1071 and 462?"}' \
--stream \
--format jsonlclient = anthropic.Anthropic()
with client.messages.stream(
model="claude-opus-4-8",
max_tokens=16000,
thinking={"type": "adaptive", "display": "summarized"},
messages=[
{
"role": "user",
"content": "What is the greatest common divisor of 1071 and 462?",
}
],
) as stream:
for event in stream:
if event.type == "content_block_start":
print(f"\nStarting {event.content_block.type} block...")
elif event.type == "content_block_delta":
if event.delta.type == "thinking_delta":
print(event.delta.thinking, end="", flush=True)
elif event.delta.type == "text_delta":
print(event.delta.text, end="", flush=True)const client = new Anthropic();
const stream = client.messages.stream({
model: "claude-opus-4-8",
max_tokens: 16000,
thinking: { type: "adaptive", display: "summarized" },
messages: [{ role: "user", content: "What is the greatest common divisor of 1071 and 462?" }]
});
for await (const event of stream) {
if (event.type === "content_block_start") {
console.log(`\nStarting ${event.content_block.type} block...`);
} else if (event.type === "content_block_delta") {
if (event.delta.type === "thinking_delta") {
process.stdout.write(event.delta.thinking);
} else if (event.delta.type === "text_delta") {
process.stdout.write(event.delta.text);
}
}
}AnthropicClient client = new();
var parameters = new MessageCreateParams
{
Model = Model.ClaudeOpus4_8,
MaxTokens = 16000,
Thinking = new ThinkingConfigAdaptive { Display = Display.Summarized },
Messages = [new() { Role = Role.User, Content = "What is the greatest common divisor of 1071 and 462?" }]
};
await foreach (var rawEvent in client.Messages.CreateStreaming(parameters))
{
if (rawEvent.TryPickContentBlockStart(out var start))
{
Console.WriteLine($"\nStarting {start.ContentBlock.Type} block...");
}
else if (rawEvent.TryPickContentBlockDelta(out var delta))
{
if (delta.Delta.TryPickThinking(out var thinkingDelta))
{
Console.Write(thinkingDelta.Thinking);
}
else if (delta.Delta.TryPickText(out var textDelta))
{
Console.Write(textDelta.Text);
}
}
}client := anthropic.NewClient()
stream := client.Messages.NewStreaming(context.TODO(), anthropic.MessageNewParams{
Model: anthropic.ModelClaudeOpus4_8,
MaxTokens: 16000,
Thinking: anthropic.ThinkingConfigParamUnion{
OfAdaptive: &anthropic.ThinkingConfigAdaptiveParam{
Display: anthropic.ThinkingConfigAdaptiveDisplaySummarized,
},
},
Messages: []anthropic.MessageParam{
anthropic.NewUserMessage(anthropic.NewTextBlock("What is the greatest common divisor of 1071 and 462?")),
},
})
for stream.Next() {
event := stream.Current()
switch eventVariant := event.AsAny().(type) {
case anthropic.ContentBlockStartEvent:
fmt.Printf("\nStarting %s block...\n", eventVariant.ContentBlock.Type)
case anthropic.ContentBlockDeltaEvent:
switch deltaVariant := eventVariant.Delta.AsAny().(type) {
case anthropic.ThinkingDelta:
fmt.Print(deltaVariant.Thinking)
case anthropic.TextDelta:
fmt.Print(deltaVariant.Text)
}
}
}
if err := stream.Err(); err != nil {
log.Fatal(err)
}import com.anthropic.models.messages.ThinkingConfigAdaptive;
void main() {
AnthropicClient client = AnthropicOkHttpClient.fromEnv();
MessageCreateParams params = MessageCreateParams.builder()
.model(Model.CLAUDE_OPUS_4_8)
.maxTokens(16000L)
.thinking(ThinkingConfigAdaptive.builder()
.display(ThinkingConfigAdaptive.Display.SUMMARIZED)
.build())
.addUserMessage("What is the greatest common divisor of 1071 and 462?")
.build();
try (var streamResponse = client.messages().createStreaming(params)) {
streamResponse.stream().forEach(event -> {
if (event.contentBlockStart().isPresent()) {
var startEvent = event.contentBlockStart().get();
var block = startEvent.contentBlock();
if (block.isThinking()) {
IO.println("\nStarting thinking block...");
} else if (block.isText()) {
IO.println("\nStarting text block...");
}
} else if (event.contentBlockDelta().isPresent()) {
var deltaEvent = event.contentBlockDelta().get();
deltaEvent.delta().thinking().ifPresent(td ->
IO.print(td.thinking())
);
deltaEvent.delta().text().ifPresent(td ->
IO.print(td.text())
);
}
});
}
}$client = new Client();
$stream = $client->messages->createStream(
maxTokens: 16000,
messages: [
['role' => 'user', 'content' => 'What is the greatest common divisor of 1071 and 462?']
],
model: 'claude-opus-4-8',
thinking: ['type' => 'adaptive', 'display' => 'summarized'],
);
foreach ($stream as $event) {
if ($event->type === 'content_block_start') {
echo "\nStarting {$event->contentBlock->type} block...\n";
} elseif ($event->type === 'content_block_delta') {
if ($event->delta->type === 'thinking_delta') {
echo $event->delta->thinking;
} elseif ($event->delta->type === 'text_delta') {
echo $event->delta->text;
}
}
}client = Anthropic::Client.new
stream = client.messages.stream(
model: "claude-opus-4-8",
max_tokens: 16000,
thinking: { type: "adaptive", display: "summarized" },
messages: [
{ role: "user", content: "What is the greatest common divisor of 1071 and 462?" }
]
)
stream.each do |event|
case event
when Anthropic::Streaming::ThinkingEvent
print event.thinking
when Anthropic::Streaming::TextEvent
print event.text
end
endTo reassemble complete thinking blocks with their signatures after streaming, use your SDK's message-accumulation helper where one exists (for example, stream.get_final_message() in Python or stream.finalMessage() in TypeScript) instead of concatenating deltas yourself.
event: message_start
data: {"type": "message_start", "message": {"id": "msg_01...", "type": "message", "role": "assistant", "content": [], "model": "claude-opus-4-8", "stop_reason": null, "stop_sequence": null}}
event: content_block_start
data: {"type": "content_block_start", "index": 0, "content_block": {"type": "thinking", "thinking": "", "signature": ""}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "I need to find the GCD of 1071 and 462 using the Euclidean algorithm.\n\n1071 = 2 × 462 + 147"}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "thinking_delta", "thinking": "\n462 = 3 × 147 + 21\n147 = 7 × 21 + 0\n\nSo GCD(1071, 462) = 21"}}
// Additional thinking deltas...
event: content_block_delta
data: {"type": "content_block_delta", "index": 0, "delta": {"type": "signature_delta", "signature": "EqQBCgIYAhIM1gbcDa9GJwZA2b..."}}
event: content_block_stop
data: {"type": "content_block_stop", "index": 0}
event: content_block_start
data: {"type": "content_block_start", "index": 1, "content_block": {"type": "text", "text": ""}}
event: content_block_delta
data: {"type": "content_block_delta", "index": 1, "delta": {"type": "text_delta", "text": "The greatest common divisor of 1071 and 462 is **21**."}}
// Additional text deltas...
event: content_block_stop
data: {"type": "content_block_stop", "index": 1}
event: message_delta
data: {"type": "message_delta", "delta": {"stop_reason": "end_turn", "stop_sequence": null}}
event: message_stop
data: {"type": "message_stop"}When display: "omitted" is set, the thinking block opens, a single signature_delta arrives, and the block closes without any thinking_delta events. Text streaming begins immediately after:
event: content_block_start
data: {"type":"content_block_start","index":0,"content_block":{"type":"thinking","thinking":"","signature":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":0,"delta":{"type":"signature_delta","signature":"EosnCkYICxIMMb3LzNrMu..."}}
event: content_block_stop
data: {"type":"content_block_stop","index":0}
event: content_block_start
data: {"type":"content_block_start","index":1,"content_block":{"type":"text","text":""}}With display: "updates" (beta), reasoning blocks stream as they do under "omitted". Each progress-update block streams its text as thinking_delta events before the tool_use block it introduces. A pause of several seconds before the progress-update block opens is normal:
event: content_block_start
data: {"type":"content_block_start","index":1,"content_block":{"type":"thinking","thinking":"","signature":""}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"thinking_delta","thinking":"Confirmed the retry path never refreshes the expired token. Editing auth.py to add the refresh call."}}
event: content_block_delta
data: {"type":"content_block_delta","index":1,"delta":{"type":"signature_delta","signature":"Es8CCkYICxIM..."}}
event: content_block_stop
data: {"type":"content_block_stop","index":1}
event: content_block_start
data: {"type":"content_block_start","index":2,"content_block":{"type":"tool_use","id":"toolu_01D7FLrfh4GYq7yT1ULFeyMV","name":"edit_file","input":{}}}Under "updates", treat a block as a progress update as soon as one of its thinking_delta events carries non-empty text.
When using streaming with thinking enabled, you might notice that text sometimes arrives in larger chunks alternating with smaller, token-by-token delivery. This is expected behavior, especially for thinking content.
The streaming system processes content in batches, which can delay and group streaming events into this "chunky" delivery pattern.
For general streaming mechanics, see Streaming Messages.