From 38506deda215180b47044f79a5c551139ec1ccee Mon Sep 17 00:00:00 2001 From: Chiang Jun Wai Date: Mon, 22 Jun 2026 22:09:18 +0800 Subject: [PATCH] Fix logging for parallel tool calls in Langfuse hook Ensure parallel tool calls are logged correctly to Langfuse by fixing conditional handling and adding clearer context to log entries in hooks/langfuse_hook.py --- hooks/langfuse_hook.py | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/hooks/langfuse_hook.py b/hooks/langfuse_hook.py index e4a6b86..7c773cd 100644 --- a/hooks/langfuse_hook.py +++ b/hooks/langfuse_hook.py @@ -415,6 +415,23 @@ class Turn: # belongs to, taken from isMeta rows carrying sourceToolUseID. injected_by_tool_id: Dict[str, str] + +def merge_assistant_content(base: Dict[str, Any], extra: Dict[str, Any]) -> None: + """ + Append extra's content blocks onto bases's, in place. + Claude Code emits one logical assistant message (shared message.id) as several transcript rows. + We keep the first row as base and concatenate later rows' content blocks so all tool_use blocks + are preserved. Text/usage already match across rows, so base's metadata (model, usage, timestamp) + is left untouched. + """ + + bc = get_content(base) + ec = get_content(extra) + if not isinstance(bc, list) or not isinstance(ec, list): + return + bc.extend(ec) + + def build_turns(messages: List[Dict[str, Any]]) -> List[Turn]: """ Groups incremental transcript rows into turns: @@ -493,7 +510,14 @@ def flush_turn(): mid = get_message_id(msg) or f"noid:{len(assistant_order)}" if mid not in assistant_latest: assistant_order.append(mid) - assistant_latest[mid] = msg + assistant_latest[mid] = msg + else: + # Claude Code splits ONE assistant message (same message.id) across + # multiple transcripts row: thinking/text first, then one row per + # parallel tool_use (rows can be interleaved with tool_result rows). + # "Latest row wins" dropped every parallel toll call but the last + # merge the content blocks instead so all tool_uses survive. + merge_assistant_content(assistant_latest[mid], msg) continue # ignore unknown rows