11from functools import wraps
2+ import json
23
34import sentry_sdk
45from sentry_sdk import consts
2122try :
2223 from openai .resources .chat .completions import Completions , AsyncCompletions
2324 from openai .resources import Embeddings , AsyncEmbeddings
25+ from openai .resources .responses import Responses
2426
2527 if TYPE_CHECKING :
2628 from openai .types .chat import ChatCompletionMessageParam , ChatCompletionChunk
@@ -47,6 +49,7 @@ def setup_once():
4749 # type: () -> None
4850 Completions .create = _wrap_chat_completion_create (Completions .create )
4951 Embeddings .create = _wrap_embeddings_create (Embeddings .create )
52+ Responses .create = _wrap_responses_create (Responses .create )
5053
5154 AsyncCompletions .create = _wrap_async_chat_completion_create (
5255 AsyncCompletions .create
@@ -74,44 +77,46 @@ def _calculate_chat_completion_usage(
7477 messages , response , span , streaming_message_responses , count_tokens
7578):
7679 # type: (Iterable[ChatCompletionMessageParam], Any, Span, Optional[List[str]], Callable[..., Any]) -> None
77- completion_tokens = 0 # type: Optional[int]
78- prompt_tokens = 0 # type: Optional[int]
80+ input_tokens = 0 # type: Optional[int]
81+ output_tokens = 0 # type: Optional[int]
7982 total_tokens = 0 # type: Optional[int]
8083 if hasattr (response , "usage" ):
81- if hasattr (response .usage , "completion_tokens " ) and isinstance (
82- response .usage .completion_tokens , int
84+ if hasattr (response .usage , "input_tokens " ) and isinstance (
85+ response .usage .input_tokens , int
8386 ):
84- completion_tokens = response .usage .completion_tokens
85- if hasattr (response .usage , "prompt_tokens" ) and isinstance (
86- response .usage .prompt_tokens , int
87+ input_tokens = response .usage .input_tokens
88+
89+ if hasattr (response .usage , "output_tokens" ) and isinstance (
90+ response .usage .output_tokens , int
8791 ):
88- prompt_tokens = response .usage .prompt_tokens
92+ output_tokens = response .usage .output_tokens
93+
8994 if hasattr (response .usage , "total_tokens" ) and isinstance (
9095 response .usage .total_tokens , int
9196 ):
9297 total_tokens = response .usage .total_tokens
9398
94- if prompt_tokens == 0 :
99+ if input_tokens == 0 :
95100 for message in messages :
96101 if "content" in message :
97- prompt_tokens += count_tokens (message ["content" ])
102+ input_tokens += count_tokens (message ["content" ])
98103
99- if completion_tokens == 0 :
104+ if output_tokens == 0 :
100105 if streaming_message_responses is not None :
101106 for message in streaming_message_responses :
102- completion_tokens += count_tokens (message )
107+ output_tokens += count_tokens (message )
103108 elif hasattr (response , "choices" ):
104109 for choice in response .choices :
105110 if hasattr (choice , "message" ):
106- completion_tokens += count_tokens (choice .message )
111+ output_tokens += count_tokens (choice .message )
107112
108- if prompt_tokens == 0 :
109- prompt_tokens = None
110- if completion_tokens == 0 :
111- completion_tokens = None
113+ if input_tokens == 0 :
114+ input_tokens = None
115+ if output_tokens == 0 :
116+ output_tokens = None
112117 if total_tokens == 0 :
113118 total_tokens = None
114- record_token_usage (span , prompt_tokens , completion_tokens , total_tokens )
119+ record_token_usage (span , input_tokens , output_tokens , total_tokens )
115120
116121
117122def _new_chat_completion_common (f , * args , ** kwargs ):
@@ -427,3 +432,80 @@ async def _sentry_patched_create_async(*args, **kwargs):
427432 return await _execute_async (f , * args , ** kwargs )
428433
429434 return _sentry_patched_create_async
435+
436+
437+ def _new_responses_create_common (f , * args , ** kwargs ):
438+ # type: (Any, *Any, **Any) -> Any
439+ integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
440+ if integration is None :
441+ return f (* args , ** kwargs )
442+
443+ model = kwargs .get ("model" )
444+ input = kwargs .get ("input" )
445+
446+ span = sentry_sdk .start_span (
447+ op = consts .OP .GEN_AI_RESPONSES ,
448+ name = f"{ consts .OP .GEN_AI_RESPONSES } { model } " ,
449+ origin = OpenAIIntegration .origin ,
450+ )
451+ span .__enter__ ()
452+
453+ set_data_normalized (span , SPANDATA .GEN_AI_REQUEST_MODEL , model )
454+
455+ if should_send_default_pii () and integration .include_prompts :
456+ set_data_normalized (span , SPANDATA .GEN_AI_REQUEST_MESSAGES , input )
457+
458+ res = yield f , args , kwargs
459+
460+ if hasattr (res , "output" ):
461+ if should_send_default_pii () and integration .include_prompts :
462+ set_data_normalized (
463+ span ,
464+ SPANDATA .GEN_AI_RESPONSE_TEXT ,
465+ json .dumps ([item .to_dict () for item in res .output ]),
466+ )
467+ import ipdb
468+
469+ ipdb .set_trace ()
470+ _calculate_chat_completion_usage ([], res , span , None , integration .count_tokens )
471+ span .__exit__ (None , None , None )
472+
473+ else :
474+ set_data_normalized (span , "unknown_response" , True )
475+ span .__exit__ (None , None , None )
476+
477+ return res
478+
479+
480+ def _wrap_responses_create (f ):
481+ # type: (Any) -> Any
482+ def _execute_sync (f , * args , ** kwargs ):
483+ # type: (Any, *Any, **Any) -> Any
484+ gen = _new_responses_create_common (f , * args , ** kwargs )
485+
486+ try :
487+ f , args , kwargs = next (gen )
488+ except StopIteration as e :
489+ return e .value
490+
491+ try :
492+ try :
493+ result = f (* args , ** kwargs )
494+ except Exception as e :
495+ _capture_exception (e )
496+ raise e from None
497+
498+ return gen .send (result )
499+ except StopIteration as e :
500+ return e .value
501+
502+ @wraps (f )
503+ def _sentry_patched_create_sync (* args , ** kwargs ):
504+ # type: (*Any, **Any) -> Any
505+ integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
506+ if integration is None :
507+ return f (* args , ** kwargs )
508+
509+ return _execute_sync (f , * args , ** kwargs )
510+
511+ return _sentry_patched_create_sync
0 commit comments