feat: support filtering out messages when converting to openai dict (#4337)

* feat: support filtering out messages when converting to openai dict

* fix imports
This commit is contained in:
cthomas
2025-09-01 12:48:45 -07:00
committed by GitHub
parent 2dcbb9d9a6
commit 1edcc13778
13 changed files with 59 additions and 32 deletions

View File

@@ -11,7 +11,7 @@ from openai.types.chat.chat_completion_chunk import ChatCompletionChunk
from letta.llm_api.openai_client import OpenAIClient
from letta.otel.tracing import trace_method
from letta.schemas.llm_config import LLMConfig
from letta.schemas.message import Message as PydanticMessage, Message as _Message
from letta.schemas.message import Message as PydanticMessage
from letta.schemas.openai.chat_completion_request import (
AssistantMessage,
ChatCompletionRequest,
@@ -119,7 +119,9 @@ def build_deepseek_chat_completions_request(
# inner_thoughts_description=inner_thoughts_desc,
# )
openai_message_list = [cast_message_to_subtype(m.to_openai_dict(put_inner_thoughts_in_kwargs=False)) for m in messages]
openai_message_list = [
cast_message_to_subtype(m) for m in PydanticMessage.to_openai_dicts_from_list(messages, put_inner_thoughts_in_kwargs=False)
]
if llm_config.model:
model = llm_config.model
@@ -343,7 +345,9 @@ class DeepseekClient(OpenAIClient):
system_message.content += f"<available functions> {''.join(json.dumps(f) for f in tools)} </available functions>"
system_message.content += 'Select best function to call simply respond with a single json block with the fields "name" and "arguments". Use double quotes around the arguments.'
openai_message_list = [cast_message_to_subtype(m.to_openai_dict(put_inner_thoughts_in_kwargs=False)) for m in messages]
openai_message_list = [
cast_message_to_subtype(m) for m in PydanticMessage.to_openai_dicts_from_list(messages, put_inner_thoughts_in_kwargs=False)
]
if llm_config.model == "deepseek-reasoner": # R1 currently doesn't support function calling natively
add_functions_to_system_message(

View File

@@ -310,7 +310,7 @@ def calculate_summarizer_cutoff(in_context_messages: List[Message], token_counts
f"Given in_context_messages has different length from given token_counts: {len(in_context_messages)} != {len(token_counts)}"
)
in_context_messages_openai = [m.to_openai_dict() for m in in_context_messages]
in_context_messages_openai = Message.to_openai_dicts_from_list(in_context_messages)
if summarizer_settings.evict_all_messages:
logger.info("Evicting all messages...")
@@ -351,7 +351,7 @@ def calculate_summarizer_cutoff(in_context_messages: List[Message], token_counts
def get_token_counts_for_messages(in_context_messages: List[Message]) -> List[int]:
in_context_messages_openai = [m.to_openai_dict() for m in in_context_messages]
in_context_messages_openai = Message.to_openai_dicts_from_list(in_context_messages)
token_counts = [count_tokens(str(msg)) for msg in in_context_messages_openai]
return token_counts

View File

@@ -145,7 +145,7 @@ def create(
# Count the tokens first, if there's an overflow exit early by throwing an error up the stack
# NOTE: we want to include a specific substring in the error message to trigger summarization
messages_oai_format = [m.to_openai_dict() for m in messages]
messages_oai_format = Message.to_openai_dicts_from_list(messages)
prompt_tokens = num_tokens_from_messages(messages=messages_oai_format, model=llm_config.model)
function_tokens = num_tokens_from_functions(functions=functions, model=llm_config.model) if functions else 0
if prompt_tokens + function_tokens > llm_config.context_window:

View File

@@ -21,7 +21,7 @@ from letta.local_llm.utils import num_tokens_from_functions, num_tokens_from_mes
from letta.log import get_logger
from letta.otel.tracing import log_event
from letta.schemas.llm_config import LLMConfig
from letta.schemas.message import Message as _Message, MessageRole as _MessageRole
from letta.schemas.message import Message as PydanticMessage, MessageRole as _MessageRole
from letta.schemas.openai.chat_completion_request import (
ChatCompletionRequest,
FunctionCall as ToolFunctionChoiceFunctionCall,
@@ -177,7 +177,7 @@ async def openai_get_model_list_async(
def build_openai_chat_completions_request(
llm_config: LLMConfig,
messages: List[_Message],
messages: List[PydanticMessage],
user_id: Optional[str],
functions: Optional[list],
function_call: Optional[str],
@@ -201,13 +201,12 @@ def build_openai_chat_completions_request(
use_developer_message = accepts_developer_role(llm_config.model)
openai_message_list = [
cast_message_to_subtype(
m.to_openai_dict(
put_inner_thoughts_in_kwargs=llm_config.put_inner_thoughts_in_kwargs,
use_developer_message=use_developer_message,
)
cast_message_to_subtype(m)
for m in PydanticMessage.to_openai_dicts_from_list(
messages,
put_inner_thoughts_in_kwargs=llm_config.put_inner_thoughts_in_kwargs,
use_developer_message=use_developer_message,
)
for m in messages
]
if llm_config.model:
@@ -326,7 +325,7 @@ def openai_chat_completions_process_stream(
# Create a dummy Message object to get an ID and date
# TODO(sarah): add message ID generation function
dummy_message = _Message(
dummy_message = PydanticMessage(
role=_MessageRole.assistant,
content=[],
agent_id="",

View File

@@ -179,13 +179,12 @@ class OpenAIClient(LLMClientBase):
use_developer_message = accepts_developer_role(llm_config.model)
openai_message_list = [
cast_message_to_subtype(
m.to_openai_dict(
put_inner_thoughts_in_kwargs=llm_config.put_inner_thoughts_in_kwargs,
use_developer_message=use_developer_message,
)
cast_message_to_subtype(m)
for m in PydanticMessage.to_openai_dicts_from_list(
messages,
put_inner_thoughts_in_kwargs=llm_config.put_inner_thoughts_in_kwargs,
use_developer_message=use_developer_message,
)
for m in messages
]
if llm_config.model: