Commit 23b96159

Robert Craigie <robert@craigie.dev>
2024-09-10 23:46:51
feat(errors): include completion in LengthFinishReasonError (#1701)
1 parent 6b07089
Changed files (3)
src
openai
src/openai/lib/_parsing/_completions.py
@@ -69,7 +69,7 @@ def parse_chat_completion(
     choices: list[ParsedChoice[ResponseFormatT]] = []
     for choice in chat_completion.choices:
         if choice.finish_reason == "length":
-            raise LengthFinishReasonError()
+            raise LengthFinishReasonError(completion=chat_completion)
 
         if choice.finish_reason == "content_filter":
             raise ContentFilterFinishReasonError()
src/openai/lib/streaming/chat/_completions.py
@@ -394,7 +394,9 @@ class ChatCompletionStreamState(Generic[ResponseFormatT]):
 
                 if has_parseable_input(response_format=self._response_format, input_tools=self._input_tools):
                     if choice.finish_reason == "length":
-                        raise LengthFinishReasonError()
+                        # at the time of writing, `.usage` will always be `None` but
+                        # we include it here in case that is changed in the future
+                        raise LengthFinishReasonError(completion=completion_snapshot)
 
                     if choice.finish_reason == "content_filter":
                         raise ContentFilterFinishReasonError()
src/openai/_exceptions.py
@@ -2,7 +2,7 @@
 
 from __future__ import annotations
 
-from typing import Any, Optional, cast
+from typing import TYPE_CHECKING, Any, Optional, cast
 from typing_extensions import Literal
 
 import httpx
@@ -10,6 +10,9 @@ import httpx
 from ._utils import is_dict
 from ._models import construct_type
 
+if TYPE_CHECKING:
+    from .types.chat import ChatCompletion
+
 __all__ = [
     "BadRequestError",
     "AuthenticationError",
@@ -130,10 +133,20 @@ class InternalServerError(APIStatusError):
 
 
 class LengthFinishReasonError(OpenAIError):
-    def __init__(self) -> None:
-        super().__init__(
-            f"Could not parse response content as the length limit was reached",
-        )
+    completion: ChatCompletion
+    """The completion that caused this error.
+
+    Note: this will *not* be a complete `ChatCompletion` object when streaming as `usage`
+          will not be included.
+    """
+
+    def __init__(self, *, completion: ChatCompletion) -> None:
+        msg = "Could not parse response content as the length limit was reached"
+        if completion.usage:
+            msg += f" - {completion.usage}"
+
+        super().__init__(msg)
+        self.completion = completion
 
 
 class ContentFilterFinishReasonError(OpenAIError):