fix: prevent empty reasoning messages in streaming interfaces (#7207)
* fix: prevent empty reasoning messages in streaming interfaces Prevents empty "Thinking..." indicators from appearing in clients by filtering out reasoning messages with no content at the source. Changes: - Gemini: Don't emit ReasoningMessage when only thought_signature exists - Gemini: Only emit reasoning content if text is non-empty - Anthropic: Don't emit ReasoningMessage for BetaSignatureDelta - Anthropic: Only emit reasoning content if thinking text is non-empty This fixes the issue where providers send signature metadata before actual thinking content, causing empty reasoning blocks to appear in the UI after responses complete. Affects: Gemini reasoning, Anthropic extended thinking 👾 Generated with [Letta Code](https://letta.com) Co-Authored-By: Letta <noreply@letta.com> * fix: handle Anthropic thinking signature correctly - Only include 'signature' in Anthropic message payload if it is not None (fixes BadRequestError). - Capture and attach 'signature' to ReasoningMessage in streaming interface. * fix(anthropic): attach signature to last reasoning message in stream --------- Co-authored-by: Letta <noreply@letta.com>
This commit is contained in:
@@ -88,6 +88,7 @@ class SimpleAnthropicStreamingInterface:
|
||||
self.tool_call_name = None
|
||||
self.accumulated_tool_call_args = ""
|
||||
self.previous_parse = {}
|
||||
self.thinking_signature = None
|
||||
|
||||
# usage trackers
|
||||
self.input_tokens = 0
|
||||
@@ -426,20 +427,23 @@ class SimpleAnthropicStreamingInterface:
|
||||
f"Streaming integrity failed - received BetaThinkingBlock object while not in THINKING EventMode: {delta}"
|
||||
)
|
||||
|
||||
if prev_message_type and prev_message_type != "reasoning_message":
|
||||
message_index += 1
|
||||
reasoning_message = ReasoningMessage(
|
||||
id=self.letta_message_id,
|
||||
source="reasoner_model",
|
||||
reasoning=delta.thinking,
|
||||
date=datetime.now(timezone.utc).isoformat(),
|
||||
otid=Message.generate_otid_from_id(self.letta_message_id, message_index),
|
||||
run_id=self.run_id,
|
||||
step_id=self.step_id,
|
||||
)
|
||||
self.reasoning_messages.append(reasoning_message)
|
||||
prev_message_type = reasoning_message.message_type
|
||||
yield reasoning_message
|
||||
# Only emit reasoning message if we have actual content
|
||||
if delta.thinking and delta.thinking.strip():
|
||||
if prev_message_type and prev_message_type != "reasoning_message":
|
||||
message_index += 1
|
||||
reasoning_message = ReasoningMessage(
|
||||
id=self.letta_message_id,
|
||||
source="reasoner_model",
|
||||
reasoning=delta.thinking,
|
||||
signature=self.thinking_signature,
|
||||
date=datetime.now(timezone.utc).isoformat(),
|
||||
otid=Message.generate_otid_from_id(self.letta_message_id, message_index),
|
||||
run_id=self.run_id,
|
||||
step_id=self.step_id,
|
||||
)
|
||||
self.reasoning_messages.append(reasoning_message)
|
||||
prev_message_type = reasoning_message.message_type
|
||||
yield reasoning_message
|
||||
|
||||
elif isinstance(delta, BetaSignatureDelta):
|
||||
# Safety check
|
||||
@@ -448,21 +452,15 @@ class SimpleAnthropicStreamingInterface:
|
||||
f"Streaming integrity failed - received BetaSignatureDelta object while not in THINKING EventMode: {delta}"
|
||||
)
|
||||
|
||||
if prev_message_type and prev_message_type != "reasoning_message":
|
||||
message_index += 1
|
||||
reasoning_message = ReasoningMessage(
|
||||
id=self.letta_message_id,
|
||||
source="reasoner_model",
|
||||
reasoning="",
|
||||
date=datetime.now(timezone.utc).isoformat(),
|
||||
signature=delta.signature,
|
||||
otid=Message.generate_otid_from_id(self.letta_message_id, message_index),
|
||||
run_id=self.run_id,
|
||||
step_id=self.step_id,
|
||||
)
|
||||
self.reasoning_messages.append(reasoning_message)
|
||||
prev_message_type = reasoning_message.message_type
|
||||
yield reasoning_message
|
||||
# Store signature but don't emit empty reasoning message
|
||||
# Signature will be attached when actual thinking content arrives
|
||||
self.thinking_signature = delta.signature
|
||||
|
||||
# Update the last reasoning message with the signature so it gets persisted
|
||||
if self.reasoning_messages:
|
||||
last_msg = self.reasoning_messages[-1]
|
||||
if isinstance(last_msg, ReasoningMessage):
|
||||
last_msg.signature = delta.signature
|
||||
|
||||
elif isinstance(event, BetaRawMessageStartEvent):
|
||||
self.message_id = event.message.id
|
||||
|
||||
Reference in New Issue
Block a user