feat: Add verbose error logging on Anthropic client (#1424)

This commit is contained in:
Matthew Zhou 2025-03-26 15:25:16 -07:00 committed by GitHub
parent 6f2dfeb991
commit bcfab1767d

View File

@ -1,6 +1,6 @@
import json import json
import re import re
from typing import List, Optional from typing import List, Optional, Union
import anthropic import anthropic
from anthropic.types import Message as AnthropicMessage from anthropic.types import Message as AnthropicMessage
@ -10,54 +10,51 @@ from letta.llm_api.helpers import add_inner_thoughts_to_functions, unpack_all_in
from letta.llm_api.llm_api_tools import cast_message_to_subtype from letta.llm_api.llm_api_tools import cast_message_to_subtype
from letta.llm_api.llm_client_base import LLMClientBase from letta.llm_api.llm_client_base import LLMClientBase
from letta.local_llm.constants import INNER_THOUGHTS_KWARG, INNER_THOUGHTS_KWARG_DESCRIPTION from letta.local_llm.constants import INNER_THOUGHTS_KWARG, INNER_THOUGHTS_KWARG_DESCRIPTION
from letta.log import get_logger
from letta.schemas.message import Message as PydanticMessage from letta.schemas.message import Message as PydanticMessage
from letta.schemas.openai.chat_completion_request import ChatCompletionRequest, Tool from letta.schemas.openai.chat_completion_request import ChatCompletionRequest, Tool
from letta.schemas.openai.chat_completion_response import ChatCompletionResponse, Choice, FunctionCall from letta.schemas.openai.chat_completion_response import ChatCompletionResponse, Choice, FunctionCall
from letta.schemas.openai.chat_completion_response import Message as ChoiceMessage from letta.schemas.openai.chat_completion_response import Message as ChoiceMessage
from letta.schemas.openai.chat_completion_response import ToolCall, UsageStatistics from letta.schemas.openai.chat_completion_response import ToolCall, UsageStatistics
from letta.services.provider_manager import ProviderManager from letta.services.provider_manager import ProviderManager
from letta.settings import model_settings
DUMMY_FIRST_USER_MESSAGE = "User initializing bootup sequence." DUMMY_FIRST_USER_MESSAGE = "User initializing bootup sequence."
logger = get_logger(__name__)
class AnthropicClient(LLMClientBase): class AnthropicClient(LLMClientBase):
def request(self, request_data: dict) -> dict: def request(self, request_data: dict) -> dict:
""" try:
Performs underlying request to llm and returns raw response. client = self._get_anthropic_client(async_client=False)
""" response = client.beta.messages.create(**request_data, betas=["tools-2024-04-04"])
anthropic_client = None return response.model_dump()
anthropic_override_key = ProviderManager().get_anthropic_override_key() except Exception as e:
if anthropic_override_key: self._handle_anthropic_error(e)
anthropic_client = anthropic.Anthropic(api_key=anthropic_override_key)
elif model_settings.anthropic_api_key:
anthropic_client = anthropic.Anthropic()
else:
raise ValueError("No available Anthropic API key")
response = anthropic_client.beta.messages.create(
**request_data,
betas=["tools-2024-04-04"],
)
return response.model_dump()
async def request_async(self, request_data: dict) -> dict: async def request_async(self, request_data: dict) -> dict:
""" try:
Performs underlying async request to llm and returns raw response. client = self._get_anthropic_client(async_client=True)
""" response = await client.beta.messages.create(**request_data, betas=["tools-2024-04-04"])
anthropic_client = None return response.model_dump()
anthropic_override_key = ProviderManager().get_anthropic_override_key() except Exception as e:
if anthropic_override_key: self._handle_anthropic_error(e)
anthropic_client = anthropic.AsyncAnthropic(api_key=anthropic_override_key)
elif model_settings.anthropic_api_key: def _get_anthropic_client(self, async_client: bool = False) -> Union[anthropic.AsyncAnthropic, anthropic.Anthropic]:
anthropic_client = anthropic.AsyncAnthropic() override_key = ProviderManager().get_anthropic_override_key()
else: if async_client:
raise ValueError("No available Anthropic API key") return anthropic.AsyncAnthropic(api_key=override_key) if override_key else anthropic.AsyncAnthropic()
response = await anthropic_client.beta.messages.create( return anthropic.Anthropic(api_key=override_key) if override_key else anthropic.Anthropic()
**request_data,
betas=["tools-2024-04-04"], def _handle_anthropic_error(self, e: Exception):
) if isinstance(e, anthropic.APIConnectionError):
return response.model_dump() logger.warning(f"[Anthropic] API connection error: {e.__cause__}")
elif isinstance(e, anthropic.RateLimitError):
logger.warning("[Anthropic] Rate limited (429). Consider backoff.")
elif isinstance(e, anthropic.APIStatusError):
logger.warning(f"[Anthropic] API status error: {e.status_code}, {e.response}")
raise e
def build_request_data( def build_request_data(
self, self,