Skip to content

Commit d4b4d65

Browse files
committed
fix more merge conflicts
1 parent ed0fe8c commit d4b4d65

3 files changed

Lines changed: 144 additions & 63 deletions

File tree

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
interactions:
2+
- request:
3+
body: '{"max_tokens":100,"messages":[{"role":"user","content":"hi"}],"model":"claude-3-haiku-20240307"}'
4+
headers:
5+
Accept:
6+
- application/json
7+
Accept-Encoding:
8+
- gzip, deflate
9+
Connection:
10+
- keep-alive
11+
Content-Length:
12+
- '96'
13+
Content-Type:
14+
- application/json
15+
Host:
16+
- api.anthropic.com
17+
User-Agent:
18+
- Anthropic/Python 0.86.0
19+
X-Stainless-Arch:
20+
- arm64
21+
X-Stainless-Async:
22+
- 'false'
23+
X-Stainless-Lang:
24+
- python
25+
X-Stainless-OS:
26+
- MacOS
27+
X-Stainless-Package-Version:
28+
- 0.86.0
29+
X-Stainless-Runtime:
30+
- CPython
31+
X-Stainless-Runtime-Version:
32+
- 3.13.3
33+
anthropic-version:
34+
- '2023-06-01'
35+
x-stainless-read-timeout:
36+
- '600'
37+
x-stainless-retry-count:
38+
- '0'
39+
x-stainless-timeout:
40+
- '600'
41+
method: POST
42+
uri: https://api.anthropic.com/v1/messages
43+
response:
44+
body:
45+
string: '{"model":"claude-3-haiku-20240307","id":"msg_0117iU2tMYP3e6LP1NXdHcpt","type":"message","role":"assistant","content":[{"type":"text","text":"Hello!
46+
How can I assist you today?"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":8,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":12,"service_tier":"standard","inference_geo":"not_available"}}'
47+
headers:
48+
CF-RAY:
49+
- 9e17520e5d2226b0-SJC
50+
Connection:
51+
- keep-alive
52+
Content-Security-Policy:
53+
- default-src 'none'; frame-ancestors 'none'
54+
Content-Type:
55+
- application/json
56+
Date:
57+
- Tue, 24 Mar 2026 17:15:54 GMT
58+
Server:
59+
- cloudflare
60+
Transfer-Encoding:
61+
- chunked
62+
X-Robots-Tag:
63+
- none
64+
anthropic-organization-id:
65+
- 27796668-7351-40ac-acc4-024aee8995a5
66+
anthropic-ratelimit-input-tokens-limit:
67+
- '8000000'
68+
anthropic-ratelimit-input-tokens-remaining:
69+
- '8000000'
70+
anthropic-ratelimit-input-tokens-reset:
71+
- '2026-03-24T17:15:54Z'
72+
anthropic-ratelimit-output-tokens-limit:
73+
- '1500000'
74+
anthropic-ratelimit-output-tokens-remaining:
75+
- '1500000'
76+
anthropic-ratelimit-output-tokens-reset:
77+
- '2026-03-24T17:15:54Z'
78+
anthropic-ratelimit-requests-limit:
79+
- '10000'
80+
anthropic-ratelimit-requests-remaining:
81+
- '9999'
82+
anthropic-ratelimit-requests-reset:
83+
- '2026-03-24T17:15:54Z'
84+
anthropic-ratelimit-tokens-limit:
85+
- '9500000'
86+
anthropic-ratelimit-tokens-remaining:
87+
- '9500000'
88+
anthropic-ratelimit-tokens-reset:
89+
- '2026-03-24T17:15:54Z'
90+
cf-cache-status:
91+
- DYNAMIC
92+
content-length:
93+
- '468'
94+
request-id:
95+
- req_011CZNHNsVf8oVrJ25Aaqocy
96+
server-timing:
97+
- x-originResponse;dur=379
98+
set-cookie:
99+
- _cfuvid=wNnIJEFrdvZ9PvwuqaVIcC.f38mcpIT580b2SVCmGeg-1774372553.9806187-1.0.1.1-xSLK0QdT6_irKBkykjkQKy4uFCjzt9RoIb5SrFAfiJk;
100+
HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com
101+
strict-transport-security:
102+
- max-age=31536000; includeSubDomains; preload
103+
vary:
104+
- Accept-Encoding
105+
x-envoy-upstream-service-time:
106+
- '378'
107+
status:
108+
code: 200
109+
message: OK
110+
version: 1

py/src/braintrust/integrations/anthropic/test_anthropic.py

