diff --git a/letta/llm_api/openai.py b/letta/llm_api/openai.py index d72fb259..e35429bc 100644 --- a/letta/llm_api/openai.py +++ b/letta/llm_api/openai.py @@ -7,7 +7,7 @@ from openai import OpenAI from letta.constants import LETTA_MODEL_ENDPOINT from letta.helpers.datetime_helpers import timestamp_to_datetime from letta.llm_api.helpers import add_inner_thoughts_to_functions, convert_to_structured_output, make_post_request -from letta.llm_api.openai_client import supports_parallel_tool_calling, supports_temperature_param +from letta.llm_api.openai_client import accepts_developer_role, supports_parallel_tool_calling, supports_temperature_param from letta.local_llm.constants import INNER_THOUGHTS_KWARG, INNER_THOUGHTS_KWARG_DESCRIPTION, INNER_THOUGHTS_KWARG_DESCRIPTION_GO_FIRST from letta.local_llm.utils import num_tokens_from_functions, num_tokens_from_messages from letta.log import get_logger @@ -114,8 +114,16 @@ def build_openai_chat_completions_request( put_inner_thoughts_first=put_inner_thoughts_first, ) + 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)) for m in messages + 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, + ) + ) + for m in messages ] if llm_config.model: diff --git a/letta/llm_api/openai_client.py b/letta/llm_api/openai_client.py index c5e512d0..cf464b2c 100644 --- a/letta/llm_api/openai_client.py +++ b/letta/llm_api/openai_client.py @@ -40,7 +40,19 @@ def is_openai_reasoning_model(model: str) -> bool: """Utility function to check if the model is a 'reasoner'""" # NOTE: needs to be updated with new model releases - return model.startswith("o1") or model.startswith("o3") + is_reasoning = model.startswith("o1") or model.startswith("o3") + return is_reasoning + + +def accepts_developer_role(model: str) -> bool: + """Checks if the model accepts the 'developer' role. Note that not all reasoning models accept this role. + + See: https://community.openai.com/t/developer-role-not-accepted-for-o1-o1-mini-o3-mini/1110750/7 + """ + if is_openai_reasoning_model(model): + return True + else: + return False def supports_temperature_param(model: str) -> bool: @@ -102,7 +114,7 @@ class OpenAIClient(LLMClientBase): put_inner_thoughts_first=True, ) - use_developer_message = is_openai_reasoning_model(llm_config.model) + use_developer_message = accepts_developer_role(llm_config.model) openai_message_list = [ cast_message_to_subtype( diff --git a/letta/schemas/providers.py b/letta/schemas/providers.py index f067007a..0b9dc2b3 100644 --- a/letta/schemas/providers.py +++ b/letta/schemas/providers.py @@ -201,7 +201,9 @@ class OpenAIProvider(Provider): # for openai, filter models if self.base_url == "https://api.openai.com/v1": allowed_types = ["gpt-4", "o1", "o3"] - disallowed_types = ["transcribe", "search", "realtime", "tts", "audio", "computer"] + # NOTE: o1-mini and o1-preview do not support tool calling + # NOTE: o1-pro is only available in Responses API + disallowed_types = ["transcribe", "search", "realtime", "tts", "audio", "computer", "o1-mini", "o1-preview", "o1-pro"] skip = True for model_type in allowed_types: if model_name.startswith(model_type):