Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import inspect

import openai
from braintrust import wrap_openai
from braintrust.auto import auto_instrument
from braintrust.integrations.test_utils import autoinstrument_test_context
from wrapt import FunctionWrapper
Expand Down Expand Up @@ -49,4 +50,20 @@ def _is_braintrust_wrapped(resource) -> bool:
assert span["metadata"]["provider"] == "openai"
assert "gpt-4o-mini" in span["metadata"]["model"]

# 5. Manual wrapping after auto-instrumentation must not duplicate the span or tokens.
with autoinstrument_test_context("test_openai_responses_metrics", integration="openai") as memory_logger:
client = wrap_openai(openai.OpenAI())
response = client.responses.create(
model="gpt-4o-mini",
input="What's 12 + 12?",
instructions="Just the number please",
)
assert response.output

spans = memory_logger.pop()
assert len(spans) == 1, f"Expected 1 Responses span, got {len(spans)}"
assert spans[0]["span_attributes"]["name"] == "openai.responses.create"
assert spans[0]["metrics"]["prompt_tokens"] == response.usage.input_tokens
assert spans[0]["metrics"]["completion_tokens"] == response.usage.output_tokens

print("SUCCESS")
4 changes: 3 additions & 1 deletion py/src/braintrust/integrations/openai/patchers.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ def _make_method_patchers(
"name": wrap_name,
"target_path": method,
"wrapper": wrapper,
# The instance marker differs from the class-level setup markers.
"setup_patchers": (sync_patcher, async_patcher),
},
)
return sync_patcher, async_patcher, instance_patcher
Expand Down Expand Up @@ -479,7 +481,7 @@ class methods. Only Braintrust's own patch marker should make
``wrap_openai()`` skip instance-level instrumentation.
"""
cls_attr = inspect.getattr_static(type(resource), method_name, None)
return patcher.has_patch_marker(cls_attr)
return any(setup_patcher.has_patch_marker(cls_attr) for setup_patcher in patcher.setup_patchers)


def _delegates_to_wrapped_method(resource: Any, method_name: str) -> bool:
Expand Down
Loading