Lines changed: 17 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
Tests to ensure we reliably wrap the Anthropic API.
33
"""
44

5-
import inspect
65
import time
76
import unittest.mock
87
from pathlib import Path
@@ -11,7 +10,6 @@
1110
import pytest
1211
from braintrust import logger
1312
from braintrust.integrations.anthropic import AnthropicIntegration, wrap_anthropic
14-
from braintrust.integrations.versioning import make_specifier, version_satisfies
1513
from braintrust.test_helpers import init_test_logger
1614

1715

@@ -494,66 +492,23 @@ async def test_anthropic_beta_messages_streaming_async(memory_logger):
494492
assert metrics["tokens"] == usage.input_tokens + usage.output_tokens
495493

496494

497-
class TestAnthropicIntegrationSetup:
498-
"""Tests for `AnthropicIntegration.setup()`."""
499-
500-
def test_available_patchers(self):
501-
assert AnthropicIntegration.available_patchers() == (
502-
"anthropic.init.sync",
503-
"anthropic.init.async",
504-
)
505-
506-
def test_setup_rejects_unsupported_versions(self):
507-
spec = make_specifier(
508-
min_version=AnthropicIntegration.min_version, max_version=AnthropicIntegration.max_version
509-
)
510-
assert version_satisfies("0.47.9", spec) is False
511-
512-
def test_setup_wraps_supported_clients(self):
513-
"""`AnthropicIntegration.setup()` should wrap both sync and async client constructors."""
514-
unpatched_sync = anthropic.Anthropic(api_key="test-key")
515-
unpatched_async = anthropic.AsyncAnthropic(api_key="test-key")
516-
assert type(unpatched_sync.messages).__module__.startswith("anthropic.")
517-
assert type(unpatched_async.messages).__module__.startswith("anthropic.")
518-
519-
AnthropicIntegration.setup()
520-
patched_sync = anthropic.Anthropic(api_key="test-key")
521-
patched_async = anthropic.AsyncAnthropic(api_key="test-key")
522-
assert type(patched_sync.messages).__module__ == "braintrust.integrations.anthropic.tracing"
523-
assert type(patched_async.messages).__module__ == "braintrust.integrations.anthropic.tracing"
524-
525-
def test_setup_is_idempotent(self):
526-
"""Multiple `AnthropicIntegration.setup()` calls should be safe."""
527-
AnthropicIntegration.setup()
528-
first_sync_init = inspect.getattr_static(anthropic.Anthropic, "__init__")
529-
first_async_init = inspect.getattr_static(anthropic.AsyncAnthropic, "__init__")
530-
531-
AnthropicIntegration.setup()
532-
assert first_sync_init is inspect.getattr_static(anthropic.Anthropic, "__init__")
533-
assert first_async_init is inspect.getattr_static(anthropic.AsyncAnthropic, "__init__")
534-
535-
def test_setup_creates_spans(self):
536-
"""`AnthropicIntegration.setup()` should create spans when making API calls."""
537-
init_test_logger("test-auto")
538-
with logger._internal_with_memory_background_logger() as memory_logger:
539-
AnthropicIntegration.setup()
540-
541-
client = anthropic.Anthropic()
542-
543-
import braintrust
544-
545-
with braintrust.start_span(name="test"):
546-
try:
547-
client.messages.create(
548-
model="claude-3-5-haiku-latest",
549-
max_tokens=100,
550-
messages=[{"role": "user", "content": "hi"}],
551-
)
552-
except Exception:
553-
pass
554-
555-
spans = memory_logger.pop()
556-
assert len(spans) >= 1, f"Expected spans, got {spans}"
495+
@pytest.mark.vcr
496+
def test_setup_creates_spans(memory_logger):
497+
"""`AnthropicIntegration.setup()` should create spans when making API calls."""
498+
AnthropicIntegration.setup()
499+
500+
client = anthropic.Anthropic()
501+
client.messages.create(
502+
model=MODEL,
503+
max_tokens=100,
504+
messages=[{"role": "user", "content": "hi"}],
505+
)
506+
507+
spans = memory_logger.pop()
508+
assert len(spans) == 1
509+
span = spans[0]
510+
assert span["metadata"]["model"] == MODEL
511+
assert span["metadata"]["provider"] == "anthropic"
557512

558513

559514
def _make_batch_requests():

py/src/braintrust/integrations/anthropic/tracing.py

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -467,8 +467,18 @@ def _catch_exceptions():
467467
log.warning("swallowing exception in tracing code", exc_info=e)
468468

469469

470+
_BRAINTRUST_TRACED = "__braintrust_traced__"
471+
472+
470473
def _wrap_anthropic(client):
471-
"""Wrap an Anthropic object (or AsyncAnthropic) to add tracing."""
474+
"""Wrap an Anthropic object (or AsyncAnthropic) to add tracing.
475+
476+
If the client is already traced (e.g. via ``AnthropicIntegration.setup()``),
477+
it is returned unchanged to avoid double-wrapping.
478+
"""
479+
if getattr(client, _BRAINTRUST_TRACED, False):
480+
return client
481+
472482
type_name = getattr(type(client), "__name__")
473483
if "AsyncAnthropic" in type_name:
474484
return TracedAsyncAnthropic(client)
@@ -482,17 +492,23 @@ def _wrap_anthropic(client):
482492

483493

484494
def _apply_anthropic_wrapper(client):
495+
if getattr(client, _BRAINTRUST_TRACED, False):
496+
return
485497
wrapped = _wrap_anthropic(client)
486498
client.messages = wrapped.messages
487499
if hasattr(wrapped, "beta"):
488500
client.beta = wrapped.beta
501+
setattr(client, _BRAINTRUST_TRACED, True)
489502

490503

491504
def _apply_async_anthropic_wrapper(client):
505+
if getattr(client, _BRAINTRUST_TRACED, False):
506+
return
492507
wrapped = _wrap_anthropic(client)
493508
client.messages = wrapped.messages
494509
if hasattr(wrapped, "beta"):
495510
client.beta = wrapped.beta
511+
setattr(client, _BRAINTRUST_TRACED, True)
496512

497513

498514
def _anthropic_init_wrapper(wrapped, instance, args, kwargs):

0 commit comments

Comments
 (0)