Skip to content

Feature request: capture assistant "thinking" blocks (reasoning summaries) into traces #39

Description

@nevarDeath

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:

  1. In ~/.claude/settings.json set "showThinkingSummaries": true.
  2. Run a Claude Code session (with a model that emits extended thinking).
  3. 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions