feat: rename internal monologue (#2289)

This commit is contained in:
cthomas
2024-12-19 15:16:36 -08:00
committed by GitHub
parent a70fbca219
commit c183fc6b0e
9 changed files with 49 additions and 31 deletions

View File

@@ -95,6 +95,8 @@ def nb_print(messages):
def get_formatted_content(msg):
if msg.message_type == "internal_monologue":
return f'<div class="content"><span class="internal-monologue">{html.escape(msg.internal_monologue)}</span></div>'
elif msg.message_type == "reasoning_message":
return f'<div class="content"><span class="internal-monologue">{html.escape(msg.reasoning)}</span></div>'
elif msg.message_type == "function_call":
args = format_json(msg.function_call.arguments)
return f'<div class="content"><span class="function-name">{html.escape(msg.function_call.name)}</span>({args})</div>'

View File

@@ -10,7 +10,7 @@ from letta.schemas.enums import MessageStreamStatus
from letta.schemas.letta_message import (
ToolCallMessage,
ToolReturnMessage,
InternalMonologue,
ReasoningMessage,
)
from letta.schemas.letta_response import LettaStreamingResponse
from letta.schemas.usage import LettaUsageStatistics
@@ -53,8 +53,8 @@ def _sse_post(url: str, data: dict, headers: dict) -> Generator[LettaStreamingRe
yield MessageStreamStatus(sse.data)
else:
chunk_data = json.loads(sse.data)
if "internal_monologue" in chunk_data:
yield InternalMonologue(**chunk_data)
if "reasoning" in chunk_data:
yield ReasoningMessage(**chunk_data)
elif "tool_call" in chunk_data:
yield ToolCallMessage(**chunk_data)
elif "tool_return" in chunk_data:

View File

@@ -60,18 +60,18 @@ class UserMessage(LettaMessage):
message: str
class InternalMonologue(LettaMessage):
class ReasoningMessage(LettaMessage):
"""
Representation of an agent's internal monologue.
Representation of an agent's internal reasoning.
Attributes:
internal_monologue (str): The internal monologue of the agent
reasoning (str): The internal reasoning of the agent
id (str): The ID of the message
date (datetime): The date the message was created in ISO format
"""
message_type: Literal["internal_monologue"] = "internal_monologue"
internal_monologue: str
message_type: Literal["reasoning_message"] = "reasoning_message"
reasoning: str
class ToolCall(BaseModel):
@@ -196,10 +196,24 @@ class LegacyFunctionReturn(LettaMessage):
stderr: Optional[List[str]] = None
LegacyLettaMessage = Union[InternalMonologue, AssistantMessage, LegacyFunctionCallMessage, LegacyFunctionReturn]
class LegacyInternalMonologue(LettaMessage):
"""
Representation of an agent's internal monologue.
Attributes:
internal_monologue (str): The internal monologue of the agent
id (str): The ID of the message
date (datetime): The date the message was created in ISO format
"""
message_type: Literal["internal_monologue"] = "internal_monologue"
internal_monologue: str
LegacyLettaMessage = Union[LegacyInternalMonologue, AssistantMessage, LegacyFunctionCallMessage, LegacyFunctionReturn]
LettaMessageUnion = Annotated[
Union[SystemMessage, UserMessage, InternalMonologue, ToolCallMessage, ToolReturnMessage, AssistantMessage],
Union[SystemMessage, UserMessage, ReasoningMessage, ToolCallMessage, ToolReturnMessage, AssistantMessage],
Field(discriminator="message_type"),
]

View File

@@ -40,6 +40,8 @@ class LettaResponse(BaseModel):
def get_formatted_content(msg):
if msg.message_type == "internal_monologue":
return f'<div class="content"><span class="internal-monologue">{html.escape(msg.internal_monologue)}</span></div>'
if msg.message_type == "reasoning_message":
return f'<div class="content"><span class="internal-monologue">{html.escape(msg.reasoning)}</span></div>'
elif msg.message_type == "function_call":
args = format_json(msg.function_call.arguments)
return f'<div class="content"><span class="function-name">{html.escape(msg.function_call.name)}</span>({args})</div>'

View File

@@ -19,7 +19,7 @@ from letta.schemas.letta_message import (
ToolCall as LettaToolCall,
ToolCallMessage,
ToolReturnMessage,
InternalMonologue,
ReasoningMessage,
LettaMessage,
SystemMessage,
UserMessage,
@@ -145,10 +145,10 @@ class Message(BaseMessage):
if self.text is not None:
# This is type InnerThoughts
messages.append(
InternalMonologue(
ReasoningMessage(
id=self.id,
date=self.created_at,
internal_monologue=self.text,
reasoning=self.text,
)
)
if self.tool_calls is not None:

View File

@@ -16,7 +16,7 @@ from letta.schemas.letta_message import (
ToolCallDelta,
ToolCallMessage,
ToolReturnMessage,
InternalMonologue,
ReasoningMessage,
LegacyFunctionCallMessage,
LegacyLettaMessage,
LettaMessage,
@@ -411,7 +411,7 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
def _process_chunk_to_letta_style(
self, chunk: ChatCompletionChunkResponse, message_id: str, message_date: datetime
) -> Optional[Union[InternalMonologue, ToolCallMessage, AssistantMessage]]:
) -> Optional[Union[ReasoningMessage, ToolCallMessage, AssistantMessage]]:
"""
Example data from non-streaming response looks like:
@@ -426,10 +426,10 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
# inner thoughts
if message_delta.content is not None:
processed_chunk = InternalMonologue(
processed_chunk = ReasoningMessage(
id=message_id,
date=message_date,
internal_monologue=message_delta.content,
reasoning=message_delta.content,
)
# tool calls
@@ -518,10 +518,10 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
# If we have inner thoughts, we should output them as a chunk
if updates_inner_thoughts:
processed_chunk = InternalMonologue(
processed_chunk = ReasoningMessage(
id=message_id,
date=message_date,
internal_monologue=updates_inner_thoughts,
reasoning=updates_inner_thoughts,
)
# Additionally inner thoughts may stream back with a chunk of main JSON
# In that case, since we can only return a chunk at a time, we should buffer it
@@ -680,13 +680,13 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
# Once we get a complete key, check if the key matches
# If it does match, start processing the value (stringified-JSON string
# And with each new chunk, output it as a chunk of type InternalMonologue
# And with each new chunk, output it as a chunk of type ReasoningMessage
# If the key doesn't match, then flush the buffer as a single ToolCallMessage chunk
# If we're reading a value
# If we're reading the inner thoughts value, we output chunks of type InternalMonologue
# If we're reading the inner thoughts value, we output chunks of type ReasoningMessage
# Otherwise, do simple chunks of ToolCallMessage
@@ -823,10 +823,10 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
# "id": str(msg_obj.id) if msg_obj is not None else None,
# }
assert msg_obj is not None, "Internal monologue requires msg_obj references for metadata"
processed_chunk = InternalMonologue(
processed_chunk = ReasoningMessage(
id=msg_obj.id,
date=msg_obj.created_at,
internal_monologue=msg,
reasoning=msg,
)
self._push_to_buffer(processed_chunk)

View File

@@ -26,7 +26,7 @@ from letta.schemas.agent import AgentState
from letta.schemas.embedding_config import EmbeddingConfig
from letta.schemas.letta_message import (
ToolCallMessage,
InternalMonologue,
ReasoningMessage,
LettaMessage,
)
from letta.schemas.letta_response import LettaResponse
@@ -419,7 +419,7 @@ def assert_invoked_function_call(messages: Sequence[LettaMessage], function_name
def assert_inner_monologue_is_present_and_valid(messages: List[LettaMessage]) -> None:
for message in messages:
if isinstance(message, InternalMonologue):
if isinstance(message, ReasoningMessage):
# Found it, do nothing
return

View File

@@ -20,7 +20,7 @@ from letta.schemas.letta_message import (
AssistantMessage,
ToolCallMessage,
ToolReturnMessage,
InternalMonologue,
ReasoningMessage,
LettaMessage,
SystemMessage,
UserMessage,
@@ -171,7 +171,7 @@ def test_agent_interactions(mock_e2b_api_key_none, client: Union[LocalClient, RE
assert type(letta_message) in [
SystemMessage,
UserMessage,
InternalMonologue,
ReasoningMessage,
ToolCallMessage,
ToolReturnMessage,
AssistantMessage,
@@ -255,7 +255,7 @@ def test_streaming_send_message(mock_e2b_api_key_none, client: RESTClient, agent
assert response, "Sending message failed"
for chunk in response:
assert isinstance(chunk, LettaStreamingResponse)
if isinstance(chunk, InternalMonologue) and chunk.internal_monologue and chunk.internal_monologue != "":
if isinstance(chunk, ReasoningMessage) and chunk.reasoning and chunk.reasoning != "":
inner_thoughts_exist = True
inner_thoughts_count += 1
if isinstance(chunk, ToolCallMessage) and chunk.tool_call and chunk.tool_call.name == "send_message":

View File

@@ -12,7 +12,7 @@ from letta.schemas.enums import MessageRole
from letta.schemas.letta_message import (
ToolCallMessage,
ToolReturnMessage,
InternalMonologue,
ReasoningMessage,
LettaMessage,
SystemMessage,
UserMessage,
@@ -691,14 +691,14 @@ def _test_get_messages_letta_format(
letta_message = letta_messages[letta_message_index]
if message.text:
assert isinstance(letta_message, InternalMonologue)
assert isinstance(letta_message, ReasoningMessage)
letta_message_index += 1
else:
assert message.tool_calls is not None
else: # Non-reverse handling
if message.text:
assert isinstance(letta_message, InternalMonologue)
assert isinstance(letta_message, ReasoningMessage)
letta_message_index += 1
if letta_message_index >= len(letta_messages):
break