Skip to content

Commit

Permalink
Catch 409's (#375)
Browse files Browse the repository at this point in the history
  • Loading branch information
hinthornw authored Jan 24, 2024
1 parent 482c13c commit 5bed311
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 0 deletions.
21 changes: 21 additions & 0 deletions python/langsmith/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -425,6 +425,7 @@ def request_with_retries(
request_kwargs: Mapping,
stop_after_attempt: int = 1,
retry_on: Optional[Sequence[Type[BaseException]]] = None,
to_ignore: Optional[Sequence[Type[BaseException]]] = None,
) -> requests.Response:
"""Send a request with retries.
Expand All @@ -436,6 +437,13 @@ def request_with_retries(
The URL to send the request to.
request_kwargs : Mapping
Additional request parameters.
stop_after_attempt : int, default=1
The number of attempts to make.
retry_on : Sequence[Type[BaseException]] or None, default=None
The exceptions to retry on. In addition to:
[LangSmithConnectionError, LangSmithAPIError].
to_ignore : Sequence[Type[BaseException]] or None, default=None
The exceptions to ignore / pass on.
Returns
-------
Expand All @@ -458,6 +466,8 @@ def request_with_retries(
*(retry_on or []),
*(ls_utils.LangSmithConnectionError, ls_utils.LangSmithAPIError),
)
to_ignore_: Tuple[Type[BaseException], ...] = (*(to_ignore or ()),)
response = None
for idx in range(stop_after_attempt):
try:
try:
Expand Down Expand Up @@ -486,6 +496,10 @@ def request_with_retries(
raise ls_utils.LangSmithNotFoundError(
f"Resource not found for {url}. {repr(e)}"
)
elif response.status_code == 409:
raise ls_utils.LangSmithConflictError(
f"Conflict for {url}. {repr(e)}"
)
else:
raise ls_utils.LangSmithError(
f"Failed to {request_method} {url} in LangSmith"
Expand All @@ -511,6 +525,11 @@ def request_with_retries(
raise ls_utils.LangSmithError(
f"Failed to {request_method} {url} in LangSmith API. {emsg}"
) from e
except to_ignore_ as e:
if response is not None:
logger.debug("Passing on exception %s", e)
return response
# Else we still raise an error
except retry_on_:
if idx + 1 == stop_after_attempt:
raise
Expand Down Expand Up @@ -868,6 +887,7 @@ def create_run(
"headers": headers,
"timeout": self.timeout_ms / 1000,
},
to_ignore=(ls_utils.LangSmithConflictError,),
)

def batch_ingest_runs(
Expand Down Expand Up @@ -961,6 +981,7 @@ def batch_ingest_runs(
"Content-Type": "application/json",
},
},
to_ignore=(ls_utils.LangSmithConflictError,),
)

def update_run(
Expand Down
4 changes: 4 additions & 0 deletions python/langsmith/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ class LangSmithNotFoundError(LangSmithError):
"""Couldn't find the requested resource."""


class LangSmithConflictError(LangSmithError):
"""The resource already exists."""


class LangSmithConnectionError(LangSmithError):
"""Couldn't connect to the LangSmith API."""

Expand Down
20 changes: 20 additions & 0 deletions python/tests/unit_tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,26 @@ def test_http_status_500_handling(mock_sleep):
assert mock_session.request.call_count == 2


@patch("langsmith.client.time.sleep")
def test_pass_on_409_handling(mock_sleep):
client = Client(api_key="test")
with patch.object(client, "session") as mock_session:
mock_response = MagicMock()
mock_response.status_code = 409
mock_response.raise_for_status.side_effect = HTTPError()
mock_session.request.return_value = mock_response

response = client.request_with_retries(
"GET",
"https://test.url",
{},
stop_after_attempt=5,
to_ignore=[ls_utils.LangSmithConflictError],
)
assert mock_session.request.call_count == 1
assert response == mock_response


@patch("langsmith.client.ls_utils.raise_for_status_with_text")
def test_http_status_429_handling(mock_raise_for_status):
client = Client(api_key="test")
Expand Down

0 comments on commit 5bed311

Please sign in to comment.