Summary
Note: AI assisted with the writing of this request, but all testing, log verification (showThinkingSummaries), and fact-checking were done manually by me, a human. If something is off, it's likely my python skills.
The plugin traces text, tool_use, and tool_result content, but drops the assistant's
thinking blocks (reasoning summaries). When they're available in the transcript they'd be valuable
in Langfuse - reasoning is often where the "why" behind a tool call or answer lives. I'd like the hook
to ingest them.
⚠️ Important prerequisite (please read before reproducing)
Thinking summaries are not written to the transcript by default. showThinkingSummaries is a
Claude Code setting (in ~/.claude/settings.json), and it is false by default. It must be set
to true for Claude Code to log thinking summaries to the session *.jsonl at all.
So if you open a normal transcript and see no thinking blocks, that's expected - the data isn't
missing from your parser, it was never logged. To reproduce / see the data:
- In
~/.claude/settings.json set "showThinkingSummaries": true.
- Run a Claude Code session (with a model that emits extended thinking).
- The transcript will then contain
thinking blocks (see shape below).
Because enabling this Claude setting is itself a deliberate user opt-in, the plugin can safely ingest
thinking blocks whenever they are present - the setting already gates whether the data exists.
Current behavior
In hooks/langfuse_hook.py, extract_text_from_content() keeps only type == "text" blocks:
def extract_text_from_content(content: Any) -> str:
if isinstance(content, list):
parts: List[str] = []
for x in content:
if isinstance(x, dict) and x.get("type") == "text": # thinking blocks skipped
parts.append(x.get("text", ""))
...
tool_use / tool_result get their own handling, but thinking blocks are ignored, so reasoning
never reaches Langfuse even when it has been logged.
Transcript shape (with showThinkingSummaries enabled)
Thinking blocks appear inside each assistant message's content array, interleaved with text and
tool_use, in ~/.claude/projects/<project>/<session>.jsonl:
{
"type": "assistant",
"message": {
"role": "assistant",
"content": [
{ "type": "thinking", "thinking": "<reasoning summary text>", "signature": "<attestation>" },
{ "type": "text", "text": "..." }
]
}
}
Proposed change
Extract type == "thinking" blocks and attach them to the corresponding turn/generation. Reasonable
representations (maintainers' call):
- as
metadata.thinking on the assistant generation, or
- as a dedicated child observation (e.g. name
thinking), or
- prepended to the generation
output behind a clear delimiter.
Keep only the thinking text.
Question for maintainers (routing)
Is this fully doable in the hook using existing observation fields, or does it need any coordination
with Langfuse core / the ingestion API (e.g. a first-class "reasoning" observation type)? Happy to move
or cross-post this to the org Ideas board if you'd prefer it there. Related precedent for reasoning
capture in another integration: langfuse/langfuse#12876.
Environment
- Plugin:
langfuse/Claude-Observability-Plugin (latest)
- Claude Code with
showThinkingSummaries: true (non-default; required for the data to exist)
Summary
Note: AI assisted with the writing of this request, but all testing, log verification (showThinkingSummaries), and fact-checking were done manually by me, a human. If something is off, it's likely my python skills.
The plugin traces
text,tool_use, andtool_resultcontent, but drops the assistant'sthinkingblocks (reasoning summaries). When they're available in the transcript they'd be valuablein Langfuse - reasoning is often where the "why" behind a tool call or answer lives. I'd like the hook
to ingest them.
Thinking summaries are not written to the transcript by default.
showThinkingSummariesis aClaude Code setting (in
~/.claude/settings.json), and it isfalseby default. It must be setto
truefor Claude Code to log thinking summaries to the session*.jsonlat all.So if you open a normal transcript and see no
thinkingblocks, that's expected - the data isn'tmissing from your parser, it was never logged. To reproduce / see the data:
~/.claude/settings.jsonset"showThinkingSummaries": true.thinkingblocks (see shape below).Because enabling this Claude setting is itself a deliberate user opt-in, the plugin can safely ingest
thinking blocks whenever they are present - the setting already gates whether the data exists.
Current behavior
In
hooks/langfuse_hook.py,extract_text_from_content()keeps onlytype == "text"blocks:tool_use/tool_resultget their own handling, butthinkingblocks are ignored, so reasoningnever reaches Langfuse even when it has been logged.
Transcript shape (with showThinkingSummaries enabled)
Thinking blocks appear inside each assistant message's
contentarray, interleaved withtextandtool_use, in~/.claude/projects/<project>/<session>.jsonl:{ "type": "assistant", "message": { "role": "assistant", "content": [ { "type": "thinking", "thinking": "<reasoning summary text>", "signature": "<attestation>" }, { "type": "text", "text": "..." } ] } }Proposed change
Extract
type == "thinking"blocks and attach them to the corresponding turn/generation. Reasonablerepresentations (maintainers' call):
metadata.thinkingon the assistant generation, orthinking), oroutputbehind a clear delimiter.Keep only the
thinkingtext.Question for maintainers (routing)
Is this fully doable in the hook using existing observation fields, or does it need any coordination
with Langfuse core / the ingestion API (e.g. a first-class "reasoning" observation type)? Happy to move
or cross-post this to the org Ideas board if you'd prefer it there. Related precedent for reasoning
capture in another integration: langfuse/langfuse#12876.
Environment
langfuse/Claude-Observability-Plugin(latest)showThinkingSummaries: true(non-default; required for the data to exist)