11from functools import wraps
2+ import json
23
34import sentry_sdk
45from sentry_sdk import consts
2728except ImportError :
2829 raise DidNotEnable ("OpenAI not installed" )
2930
31+ RESPONSES_API_ENABLED = True
32+ try :
33+ # responses API support was introduced in v1.66.0
34+ from openai .resources .responses import Responses , AsyncResponses
35+ except ImportError :
36+ RESPONSES_API_ENABLED = False
37+
3038
3139class OpenAIIntegration (Integration ):
3240 identifier = "openai"
@@ -46,13 +54,17 @@ def __init__(self, include_prompts=True, tiktoken_encoding_name=None):
4654 def setup_once ():
4755 # type: () -> None
4856 Completions .create = _wrap_chat_completion_create (Completions .create )
49- Embeddings .create = _wrap_embeddings_create (Embeddings .create )
50-
5157 AsyncCompletions .create = _wrap_async_chat_completion_create (
5258 AsyncCompletions .create
5359 )
60+
61+ Embeddings .create = _wrap_embeddings_create (Embeddings .create )
5462 AsyncEmbeddings .create = _wrap_async_embeddings_create (AsyncEmbeddings .create )
5563
64+ if RESPONSES_API_ENABLED :
65+ Responses .create = _wrap_responses_create (Responses .create )
66+ AsyncResponses .create = _wrap_async_responses_create (AsyncResponses .create )
67+
5668 def count_tokens (self , s ):
5769 # type: (OpenAIIntegration, str) -> int
5870 if self .tiktoken_encoding is not None :
@@ -62,6 +74,12 @@ def count_tokens(self, s):
6274
6375def _capture_exception (exc ):
6476 # type: (Any) -> None
77+ # Close an eventually open span
78+ # We need to do this by hand because we are not using the start_span context manager
79+ current_span = sentry_sdk .get_current_span ()
80+ if current_span is not None :
81+ current_span .__exit__ (None , None , None )
82+
6583 event , hint = event_from_exception (
6684 exc ,
6785 client_options = sentry_sdk .get_client ().options ,
@@ -140,7 +158,7 @@ def _calculate_token_usage(
140158
141159
142160def _new_chat_completion_common (f , * args , ** kwargs ):
143- # type: (Any, * Any, ** Any) -> Any
161+ # type: (Any, Any, Any) -> Any
144162 integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
145163 if integration is None :
146164 return f (* args , ** kwargs )
@@ -270,7 +288,7 @@ async def new_iterator_async():
270288def _wrap_chat_completion_create (f ):
271289 # type: (Callable[..., Any]) -> Callable[..., Any]
272290 def _execute_sync (f , * args , ** kwargs ):
273- # type: (Any, * Any, ** Any) -> Any
291+ # type: (Any, Any, Any) -> Any
274292 gen = _new_chat_completion_common (f , * args , ** kwargs )
275293
276294 try :
@@ -291,7 +309,7 @@ def _execute_sync(f, *args, **kwargs):
291309
292310 @wraps (f )
293311 def _sentry_patched_create_sync (* args , ** kwargs ):
294- # type: (* Any, ** Any) -> Any
312+ # type: (Any, Any) -> Any
295313 integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
296314 if integration is None or "messages" not in kwargs :
297315 # no "messages" means invalid call (in all versions of openai), let it return error
@@ -305,7 +323,7 @@ def _sentry_patched_create_sync(*args, **kwargs):
305323def _wrap_async_chat_completion_create (f ):
306324 # type: (Callable[..., Any]) -> Callable[..., Any]
307325 async def _execute_async (f , * args , ** kwargs ):
308- # type: (Any, * Any, ** Any) -> Any
326+ # type: (Any, Any, Any) -> Any
309327 gen = _new_chat_completion_common (f , * args , ** kwargs )
310328
311329 try :
@@ -326,7 +344,7 @@ async def _execute_async(f, *args, **kwargs):
326344
327345 @wraps (f )
328346 async def _sentry_patched_create_async (* args , ** kwargs ):
329- # type: (* Any, ** Any) -> Any
347+ # type: (Any, Any) -> Any
330348 integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
331349 if integration is None or "messages" not in kwargs :
332350 # no "messages" means invalid call (in all versions of openai), let it return error
@@ -338,7 +356,7 @@ async def _sentry_patched_create_async(*args, **kwargs):
338356
339357
340358def _new_embeddings_create_common (f , * args , ** kwargs ):
341- # type: (Any, * Any, ** Any) -> Any
359+ # type: (Any, Any, Any) -> Any
342360 integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
343361 if integration is None :
344362 return f (* args , ** kwargs )
@@ -350,6 +368,8 @@ def _new_embeddings_create_common(f, *args, **kwargs):
350368 name = f"{ consts .OP .GEN_AI_EMBEDDINGS } { model } " ,
351369 origin = OpenAIIntegration .origin ,
352370 ) as span :
371+ set_data_normalized (span , SPANDATA .GEN_AI_REQUEST_MODEL , model )
372+
353373 if "input" in kwargs and (
354374 should_send_default_pii () and integration .include_prompts
355375 ):
@@ -365,8 +385,6 @@ def _new_embeddings_create_common(f, *args, **kwargs):
365385 set_data_normalized (
366386 span , SPANDATA .GEN_AI_REQUEST_MESSAGES , kwargs ["input" ]
367387 )
368- if "model" in kwargs :
369- set_data_normalized (span , SPANDATA .GEN_AI_REQUEST_MODEL , kwargs ["model" ])
370388
371389 response = yield f , args , kwargs
372390
@@ -397,7 +415,7 @@ def _new_embeddings_create_common(f, *args, **kwargs):
397415def _wrap_embeddings_create (f ):
398416 # type: (Any) -> Any
399417 def _execute_sync (f , * args , ** kwargs ):
400- # type: (Any, * Any, ** Any) -> Any
418+ # type: (Any, Any, Any) -> Any
401419 gen = _new_embeddings_create_common (f , * args , ** kwargs )
402420
403421 try :
@@ -418,7 +436,7 @@ def _execute_sync(f, *args, **kwargs):
418436
419437 @wraps (f )
420438 def _sentry_patched_create_sync (* args , ** kwargs ):
421- # type: (* Any, ** Any) -> Any
439+ # type: (Any, Any) -> Any
422440 integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
423441 if integration is None :
424442 return f (* args , ** kwargs )
@@ -431,7 +449,7 @@ def _sentry_patched_create_sync(*args, **kwargs):
431449def _wrap_async_embeddings_create (f ):
432450 # type: (Any) -> Any
433451 async def _execute_async (f , * args , ** kwargs ):
434- # type: (Any, * Any, ** Any) -> Any
452+ # type: (Any, Any, Any) -> Any
435453 gen = _new_embeddings_create_common (f , * args , ** kwargs )
436454
437455 try :
@@ -452,11 +470,119 @@ async def _execute_async(f, *args, **kwargs):
452470
453471 @wraps (f )
454472 async def _sentry_patched_create_async (* args , ** kwargs ):
455- # type: (* Any, ** Any) -> Any
473+ # type: (Any, Any) -> Any
456474 integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
457475 if integration is None :
458476 return await f (* args , ** kwargs )
459477
460478 return await _execute_async (f , * args , ** kwargs )
461479
462480 return _sentry_patched_create_async
481+
482+
483+ def _new_responses_create_common (f , * args , ** kwargs ):
484+ # type: (Any, Any, Any) -> Any
485+ integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
486+ if integration is None :
487+ return f (* args , ** kwargs )
488+
489+ model = kwargs .get ("model" )
490+ input = kwargs .get ("input" )
491+
492+ span = sentry_sdk .start_span (
493+ op = consts .OP .GEN_AI_RESPONSES ,
494+ name = f"{ consts .OP .GEN_AI_RESPONSES } { model } " ,
495+ origin = OpenAIIntegration .origin ,
496+ )
497+ span .__enter__ ()
498+
499+ set_data_normalized (span , SPANDATA .GEN_AI_REQUEST_MODEL , model )
500+
501+ if should_send_default_pii () and integration .include_prompts :
502+ set_data_normalized (span , SPANDATA .GEN_AI_REQUEST_MESSAGES , input )
503+
504+ res = yield f , args , kwargs
505+
506+ if hasattr (res , "output" ):
507+ if should_send_default_pii () and integration .include_prompts :
508+ set_data_normalized (
509+ span ,
510+ SPANDATA .GEN_AI_RESPONSE_TEXT ,
511+ json .dumps ([item .to_dict () for item in res .output ]),
512+ )
513+ _calculate_token_usage ([], res , span , None , integration .count_tokens )
514+
515+ else :
516+ set_data_normalized (span , "unknown_response" , True )
517+
518+ span .__exit__ (None , None , None )
519+
520+ return res
521+
522+
523+ def _wrap_responses_create (f ):
524+ # type: (Any) -> Any
525+ def _execute_sync (f , * args , ** kwargs ):
526+ # type: (Any, Any, Any) -> Any
527+ gen = _new_responses_create_common (f , * args , ** kwargs )
528+
529+ try :
530+ f , args , kwargs = next (gen )
531+ except StopIteration as e :
532+ return e .value
533+
534+ try :
535+ try :
536+ result = f (* args , ** kwargs )
537+ except Exception as e :
538+ _capture_exception (e )
539+ raise e from None
540+
541+ return gen .send (result )
542+ except StopIteration as e :
543+ return e .value
544+
545+ @wraps (f )
546+ def _sentry_patched_create_sync (* args , ** kwargs ):
547+ # type: (Any, Any) -> Any
548+ integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
549+ if integration is None :
550+ return f (* args , ** kwargs )
551+
552+ return _execute_sync (f , * args , ** kwargs )
553+
554+ return _sentry_patched_create_sync
555+
556+
557+ def _wrap_async_responses_create (f ):
558+ # type: (Any) -> Any
559+ async def _execute_async (f , * args , ** kwargs ):
560+ # type: (Any, Any, Any) -> Any
561+ gen = _new_responses_create_common (f , * args , ** kwargs )
562+
563+ try :
564+ f , args , kwargs = next (gen )
565+ except StopIteration as e :
566+ return await e .value
567+
568+ try :
569+ try :
570+ result = await f (* args , ** kwargs )
571+ except Exception as e :
572+ _capture_exception (e )
573+ raise e from None
574+
575+ return gen .send (result )
576+ except StopIteration as e :
577+ return e .value
578+
579+ @wraps (f )
580+ async def _sentry_patched_responses_async (* args , ** kwargs ):
581+ # type: (Any, Any) -> Any
582+ integration = sentry_sdk .get_client ().get_integration (OpenAIIntegration )
583+ if integration is None :
584+ return await f (* args , ** kwargs )
585+
586+ return await _execute_async (f , * args , ** kwargs )
587+
588+ return _sentry_patched_responses_async
0 commit comments