Skip to content

Commit a073290

Browse files
ref(openai): Separate sync and async Completions patches
1 parent 98294ce commit a073290

1 file changed

Lines changed: 94 additions & 46 deletions

File tree

sentry_sdk/integrations/openai.py

Lines changed: 94 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ def _set_common_output_data(
660660
span.__exit__(None, None, None)
661661

662662

663-
def _new_chat_completion_common(f: "Any", *args: "Any", **kwargs: "Any") -> "Any":
663+
def _new_sync_chat_completion(f: "Any", *args: "Any", **kwargs: "Any") -> "Any":
664664
integration = sentry_sdk.get_client().get_integration(OpenAIIntegration)
665665
if integration is None:
666666
return f(*args, **kwargs)
@@ -693,7 +693,97 @@ def _new_chat_completion_common(f: "Any", *args: "Any", **kwargs: "Any") -> "Any
693693
_set_completions_api_input_data(span, kwargs, integration)
694694

695695
start_time = time.perf_counter()
696-
response = yield f, args, kwargs
696+
697+
try:
698+
response = f(*args, **kwargs)
699+
except Exception as exc:
700+
exc_info = sys.exc_info()
701+
with capture_internal_exceptions():
702+
_capture_exception(exc)
703+
reraise(*exc_info)
704+
705+
# Attribute check to fail gracefully if the attribute is not present in future `openai` versions.
706+
if isinstance(response, Stream) and hasattr(response, "_iterator"):
707+
messages = kwargs.get("messages")
708+
709+
if messages is not None and isinstance(messages, str):
710+
messages = [messages]
711+
712+
response._iterator = _wrap_synchronous_completions_chunk_iterator(
713+
span=span,
714+
integration=integration,
715+
start_time=start_time,
716+
messages=messages,
717+
response=response,
718+
old_iterator=response._iterator,
719+
finish_span=True,
720+
)
721+
722+
# Attribute check to fail gracefully if the attribute is not present in future `openai` versions.
723+
elif isinstance(response, AsyncStream) and hasattr(response, "_iterator"):
724+
messages = kwargs.get("messages")
725+
726+
if messages is not None and isinstance(messages, str):
727+
messages = [messages]
728+
729+
response._iterator = _wrap_asynchronous_completions_chunk_iterator(
730+
span=span,
731+
integration=integration,
732+
start_time=start_time,
733+
messages=messages,
734+
response=response,
735+
old_iterator=response._iterator,
736+
finish_span=True,
737+
)
738+
else:
739+
_set_completions_api_output_data(
740+
span, response, kwargs, integration, finish_span=True
741+
)
742+
743+
return response
744+
745+
746+
async def _new_async_chat_completion(f: "Any", *args: "Any", **kwargs: "Any") -> "Any":
747+
integration = sentry_sdk.get_client().get_integration(OpenAIIntegration)
748+
if integration is None:
749+
return await f(*args, **kwargs)
750+
751+
if "messages" not in kwargs:
752+
# invalid call (in all versions of openai), let it return error
753+
return await f(*args, **kwargs)
754+
755+
try:
756+
iter(kwargs["messages"])
757+
except TypeError:
758+
# invalid call (in all versions), messages must be iterable
759+
return await f(*args, **kwargs)
760+
761+
model = kwargs.get("model")
762+
763+
span = sentry_sdk.start_span(
764+
op=consts.OP.GEN_AI_CHAT,
765+
name=f"chat {model}",
766+
origin=OpenAIIntegration.origin,
767+
)
768+
span.__enter__()
769+
770+
span.set_data(SPANDATA.GEN_AI_SYSTEM, "openai")
771+
772+
# Same bool handling as in https://github.com/openai/openai-python/blob/acd0c54d8a68efeedde0e5b4e6c310eef1ce7867/src/openai/resources/completions.py#L585
773+
is_streaming_response = kwargs.get("stream", False) or False
774+
span.set_data(SPANDATA.GEN_AI_RESPONSE_STREAMING, is_streaming_response)
775+
776+
_set_completions_api_input_data(span, kwargs, integration)
777+
778+
start_time = time.perf_counter()
779+
780+
try:
781+
response = await f(*args, **kwargs)
782+
except Exception as exc:
783+
exc_info = sys.exc_info()
784+
with capture_internal_exceptions():
785+
_capture_exception(exc)
786+
reraise(*exc_info)
697787

698788
# Attribute check to fail gracefully if the attribute is not present in future `openai` versions.
699789
if isinstance(response, Stream) and hasattr(response, "_iterator"):
@@ -1050,69 +1140,27 @@ def _set_embeddings_output_data(
10501140

10511141

10521142
def _wrap_chat_completion_create(f: "Callable[..., Any]") -> "Callable[..., Any]":
1053-
def _execute_sync(f: "Any", *args: "Any", **kwargs: "Any") -> "Any":
1054-
gen = _new_chat_completion_common(f, *args, **kwargs)
1055-
1056-
try:
1057-
f, args, kwargs = next(gen)
1058-
except StopIteration as e:
1059-
return e.value
1060-
1061-
try:
1062-
try:
1063-
result = f(*args, **kwargs)
1064-
except Exception as e:
1065-
exc_info = sys.exc_info()
1066-
with capture_internal_exceptions():
1067-
_capture_exception(e)
1068-
reraise(*exc_info)
1069-
1070-
return gen.send(result)
1071-
except StopIteration as e:
1072-
return e.value
1073-
10741143
@wraps(f)
10751144
def _sentry_patched_create_sync(*args: "Any", **kwargs: "Any") -> "Any":
10761145
integration = sentry_sdk.get_client().get_integration(OpenAIIntegration)
10771146
if integration is None or "messages" not in kwargs:
10781147
# no "messages" means invalid call (in all versions of openai), let it return error
10791148
return f(*args, **kwargs)
10801149

1081-
return _execute_sync(f, *args, **kwargs)
1150+
return _new_sync_chat_completion(f, *args, **kwargs)
10821151

10831152
return _sentry_patched_create_sync
10841153

10851154

10861155
def _wrap_async_chat_completion_create(f: "Callable[..., Any]") -> "Callable[..., Any]":
1087-
async def _execute_async(f: "Any", *args: "Any", **kwargs: "Any") -> "Any":
1088-
gen = _new_chat_completion_common(f, *args, **kwargs)
1089-
1090-
try:
1091-
f, args, kwargs = next(gen)
1092-
except StopIteration as e:
1093-
return await e.value
1094-
1095-
try:
1096-
try:
1097-
result = await f(*args, **kwargs)
1098-
except Exception as e:
1099-
exc_info = sys.exc_info()
1100-
with capture_internal_exceptions():
1101-
_capture_exception(e)
1102-
reraise(*exc_info)
1103-
1104-
return gen.send(result)
1105-
except StopIteration as e:
1106-
return e.value
1107-
11081156
@wraps(f)
11091157
async def _sentry_patched_create_async(*args: "Any", **kwargs: "Any") -> "Any":
11101158
integration = sentry_sdk.get_client().get_integration(OpenAIIntegration)
11111159
if integration is None or "messages" not in kwargs:
11121160
# no "messages" means invalid call (in all versions of openai), let it return error
11131161
return await f(*args, **kwargs)
11141162

1115-
return await _execute_async(f, *args, **kwargs)
1163+
return await _new_async_chat_completion(f, *args, **kwargs)
11161164

11171165
return _sentry_patched_create_async
11181166

0 commit comments

Comments
 (0)