Repository navigation
Exception creation, new testing, raising exception in retrieve, Fixes #3212 #3723
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
76bd1aa
4db0f14
1045f69
283adc9
495a899
be573c5
70f524d
2546827
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,6 +11,7 @@ | |
|
|
||
| if TYPE_CHECKING: | ||
| from .types.chat import ChatCompletion | ||
| from .types.responses.response import Response | ||
|
|
||
| __all__ = [ | ||
| "BadRequestError", | ||
|
|
@@ -24,6 +25,9 @@ | |
| "InternalServerError", | ||
| "LengthFinishReasonError", | ||
| "ContentFilterFinishReasonError", | ||
| "ResponseNotCompletedError", | ||
| "ResponseFailedError", | ||
| "ResponseIncompleteError", | ||
| "InvalidWebhookSignatureError", | ||
| "SubjectTokenProviderError", | ||
| "WebSocketConnectionClosedError", | ||
|
|
@@ -193,6 +197,56 @@ def __init__(self, *, completion: ChatCompletion | None = None) -> None: | |
| self.completion = completion | ||
|
|
||
|
|
||
| class ResponseNotCompletedError(OpenAIError): | ||
| """A Response reached a final state other than `completed`. | ||
|
|
||
| Carries no HTTP status code: the request that surfaced the failure succeeded. | ||
| """ | ||
|
|
||
| response: Response | ||
|
|
||
| retryable: bool | ||
| """Whether submitting a new `create()` call is worth trying. | ||
|
|
||
| Never means re-`retrieve()`ing this id, which is immutable once terminal. | ||
| """ | ||
|
|
||
| def __init__(self, message: str, *, response: Response, retryable: bool) -> None: | ||
| super().__init__(message) | ||
| self.response = response | ||
| self.retryable = retryable | ||
|
|
||
| @property | ||
| def request_id(self) -> str | None: | ||
| return getattr(self.response, "_request_id", None) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When Useful? React with 👍 / 👎. |
||
|
|
||
|
|
||
| class ResponseFailedError(ResponseNotCompletedError): | ||
| """A Response finished with `status="failed"`.""" | ||
|
|
||
| code: Optional[str] | ||
| """`response.error.code`, or `None` when the API reported no error object.""" | ||
|
|
||
| def __init__(self, message: str, *, response: Response, code: Optional[str], retryable: bool) -> None: | ||
| super().__init__(message, response=response, retryable=retryable) | ||
| self.code = code | ||
|
|
||
|
|
||
| class ResponseIncompleteError(ResponseNotCompletedError): | ||
| """A Response finished with `status="incomplete"`. | ||
|
|
||
| Not necessarily a failure: a run that stopped at `max_output_tokens` still | ||
| produced output on `.response.output`. | ||
| """ | ||
|
|
||
| reason: Optional[str] | ||
| """`response.incomplete_details.reason`, or `None` when the API reported no reason.""" | ||
|
|
||
| def __init__(self, message: str, *, response: Response, reason: Optional[str], retryable: bool) -> None: | ||
| super().__init__(message, response=response, retryable=retryable) | ||
| self.reason = reason | ||
|
|
||
|
|
||
| class InvalidWebhookSignatureError(ValueError): | ||
| """Raised when a webhook signature is invalid, meaning the computed signature does not match the expected signature.""" | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| from __future__ import annotations | ||
|
|
||
| from .._exceptions import ResponseFailedError, ResponseIncompleteError | ||
| from ..types.responses.response import Response | ||
|
|
||
| __all__ = ["raise_for_status"] | ||
|
|
||
| _RETRYABLE_ERROR_CODES = frozenset({"server_error", "rate_limit_exceeded", "vector_store_timeout"}) | ||
|
|
||
|
|
||
| def raise_for_status(response: Response, *, raise_on_incomplete: bool = False) -> None: | ||
| """Raise if `response` reached a final state other than `completed`. | ||
|
|
||
| A background run reports its own failure inside a `200 OK` poll, so `retrieve()` | ||
| returns it instead of raising. Call this to opt into an exception. `"cancelled"` | ||
| is caller-initiated and does not raise. | ||
|
|
||
| `status="incomplete"` only raises when `raise_on_incomplete` is set: the usual | ||
| reason is `max_output_tokens`, which still leaves usable output on | ||
| `response.output`. | ||
| """ | ||
| if response.status == "failed": | ||
| error = response.error | ||
| if error is None: | ||
| raise ResponseFailedError( | ||
| "Response failed without an error code.", | ||
| response=response, | ||
| code=None, | ||
| retryable=False, | ||
| ) | ||
|
|
||
| raise ResponseFailedError( | ||
| error.message, | ||
| response=response, | ||
| code=error.code, | ||
| retryable=error.code in _RETRYABLE_ERROR_CODES, | ||
| ) | ||
|
|
||
| if response.status == "incomplete" and raise_on_incomplete: | ||
| reason = response.incomplete_details.reason if response.incomplete_details else None | ||
| raise ResponseIncompleteError( | ||
| f"Response was incomplete: {reason or 'no reason reported'}.", | ||
| response=response, | ||
| reason=reason, | ||
| retryable=False, | ||
| ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
src/openai/__init__.pyis Castiron-owned, but this commit hand-edits its imports and__all__without updating the generation configuration or inventory (the manifest still records the parent checksum). The next routine schema regeneration can therefore remove these required package-root exports and break the documentedfrom openai import ResponseFailedError; add the exports through the generator configuration/compiler and regenerate the file instead.AGENTS.md reference: AGENTS.md:L5-L7
Useful? React with 👍 / 👎.