diff --git a/litellm/integrations/langsmith.py b/litellm/integrations/langsmith.py index 4c98788cb..424d18b6f 100644 --- a/litellm/integrations/langsmith.py +++ b/litellm/integrations/langsmith.py @@ -9,9 +9,12 @@ import uuid from datetime import datetime, timezone from typing import Any, List, Optional, Union +import backoff import dotenv # type: ignore import httpx import requests # type: ignore +from backoff import on_exception +from backoff._typing import Details from pydantic import BaseModel # type: ignore import litellm @@ -55,6 +58,12 @@ def is_serializable(value): return not isinstance(value, non_serializable_types) +def on_backoff(details: Details) -> None: + verbose_logger.warning( + f"Langsmith batch send failed. Retrying in {details['wait']} seconds. Attempt {details['tries']}/3" + ) + + class LangsmithLogger(CustomLogger): def __init__(self): self.langsmith_api_key = os.getenv("LANGSMITH_API_KEY") @@ -277,11 +286,23 @@ class LangsmithLogger(CustomLogger): ) if len(self.log_queue) >= self.batch_size: await self._async_send_batch() - + self.log_queue.clear() except: verbose_logger.error(f"Langsmith Layer Error - {traceback.format_exc()}") + @on_exception( + backoff.expo, (httpx.HTTPError, Exception), max_tries=3, on_backoff=on_backoff + ) async def _async_send_batch(self): + """ + sends runs to /batch endpoint + + Sends runs from self.log_queue + + Returns: None + + Raises: Does not raise an exception, will only verbose_logger.exception() + """ import json if not self.log_queue: @@ -308,14 +329,14 @@ class LangsmithLogger(CustomLogger): verbose_logger.debug( f"Batch of {len(self.log_queue)} runs successfully created" ) - - self.log_queue.clear() except httpx.HTTPStatusError as e: - verbose_logger.error( + verbose_logger.exception( f"Langsmith HTTP Error: {e.response.status_code} - {e.response.text}" ) except Exception as e: - verbose_logger.error(f"Langsmith Layer Error - {traceback.format_exc()}") + verbose_logger.exception( + f"Langsmith Layer Error - {traceback.format_exc()}" + ) def get_run_by_id(self, run_id):