Skip to content

Conversation

@dsfaccini
Copy link
Contributor

@dsfaccini dsfaccini commented Nov 20, 2025

Fixes #3439

wraps openai.APIConnectionError in ModelHTTPError to enable fallback when on connection errors

raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
raise # pragma: lax no cover
except APIConnectionError as e:
raise ModelHTTPError(status_code=0, model_name=self.model_name, body=str(e)) from e
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As I'm sure you also noticed when you wrote this, we're kind of abusing the ModelHTTPError class now because this is not actually an HTTP response with status code 0.

I suggest we add a new superclass of ModelHTTPError like ModelAPIError without a status_code field, and raise that instead.

We should update FallbackModel to have that in the default on_fallback list (and update the docs if needed)

And I think we should review every single model class and raise the new error for non-HTTP errors.

I think Bedrock currently incorrectly raises ModelHTTPError for non-HTTP errors as well, so that one will need further tweaking.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah that's part of what we discussed that handling connection errors is a bit outside the original job of the FallbackModel

  • I'll create the new class and add it as default to the FallbackModel.on_fallback
  • I'll check wherever we're already testing the FallbackModel (mainly test_fallback).
  • will try to just add it to the exsiting tests instead of duplicating the amount of tests.
  • will checkout the Bedrock case

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just pushed an update with these changes, CI fails cause of the flaky test_outlines so can't verify coverage, but the PR is in a reviewable state

if (status_code := e.status_code) >= 400:
raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
raise # pragma: lax no cover
except APIConnectionError as e:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not the top level APIError?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please also update all the other models!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will do, I only looked at google but I didn't see any logic handling it and remembered there's another PR adding support for Fallback for the google models, is that still open?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looking closer at this, there are three exceptions that inherit from APIError

class APIResponseValidationError(APIError):
class APIStatusError(APIError):
class APIConnectionError(APIError):

we were already handling APIStatusError, now this PR is handling APIConnectionError, but IMO APIResponseValidationError doesn't really belong with the other two, and in a case of bad validation the model should retry instead of falling back.

to be clearer, the two we're handling are issues that likely won't fix themselves, like a bad credential, a bad request, connection or server issues, while a validation error should be rare and should fix itself. when it doesn't fix itself it provides information to the user: "this model doesn't handle your use case very well apparently".

does that make sense?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep makes sense

@DouweM DouweM changed the title fallback on openai api connection errors Fallback on model connection errors, not just HTTP status 400+ Nov 21, 2025
model_name: str
"""The name of the model associated with the error."""

def __init__(self, model_name: str, message: str | None = None):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we require a message instead of falling back on model_name: ...?

if (status_code := e.status_code) >= 400:
raise ModelHTTPError(status_code=status_code, model_name=self.model_name, body=e.body) from e
raise # pragma: lax no cover
except APIConnectionError as e:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep makes sense

@DouweM DouweM enabled auto-merge (squash) November 21, 2025 22:54
@DouweM DouweM disabled auto-merge November 21, 2025 22:54
@DouweM DouweM enabled auto-merge (squash) November 21, 2025 22:54
@DouweM DouweM changed the title Fallback on model connection errors, not just HTTP status 400+ Make FallbackModel fall back on all model API errors, not just HTTP status 400+ Nov 21, 2025
auto-merge was automatically disabled November 21, 2025 23:08

Head branch was pushed to by a user without write access

@DouweM DouweM enabled auto-merge (squash) November 21, 2025 23:19
auto-merge was automatically disabled November 21, 2025 23:39

Head branch was pushed to by a user without write access

@DouweM DouweM enabled auto-merge (squash) November 21, 2025 23:40
@DouweM DouweM merged commit ab6de27 into pydantic:main Nov 22, 2025
29 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

FallbackModel doesn't fallback when OpenAIChatModel connection fails

2 participants