Skip to content

fix(langchain): serialize ToolMessage/FunctionMessage with correct role and tool_call_id - #950

Open
milanagm wants to merge 2 commits into
langfuse:mainfrom
milanagm:lfe-15880-langchain-toolmessage-serialized-without-role-and
Open

milanagm wants to merge 2 commits into
langfuse:mainfrom
milanagm:lfe-15880-langchain-toolmessage-serialized-without-role-and

Conversation

@milanagm

@milanagm milanagm commented Sep 14, 2026

Copy link
Copy Markdown
Contributor

Problem

extractChatMessageContent in the LangChain CallbackHandler serializes ToolMessage and FunctionMessage with role: message.name. name is optional on ToolMessage, and idiomatic usage (new ToolMessage({ content, tool_call_id })) never sets it, so the serialized message ends up with no role at all. When name is set, the role becomes the tool's function name (e.g. "get_debt"), not a valid ChatML role. On top of that, tool_call_id was dropped entirely: it's a top-level field on ToolMessage, not part of additional_kwargs.

This breaks "Open in Playground" for any generation whose history contains tool results. The web playground converter (playgroundConverter.ts) only maps a message to a ToolResult when it finds a tool_call_id, and otherwise falls back to role || ChatMessageRole.Assistant, so tool results render as plain assistant messages and re-running the completion loses the tool linkage.

Fixes #930

Linear: https://linear.app/clickhouse/issue/LFE-15880/langchain-toolmessage-serialized-without-role-and-tool-call-id-breaks

Changes

  • ToolMessage{ role: "tool", name, content, additional_kwargs, tool_call_id }
  • FunctionMessage (same pattern one branch above) → { role: "function", name, content, additional_kwargs }
  • name is kept as its own field rather than dropped, matching what LangChain itself sends to the OpenAI Chat Completions API and what the Python SDK's _convert_message_to_dict already does for ToolMessage/FunctionMessage.
  • Extended the exported LlmMessage type with optional name and tool_call_id fields instead of attaching them with as any.

Verification

  • pnpm lint
  • pnpm typecheck
  • pnpm test
  • pnpm test:integration
  • pnpm test:e2e (not run: requires a reachable Langfuse server and API credentials not available in this environment)
  • pnpm format:check

Also verified end-to-end against a local Langfuse instance using the exact history from the issue: before the fix, the ToolMessage history opens in the Playground as a plain Assistant message with no tool linkage; after the fix, it opens as a Tool message linked to the originating tool call (call_1).

Release info

Bump level

  • Major
  • Minor
  • Patch

Libraries affected

  • All of them
  • @langfuse/core
  • @langfuse/client
  • @langfuse/tracing
  • @langfuse/otel
  • @langfuse/openai
  • @langfuse/langchain

Changelog notes

  • Fixed @langfuse/langchain serializing ToolMessage/FunctionMessage without a valid role and dropping tool_call_id, which broke "Open in Playground" for traces containing tool results.

RetriggerConfidence Score: 5/5

The PR appears safe to merge with no actionable correctness, security, or repository-rule issues identified.

Summary

  • Serializes tool messages with role: "tool", optional name, and top-level tool_call_id.
  • Serializes function messages with role: "function" and preserves name separately.
  • Extends LlmMessage with typed optional metadata fields.
  • Adds integration coverage for named and unnamed tool messages and function messages.

Reviews (1) · Last reviewed commit: "fix(langchain): serialize ToolMessage/Fu..."

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Claude Code Review

This pull request is from a fork — automated review is disabled. A repository maintainer can comment @claude review to run a one-time review.

@vercel

vercel Bot commented Sep 14, 2026

Copy link
Copy Markdown

@milanagm is attempting to deploy a commit to the langfuse Team on Vercel.

A member of the Team first needs to authorize it.

…oolmessage-serialized-without-role-and

Resolved conflict in tests/integration/langchain.integration.test.ts: kept
main's two completion-start-time tests (langfuse#948) alongside the new tool/function
message serialization test, and merged the @langchain/core/messages import
into a single declaration.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

langchain: ToolMessage serialized without role and tool_call_id — breaks "Open in Playground" for tool traces

1 participant