@@ -434,15 +434,20 @@ class _WrapResponsesRaw(CompositeFunctionWrapperPatcher):
434434# ---------------------------------------------------------------------------
435435
436436
437- def _is_class_method_wrapped (resource : Any , method_name : str ) -> bool :
437+ def _is_class_method_wrapped (
438+ resource : Any ,
439+ method_name : str ,
440+ patcher : type [FunctionWrapperPatcher ],
441+ ) -> bool :
438442 """Return ``True`` if *method_name* on the **class** of *resource* is
439- already a wrapt ``FunctionWrapper`` (i.e. patched by ``setup()``) .
443+ already wrapped by the matching Braintrust ``setup()`` patcher .
440444
441- This prevents double-tracing when both ``setup()`` and ``wrap_openai()``
442- are active for the same client.
445+ Other libraries (for example ddtrace) may also use wrapt wrappers on OpenAI
446+ class methods. Only Braintrust's own patch marker should make
447+ ``wrap_openai()`` skip instance-level instrumentation.
443448 """
444449 cls_attr = inspect .getattr_static (type (resource ), method_name , None )
445- return isinstance (cls_attr , FunctionWrapper )
450+ return patcher . has_patch_marker (cls_attr )
446451
447452
448453def _delegates_to_wrapped_method (resource : Any , method_name : str ) -> bool :
@@ -480,7 +485,7 @@ def _wrap_resource(
480485 # level patchers are active and we can skip instance-level wrapping.
481486 for sub in patcher .sub_patchers :
482487 attr = sub .target_path .rsplit ("." , 1 )[- 1 ]
483- if _is_class_method_wrapped (resource , attr ):
488+ if _is_class_method_wrapped (resource , attr , sub ):
484489 return
485490 if attr_path .endswith ("with_raw_response" ) and _delegates_to_wrapped_method (resource , attr ):
486491 return
0 commit comments