From 15f5583fd220b5107d3d838e09c8311052386b94 Mon Sep 17 00:00:00 2001 From: Paul Kompfner Date: Thu, 4 Dec 2025 14:47:31 -0500 Subject: [PATCH] Simplify, at the expense of a bit of not-yet-needed flexibility: rather than associating a loose `thought_metadata` with each thought, use a `signature`. Thought signatures are the only "thought metadata" we use today. --- src/pipecat/adapters/services/anthropic_adapter.py | 3 +-- src/pipecat/frames/frames.py | 9 +++++---- .../processors/aggregators/llm_response_universal.py | 2 +- src/pipecat/services/anthropic/llm.py | 6 +----- 4 files changed, 8 insertions(+), 12 deletions(-) diff --git a/src/pipecat/adapters/services/anthropic_adapter.py b/src/pipecat/adapters/services/anthropic_adapter.py index e111b34df..da03ad013 100644 --- a/src/pipecat/adapters/services/anthropic_adapter.py +++ b/src/pipecat/adapters/services/anthropic_adapter.py @@ -185,8 +185,7 @@ class AnthropicLLMAdapter(BaseLLMAdapter[AnthropicLLMInvocationParams]): isinstance(message.message, dict) and message.message.get("type") == "thought" and (text := message.message.get("text")) - and isinstance(metadata := message.message.get("metadata"), dict) - and (signature := metadata.get("signature")) + and (signature := message.message.get("signature")) ): return { "role": "assistant", diff --git a/src/pipecat/frames/frames.py b/src/pipecat/frames/frames.py index d538ce6b4..9209d603a 100644 --- a/src/pipecat/frames/frames.py +++ b/src/pipecat/frames/frames.py @@ -642,15 +642,16 @@ class LLMThoughtEndFrame(ControlFrame): """Frame indicating the end of an LLM thought. Parameters: - thought_metadata: Optional metadata associated with the thought, - e.g. an Anthropic thought signature. + signature: Optional signature associated with the thought. + This is used by Anthropic, which includes a signature at the end of + each thought. """ - thought_metadata: Optional[Dict[str, Any]] = None + signature: Any = None def __str__(self): pts = format_pts(self.pts) - return f"{self.name}(pts: {pts}, metadata: {self.thought_metadata})" + return f"{self.name}(pts: {pts}, signature: {self.signature})" @dataclass diff --git a/src/pipecat/processors/aggregators/llm_response_universal.py b/src/pipecat/processors/aggregators/llm_response_universal.py index 4a074da93..76641f0e0 100644 --- a/src/pipecat/processors/aggregators/llm_response_universal.py +++ b/src/pipecat/processors/aggregators/llm_response_universal.py @@ -905,7 +905,7 @@ class LLMAssistantAggregator(LLMContextAggregator): message={ "type": "thought", "text": thought, - "metadata": frame.thought_metadata, + "signature": frame.signature, }, ) ) diff --git a/src/pipecat/services/anthropic/llm.py b/src/pipecat/services/anthropic/llm.py index 662975056..348745e84 100644 --- a/src/pipecat/services/anthropic/llm.py +++ b/src/pipecat/services/anthropic/llm.py @@ -421,11 +421,7 @@ class AnthropicLLMService(LLMService): elif hasattr(event.delta, "thinking"): await self.push_frame(LLMThoughtTextFrame(text=event.delta.thinking)) elif hasattr(event.delta, "signature"): - await self.push_frame( - LLMThoughtEndFrame( - thought_metadata={"signature": event.delta.signature} - ) - ) + await self.push_frame(LLMThoughtEndFrame(signature=event.delta.signature)) elif event.type == "content_block_start": if event.content_block.type == "tool_use": tool_use_block = event.content_block