diff --git a/py/src/braintrust/integrations/anthropic/cassettes/TestAnthropicIntegrationSetupAsyncSpans.test_setup_creates_async_spans.yaml b/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_async_batches_create_produces_span.yaml similarity index 58% rename from py/src/braintrust/integrations/anthropic/cassettes/TestAnthropicIntegrationSetupAsyncSpans.test_setup_creates_async_spans.yaml rename to py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_async_batches_create_produces_span.yaml index 3cda147b..aceb5c55 100644 --- a/py/src/braintrust/integrations/anthropic/cassettes/TestAnthropicIntegrationSetupAsyncSpans.test_setup_creates_async_spans.yaml +++ b/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_async_batches_create_produces_span.yaml @@ -1,6 +1,8 @@ interactions: - request: - body: '{"max_tokens":100,"messages":[{"role":"user","content":"Say hi async"}],"model":"claude-3-5-haiku-latest"}' + body: '{"requests":[{"custom_id":"req-1","params":{"model":"claude-3-haiku-20240307","max_tokens":100,"messages":[{"role":"user","content":"What + is 2+2?"}]}},{"custom_id":"req-2","params":{"model":"claude-3-haiku-20240307","max_tokens":100,"messages":[{"role":"user","content":"What + is 3+3?"}]}}]}' headers: Accept: - application/json @@ -9,7 +11,7 @@ interactions: Connection: - keep-alive Content-Length: - - '106' + - '290' Content-Type: - application/json Host: @@ -37,16 +39,15 @@ interactions: x-stainless-retry-count: - '0' x-stainless-timeout: - - '600' + - NOT_GIVEN method: POST - uri: https://api.anthropic.com/v1/messages + uri: https://api.anthropic.com/v1/messages/batches response: body: - string: '{"type":"error","error":{"type":"not_found_error","message":"model: - claude-3-5-haiku-latest"},"request_id":"req_011CZLdMZnqva1tBGWuemWN9"}' + string: '{"id":"msgbatch_019LiP2CEHA2yQZT9HiJ7rQ3","type":"message_batch","processing_status":"in_progress","request_counts":{"processing":2,"succeeded":0,"errored":0,"canceled":0,"expired":0},"ended_at":null,"created_at":"2026-03-24T16:48:33.250265+00:00","expires_at":"2026-03-25T16:48:33.250265+00:00","archived_at":null,"cancel_initiated_at":null,"results_url":null}' headers: CF-RAY: - - 9e101d82cf341fdb-SJC + - 9e1729fe4c6b042c-SJC Connection: - keep-alive Content-Security-Policy: @@ -54,7 +55,7 @@ interactions: Content-Type: - application/json Date: - - Mon, 23 Mar 2026 20:16:41 GMT + - Tue, 24 Mar 2026 16:48:33 GMT Server: - cloudflare Transfer-Encoding: @@ -66,23 +67,21 @@ interactions: cf-cache-status: - DYNAMIC content-length: - - '138' + - '361' request-id: - - req_011CZLdMZnqva1tBGWuemWN9 + - req_011CZNFHubo6iV5DHippSpJq server-timing: - - x-originResponse;dur=25 + - x-originResponse;dur=302 set-cookie: - - _cfuvid=O35oR1Sx8_qfQk2ct3bUq6P_LfxnelQS3AuhGTtAo3Q-1774297001.4099903-1.0.1.1-l__Wn1ND363h2RHwrTFmIqpFtWzPoIUNjQXrH1hjWyY; + - _cfuvid=tCl0dHjx7QmH_ZAZsV5JZXo6iXr.5DKYZ6eTwVr3O9U-1774370913.0033047-1.0.1.1-98E2NulvgerP_hm8fJJmpfNnCka.CTGTjUyU0ueSaHY; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com strict-transport-security: - max-age=31536000; includeSubDomains; preload vary: - Accept-Encoding x-envoy-upstream-service-time: - - '23' - x-should-retry: - - 'false' + - '300' status: - code: 404 - message: Not Found + code: 200 + message: OK version: 1 diff --git a/py/src/braintrust/integrations/anthropic/cassettes/TestAnthropicIntegrationSetupSpans.test_setup_creates_spans.yaml b/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_sync_batches_create_logs_error_on_failure.yaml similarity index 70% rename from py/src/braintrust/integrations/anthropic/cassettes/TestAnthropicIntegrationSetupSpans.test_setup_creates_spans.yaml rename to py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_sync_batches_create_logs_error_on_failure.yaml index 8b6d8e58..927d8cc0 100644 --- a/py/src/braintrust/integrations/anthropic/cassettes/TestAnthropicIntegrationSetupSpans.test_setup_creates_spans.yaml +++ b/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_sync_batches_create_logs_error_on_failure.yaml @@ -1,6 +1,6 @@ interactions: - request: - body: '{"max_tokens":100,"messages":[{"role":"user","content":"Say hi"}],"model":"claude-3-5-haiku-latest"}' + body: '{"requests":[]}' headers: Accept: - application/json @@ -9,7 +9,7 @@ interactions: Connection: - keep-alive Content-Length: - - '100' + - '15' Content-Type: - application/json Host: @@ -37,16 +37,16 @@ interactions: x-stainless-retry-count: - '0' x-stainless-timeout: - - '600' + - NOT_GIVEN method: POST - uri: https://api.anthropic.com/v1/messages + uri: https://api.anthropic.com/v1/messages/batches response: body: - string: '{"type":"error","error":{"type":"not_found_error","message":"model: - claude-3-5-haiku-latest"},"request_id":"req_011CZLdMYnZkfSiZUydAPCJh"}' + string: '{"type":"error","error":{"type":"invalid_request_error","message":"requests: + List should have at least 1 item after validation, not 0"},"request_id":"req_011CZNFHwZ7YMN9zyDPHXbmQ"}' headers: CF-RAY: - - 9e101d81485f9e5c-SJC + - 9e172a012bf5963f-SJC Connection: - keep-alive Content-Security-Policy: @@ -54,7 +54,7 @@ interactions: Content-Type: - application/json Date: - - Mon, 23 Mar 2026 20:16:41 GMT + - Tue, 24 Mar 2026 16:48:33 GMT Server: - cloudflare Transfer-Encoding: @@ -66,23 +66,23 @@ interactions: cf-cache-status: - DYNAMIC content-length: - - '138' + - '180' request-id: - - req_011CZLdMYnZkfSiZUydAPCJh + - req_011CZNFHwZ7YMN9zyDPHXbmQ server-timing: - - x-originResponse;dur=33 + - x-originResponse;dur=67 set-cookie: - - _cfuvid=qjfwtdMQuaH4s2MvMr53WG49MOjmHdRt83XWnDUSnZs-1774297001.1711438-1.0.1.1-6h3vTb4keZZALUiWWbTnUXgtrTuyB4XqC2sFuQc9fQk; + - _cfuvid=wf8HdCMhs1UcbQutpPzsJc1I_lzHbLbPsQj6EA0sl4s-1774370913.461001-1.0.1.1-wD9j9Vl_k5M7qG3MI7A3pbi6Q52GLfngLxFm_kctpPo; HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com strict-transport-security: - max-age=31536000; includeSubDomains; preload vary: - Accept-Encoding x-envoy-upstream-service-time: - - '31' + - '65' x-should-retry: - 'false' status: - code: 404 - message: Not Found + code: 400 + message: Bad Request version: 1 diff --git a/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_sync_batches_create_multi_model_metadata.yaml b/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_sync_batches_create_multi_model_metadata.yaml new file mode 100644 index 00000000..cecdb6de --- /dev/null +++ b/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_sync_batches_create_multi_model_metadata.yaml @@ -0,0 +1,85 @@ +interactions: +- request: + body: '{"requests":[{"custom_id":"req-1","params":{"model":"claude-3-haiku-20240307","max_tokens":100,"messages":[{"role":"user","content":"Hi"}]}},{"custom_id":"req-2","params":{"model":"claude-3-5-haiku-latest","max_tokens":100,"messages":[{"role":"user","content":"Hello"}]}}]}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate, zstd + Connection: + - keep-alive + Content-Length: + - '273' + Content-Type: + - application/json + Host: + - api.anthropic.com + User-Agent: + - Anthropic/Python 0.84.0 + X-Stainless-Arch: + - arm64 + X-Stainless-Async: + - 'false' + X-Stainless-Lang: + - python + X-Stainless-OS: + - MacOS + X-Stainless-Package-Version: + - 0.84.0 + X-Stainless-Runtime: + - CPython + X-Stainless-Runtime-Version: + - 3.13.3 + anthropic-version: + - '2023-06-01' + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages/batches + response: + body: + string: '{"id":"msgbatch_01Cq1688wP7FtQMAnsHr5G4Y","type":"message_batch","processing_status":"in_progress","request_counts":{"processing":2,"succeeded":0,"errored":0,"canceled":0,"expired":0},"ended_at":null,"created_at":"2026-03-24T16:48:33.926161+00:00","expires_at":"2026-03-25T16:48:33.926161+00:00","archived_at":null,"cancel_initiated_at":null,"results_url":null}' + headers: + CF-RAY: + - 9e172a025ecd1fdb-SJC + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 24 Mar 2026 16:48:34 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - 27796668-7351-40ac-acc4-024aee8995a5 + cf-cache-status: + - DYNAMIC + content-length: + - '361' + request-id: + - req_011CZNFHxQDRZBhMWjsVMHQh + server-timing: + - x-originResponse;dur=327 + set-cookie: + - _cfuvid=yDeqS1YweYY8i8ccTtSz_bw0CmmepS965cJPYQ4HCWE-1774370913.6565096-1.0.1.1-kTbTE.Sbl1JcCxMNIgb0nn9N_cAA7ECtmSuTerDB.zI; + HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '325' + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_sync_batches_create_produces_span.yaml b/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_sync_batches_create_produces_span.yaml new file mode 100644 index 00000000..aaaf4abd --- /dev/null +++ b/py/src/braintrust/integrations/anthropic/cassettes/TestBatchesCreateSpans.test_sync_batches_create_produces_span.yaml @@ -0,0 +1,87 @@ +interactions: +- request: + body: '{"requests":[{"custom_id":"req-1","params":{"model":"claude-3-haiku-20240307","max_tokens":100,"messages":[{"role":"user","content":"What + is 2+2?"}]}},{"custom_id":"req-2","params":{"model":"claude-3-haiku-20240307","max_tokens":100,"messages":[{"role":"user","content":"What + is 3+3?"}]}}]}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate, zstd + Connection: + - keep-alive + Content-Length: + - '290' + Content-Type: + - application/json + Host: + - api.anthropic.com + User-Agent: + - Anthropic/Python 0.84.0 + X-Stainless-Arch: + - arm64 + X-Stainless-Async: + - 'false' + X-Stainless-Lang: + - python + X-Stainless-OS: + - MacOS + X-Stainless-Package-Version: + - 0.84.0 + X-Stainless-Runtime: + - CPython + X-Stainless-Runtime-Version: + - 3.13.3 + anthropic-version: + - '2023-06-01' + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages/batches + response: + body: + string: '{"id":"msgbatch_01AGJo81SZkaPs6vyZzDWPdP","type":"message_batch","processing_status":"in_progress","request_counts":{"processing":2,"succeeded":0,"errored":0,"canceled":0,"expired":0},"ended_at":null,"created_at":"2026-03-24T16:48:32.769942+00:00","expires_at":"2026-03-25T16:48:32.769942+00:00","archived_at":null,"cancel_initiated_at":null,"results_url":null}' + headers: + CF-RAY: + - 9e1729fb1b493824-SJC + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 24 Mar 2026 16:48:32 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - 27796668-7351-40ac-acc4-024aee8995a5 + cf-cache-status: + - DYNAMIC + content-length: + - '361' + request-id: + - req_011CZNFHsS4jkRnAp2QVhfjZ + server-timing: + - x-originResponse;dur=325 + set-cookie: + - _cfuvid=sNY6UUL2YIBy6w1kKrRez1Z82ZXsyNQVqBw3YyCGBQs-1774370912.495851-1.0.1.1-IyHcfjRHrdCZ0rzZZa7kqpMFe32i22VC65d87W8gKqM; + HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '323' + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/anthropic/cassettes/TestBetaBatchesCreateSpans.test_async_beta_batches_create_produces_span.yaml b/py/src/braintrust/integrations/anthropic/cassettes/TestBetaBatchesCreateSpans.test_async_beta_batches_create_produces_span.yaml new file mode 100644 index 00000000..22969f66 --- /dev/null +++ b/py/src/braintrust/integrations/anthropic/cassettes/TestBetaBatchesCreateSpans.test_async_beta_batches_create_produces_span.yaml @@ -0,0 +1,89 @@ +interactions: +- request: + body: '{"requests":[{"custom_id":"req-1","params":{"model":"claude-3-haiku-20240307","max_tokens":100,"messages":[{"role":"user","content":"What + is 2+2?"}]}},{"custom_id":"req-2","params":{"model":"claude-3-haiku-20240307","max_tokens":100,"messages":[{"role":"user","content":"What + is 3+3?"}]}}]}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate, zstd + Connection: + - keep-alive + Content-Length: + - '290' + Content-Type: + - application/json + Host: + - api.anthropic.com + User-Agent: + - AsyncAnthropic/Python 0.84.0 + X-Stainless-Arch: + - arm64 + X-Stainless-Async: + - async:asyncio + X-Stainless-Lang: + - python + X-Stainless-OS: + - MacOS + X-Stainless-Package-Version: + - 0.84.0 + X-Stainless-Runtime: + - CPython + X-Stainless-Runtime-Version: + - 3.13.3 + anthropic-beta: + - message-batches-2024-09-24 + anthropic-version: + - '2023-06-01' + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages/batches?beta=true + response: + body: + string: '{"id":"msgbatch_01S2VNoUdbg4JkaBJpgsYnys","type":"message_batch","processing_status":"in_progress","request_counts":{"processing":2,"succeeded":0,"errored":0,"canceled":0,"expired":0},"ended_at":null,"created_at":"2026-03-24T16:48:34.991903+00:00","expires_at":"2026-03-25T16:48:34.991903+00:00","archived_at":null,"cancel_initiated_at":null,"results_url":null}' + headers: + CF-RAY: + - 9e172a0928737b82-SJC + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 24 Mar 2026 16:48:35 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - 27796668-7351-40ac-acc4-024aee8995a5 + cf-cache-status: + - DYNAMIC + content-length: + - '361' + request-id: + - req_011CZNFJ34FzMzaNYuJ1NLP2 + server-timing: + - x-originResponse;dur=308 + set-cookie: + - _cfuvid=DxZ2pOZRa.k7QucRtjwltHCtHw8iLZwpUhMq2bHvc3Q-1774370914.7470946-1.0.1.1-bp6de1ZuVzmL1QAOz_cCKNfAXwJKF2_N.JeObv7h2QI; + HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '306' + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/anthropic/cassettes/TestBetaBatchesCreateSpans.test_sync_beta_batches_create_produces_span.yaml b/py/src/braintrust/integrations/anthropic/cassettes/TestBetaBatchesCreateSpans.test_sync_beta_batches_create_produces_span.yaml new file mode 100644 index 00000000..c3c27af6 --- /dev/null +++ b/py/src/braintrust/integrations/anthropic/cassettes/TestBetaBatchesCreateSpans.test_sync_beta_batches_create_produces_span.yaml @@ -0,0 +1,89 @@ +interactions: +- request: + body: '{"requests":[{"custom_id":"req-1","params":{"model":"claude-3-haiku-20240307","max_tokens":100,"messages":[{"role":"user","content":"What + is 2+2?"}]}},{"custom_id":"req-2","params":{"model":"claude-3-haiku-20240307","max_tokens":100,"messages":[{"role":"user","content":"What + is 3+3?"}]}}]}' + headers: + Accept: + - application/json + Accept-Encoding: + - gzip, deflate, zstd + Connection: + - keep-alive + Content-Length: + - '290' + Content-Type: + - application/json + Host: + - api.anthropic.com + User-Agent: + - Anthropic/Python 0.84.0 + X-Stainless-Arch: + - arm64 + X-Stainless-Async: + - 'false' + X-Stainless-Lang: + - python + X-Stainless-OS: + - MacOS + X-Stainless-Package-Version: + - 0.84.0 + X-Stainless-Runtime: + - CPython + X-Stainless-Runtime-Version: + - 3.13.3 + anthropic-beta: + - message-batches-2024-09-24 + anthropic-version: + - '2023-06-01' + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages/batches?beta=true + response: + body: + string: '{"id":"msgbatch_01XPdGUQYkDJbwujvUhz8SGy","type":"message_batch","processing_status":"in_progress","request_counts":{"processing":2,"succeeded":0,"errored":0,"canceled":0,"expired":0},"ended_at":null,"created_at":"2026-03-24T16:48:34.521249+00:00","expires_at":"2026-03-25T16:48:34.521249+00:00","archived_at":null,"cancel_initiated_at":null,"results_url":null}' + headers: + CF-RAY: + - 9e172a063cafc68c-SJC + Connection: + - keep-alive + Content-Security-Policy: + - default-src 'none'; frame-ancestors 'none' + Content-Type: + - application/json + Date: + - Tue, 24 Mar 2026 16:48:34 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - 27796668-7351-40ac-acc4-024aee8995a5 + cf-cache-status: + - DYNAMIC + content-length: + - '361' + request-id: + - req_011CZNFJ12iaeFKCy7fwAZdJ + server-timing: + - x-originResponse;dur=319 + set-cookie: + - _cfuvid=4mz0wbr1u0hxg6B0Etmtc.CllgmwQs2cAeXVGotu.bA-1774370914.274028-1.0.1.1-PQoe5bEmFw0fztOM8HjFKLHBM2_Yt6f0sX1_QM00Mx4; + HttpOnly; SameSite=None; Secure; Path=/; Domain=api.anthropic.com + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + vary: + - Accept-Encoding + x-envoy-upstream-service-time: + - '318' + status: + code: 200 + message: OK +version: 1 diff --git a/py/src/braintrust/integrations/anthropic/cassettes/TestPatchAnthropicAsyncSpans.test_patch_anthropic_async_creates_spans.yaml b/py/src/braintrust/integrations/anthropic/cassettes/TestPatchAnthropicAsyncSpans.test_patch_anthropic_async_creates_spans.yaml deleted file mode 100644 index ac5b8caa..00000000 --- a/py/src/braintrust/integrations/anthropic/cassettes/TestPatchAnthropicAsyncSpans.test_patch_anthropic_async_creates_spans.yaml +++ /dev/null @@ -1,107 +0,0 @@ -interactions: -- request: - body: '{"max_tokens":100,"messages":[{"role":"user","content":"Say hi async"}],"model":"claude-3-5-haiku-latest"}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '106' - Content-Type: - - application/json - Host: - - api.anthropic.com - User-Agent: - - PatchedAsyncAnthropic/Python 0.76.0 - X-Stainless-Arch: - - arm64 - X-Stainless-Async: - - async:asyncio - X-Stainless-Lang: - - python - X-Stainless-OS: - - MacOS - X-Stainless-Package-Version: - - 0.76.0 - X-Stainless-Runtime: - - CPython - X-Stainless-Runtime-Version: - - 3.13.3 - anthropic-version: - - '2023-06-01' - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: !!binary | - H4sIAAAAAAAA/3VRTW8TMRD9K8YXQEqk7NIKtBduKAcuSKCqJcix7GltxTuz2GPaVZT/zjj9blVf - bL335s288V6P5CHpQbtkq4flp+XpMti4q8t+1Z90q77XCx29CMZyZVZdf/795ny6oPWP67N48WXq - fo3ffnrR8DxBU0Ep9goEyJQaYEuJhS2yQI6QQV7D7/29nuGmMcdr0GvI8L4oi8qWGV3IhFSLuqzo - OBIqJlXsrEIcNrjB7XY7zRxInke58nDZeBPih4+iUHIycM2oNnodFQexf7fRx8pmcEY1eTVTVSnu - QI3QGngYCQtny6ACXTfI2ZSkOpbbsR7m+aoPfxa6ME0mgy2EEgHQm9ZS3xEF/lZAJ1mxprTQ9bie - Ya8jTpUN0w6w6KFbyXqsC2CcWDVz81zwwAvt3+Lua5s/TAFGyDaZ0/G1/pHtwkv2sNBU+Sl08lnS - QP4XHRiOkCVn+1Nvs9eHw3/q92SIRQIAAA== - headers: - CF-RAY: - - 9be009abe8aa4e4d-EWR - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 20:56:04 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Robots-Tag: - - none - anthropic-organization-id: - - 27796668-7351-40ac-acc4-024aee8995a5 - anthropic-ratelimit-input-tokens-limit: - - '5000000' - anthropic-ratelimit-input-tokens-remaining: - - '5000000' - anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T20:56:03Z' - anthropic-ratelimit-output-tokens-limit: - - '1000000' - anthropic-ratelimit-output-tokens-remaining: - - '1000000' - anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T20:56:04Z' - anthropic-ratelimit-requests-limit: - - '10000' - anthropic-ratelimit-requests-remaining: - - '9999' - anthropic-ratelimit-requests-reset: - - '2026-01-14T20:56:03Z' - anthropic-ratelimit-tokens-limit: - - '6000000' - anthropic-ratelimit-tokens-remaining: - - '6000000' - anthropic-ratelimit-tokens-reset: - - '2026-01-14T20:56:03Z' - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX7wMPtPdemQvSMv8LCMY - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '1286' - status: - code: 200 - message: OK -version: 1 diff --git a/py/src/braintrust/integrations/anthropic/cassettes/TestPatchAnthropicSpans.test_patch_anthropic_creates_spans.yaml b/py/src/braintrust/integrations/anthropic/cassettes/TestPatchAnthropicSpans.test_patch_anthropic_creates_spans.yaml deleted file mode 100644 index b1f532c2..00000000 --- a/py/src/braintrust/integrations/anthropic/cassettes/TestPatchAnthropicSpans.test_patch_anthropic_creates_spans.yaml +++ /dev/null @@ -1,105 +0,0 @@ -interactions: -- request: - body: '{"max_tokens":100,"messages":[{"role":"user","content":"Say hi"}],"model":"claude-3-5-haiku-latest"}' - headers: - Accept: - - application/json - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '100' - Content-Type: - - application/json - Host: - - api.anthropic.com - User-Agent: - - PatchedAnthropic/Python 0.76.0 - X-Stainless-Arch: - - arm64 - X-Stainless-Async: - - 'false' - X-Stainless-Lang: - - python - X-Stainless-OS: - - MacOS - X-Stainless-Package-Version: - - 0.76.0 - X-Stainless-Runtime: - - CPython - X-Stainless-Runtime-Version: - - 3.13.3 - anthropic-version: - - '2023-06-01' - x-stainless-read-timeout: - - '600' - x-stainless-retry-count: - - '0' - x-stainless-timeout: - - '600' - method: POST - uri: https://api.anthropic.com/v1/messages - response: - body: - string: !!binary | - H4sIAAAAAAAA/3WQT0/DMAzFv0rxuZXabjusFyTgsMMucEGAUBQS00RLky5xYFXV7046UfFPnGy9 - 3/OT7RE6J9FAA8LwKLFYFZtCcX2IRV3W66qsa8hBy2ToQsvKar8V69Xu5ebx9Xhd9VcP96e7W7VP - Hhp6nF0YAm8xCd6ZWeAh6EDcUpKEs4Spa57GxU94msm5NLDTGSn0eJHt3HvGPWaDi5l02rYZOcmH - S5iecwjkeuaRB2fTEFrJKHoLnyDgMaIVKd1GY3KI54WaEbTtIzFyB7QBmm3ahwuFTKQk0s6yn7xc - eMLyP7bMzvHYK+zQc8M23V//F63Ubzrl4CJ9l6o6HYP+TQtkpNGnM+cnSu4lTNMHasIXYLYBAAA= - headers: - CF-RAY: - - 9bde85cdffba42f2-EWR - Connection: - - keep-alive - Content-Encoding: - - gzip - Content-Type: - - application/json - Date: - - Wed, 14 Jan 2026 16:31:16 GMT - Server: - - cloudflare - Transfer-Encoding: - - chunked - X-Robots-Tag: - - none - anthropic-organization-id: - - 27796668-7351-40ac-acc4-024aee8995a5 - anthropic-ratelimit-input-tokens-limit: - - '5000000' - anthropic-ratelimit-input-tokens-remaining: - - '5000000' - anthropic-ratelimit-input-tokens-reset: - - '2026-01-14T16:31:16Z' - anthropic-ratelimit-output-tokens-limit: - - '1000000' - anthropic-ratelimit-output-tokens-remaining: - - '1000000' - anthropic-ratelimit-output-tokens-reset: - - '2026-01-14T16:31:16Z' - anthropic-ratelimit-requests-limit: - - '10000' - anthropic-ratelimit-requests-remaining: - - '9999' - anthropic-ratelimit-requests-reset: - - '2026-01-14T16:31:16Z' - anthropic-ratelimit-tokens-limit: - - '6000000' - anthropic-ratelimit-tokens-remaining: - - '6000000' - anthropic-ratelimit-tokens-reset: - - '2026-01-14T16:31:16Z' - cf-cache-status: - - DYNAMIC - request-id: - - req_011CX7bAJ22sddhVB8Eb2N2U - strict-transport-security: - - max-age=31536000; includeSubDomains; preload - x-envoy-upstream-service-time: - - '731' - status: - code: 200 - message: OK -version: 1 diff --git a/py/src/braintrust/integrations/anthropic/test_anthropic.py b/py/src/braintrust/integrations/anthropic/test_anthropic.py index 570de9b1..2d53b712 100644 --- a/py/src/braintrust/integrations/anthropic/test_anthropic.py +++ b/py/src/braintrust/integrations/anthropic/test_anthropic.py @@ -2,19 +2,17 @@ Tests to ensure we reliably wrap the Anthropic API. """ -import inspect import time +import unittest.mock from pathlib import Path import anthropic import pytest from braintrust import logger -from braintrust.integrations.anthropic import AnthropicIntegration, wrap_anthropic, wrap_anthropic_client -from braintrust.integrations.versioning import make_specifier, version_satisfies +from braintrust.integrations.anthropic import wrap_anthropic from braintrust.test_helpers import init_test_logger -TEST_ORG_ID = "test-org-123" PROJECT_NAME = "test-anthropic-app" MODEL = "claude-3-haiku-20240307" # use the cheapest model since answers dont matter @@ -73,15 +71,6 @@ def test_anthropic_messages_create_stream_true(memory_logger): assert "12" in span["output"]["content"][0]["text"] -def test_wrap_anthropic_client_alias_wraps_client(): - client = _get_client() - - with pytest.deprecated_call(match="wrap_anthropic_client\\(\\) is deprecated"): - wrapped = wrap_anthropic_client(client) - - assert type(wrapped.messages).__module__ == "braintrust.integrations.anthropic.tracing" - - @pytest.mark.vcr def test_anthropic_messages_model_params_inputs(memory_logger): assert not memory_logger.pop() @@ -503,128 +492,288 @@ async def test_anthropic_beta_messages_streaming_async(memory_logger): assert metrics["tokens"] == usage.input_tokens + usage.output_tokens -class TestAnthropicIntegrationSetup: - """Tests for `AnthropicIntegration.setup()`.""" +def _make_batch_requests(): + return [ + { + "custom_id": "req-1", + "params": { + "model": MODEL, + "max_tokens": 100, + "messages": [{"role": "user", "content": "What is 2+2?"}], + }, + }, + { + "custom_id": "req-2", + "params": { + "model": MODEL, + "max_tokens": 100, + "messages": [{"role": "user", "content": "What is 3+3?"}], + }, + }, + ] + + +class TestBatchesCreateSpans: + """Tests verifying that batches.create() produces correct spans.""" + + @pytest.mark.vcr + def test_sync_batches_create_produces_span(self, memory_logger): + assert not memory_logger.pop() + + client = wrap_anthropic(_get_client()) + result = client.messages.batches.create(requests=_make_batch_requests()) + + assert result.id + assert result.processing_status == "in_progress" + + spans = memory_logger.pop() + assert len(spans) == 1 + span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.create" + assert span["span_attributes"]["type"] == "task" + assert span["metadata"]["provider"] == "anthropic" + assert span["metadata"]["num_requests"] == 2 + assert span["metadata"]["model"] == MODEL + assert span["input"] == [{"custom_id": "req-1"}, {"custom_id": "req-2"}] + assert span["output"]["id"] == result.id + assert span["output"]["processing_status"] == "in_progress" + assert span["output"]["request_counts"]["processing"] == 2 + + @pytest.mark.vcr + @pytest.mark.asyncio + async def test_async_batches_create_produces_span(self, memory_logger): + assert not memory_logger.pop() - def test_available_patchers(self): - assert AnthropicIntegration.available_patchers() == ( - "anthropic.init.sync", - "anthropic.init.async", - ) + client = wrap_anthropic(_get_async_client()) + result = await client.messages.batches.create(requests=_make_batch_requests()) - def test_resolve_patchers_honors_enable_disable_filters(self): - selected = AnthropicIntegration.resolve_patchers( - enabled_patchers={"anthropic.init.sync", "anthropic.init.async"}, - disabled_patchers={"anthropic.init.async"}, - ) + assert result.id + assert result.processing_status == "in_progress" - assert tuple(patcher.identifier() for patcher in selected) == ("anthropic.init.sync",) + spans = memory_logger.pop() + assert len(spans) == 1 + span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.create" + assert span["span_attributes"]["type"] == "task" + assert span["metadata"]["provider"] == "anthropic" + assert span["metadata"]["num_requests"] == 2 + assert span["metadata"]["model"] == MODEL + assert span["input"] == [{"custom_id": "req-1"}, {"custom_id": "req-2"}] + assert span["output"]["id"] == result.id - def test_resolve_patchers_rejects_unknown_patchers(self): - with pytest.raises(ValueError, match="Unknown patchers"): - AnthropicIntegration.resolve_patchers(enabled_patchers={"anthropic.init.unknown"}) + @pytest.mark.vcr + def test_sync_batches_create_logs_error_on_failure(self, memory_logger): + assert not memory_logger.pop() - def test_setup_rejects_unsupported_versions(self): - spec = make_specifier( - min_version=AnthropicIntegration.min_version, max_version=AnthropicIntegration.max_version - ) - assert version_satisfies("0.47.9", spec) is False + client = wrap_anthropic(_get_client()) + # Empty requests list triggers a 400 error + with pytest.raises(Exception): + client.messages.batches.create(requests=[]) - def test_setup_wraps_supported_clients(self): - """`AnthropicIntegration.setup()` should wrap both sync and async client constructors.""" - unpatched_sync = anthropic.Anthropic(api_key="test-key") - unpatched_async = anthropic.AsyncAnthropic(api_key="test-key") - assert type(unpatched_sync.messages).__module__.startswith("anthropic.") - assert type(unpatched_async.messages).__module__.startswith("anthropic.") + spans = memory_logger.pop() + assert len(spans) == 1 + span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.create" + assert span["error"] - AnthropicIntegration.setup() - patched_sync = anthropic.Anthropic(api_key="test-key") - patched_async = anthropic.AsyncAnthropic(api_key="test-key") - assert type(patched_sync.messages).__module__ == "braintrust.integrations.anthropic.tracing" - assert type(patched_async.messages).__module__ == "braintrust.integrations.anthropic.tracing" + @pytest.mark.vcr + def test_sync_batches_create_multi_model_metadata(self, memory_logger): + """When batch requests use different models, metadata should include 'models' list.""" + assert not memory_logger.pop() - def test_setup_is_idempotent(self): - """Multiple `AnthropicIntegration.setup()` calls should be safe.""" - AnthropicIntegration.setup() - first_sync_init = inspect.getattr_static(anthropic.Anthropic, "__init__") - first_async_init = inspect.getattr_static(anthropic.AsyncAnthropic, "__init__") + client = wrap_anthropic(_get_client()) + + requests = [ + { + "custom_id": "req-1", + "params": { + "model": "claude-3-haiku-20240307", + "max_tokens": 100, + "messages": [{"role": "user", "content": "Hi"}], + }, + }, + { + "custom_id": "req-2", + "params": { + "model": "claude-3-5-haiku-latest", + "max_tokens": 100, + "messages": [{"role": "user", "content": "Hello"}], + }, + }, + ] + result = client.messages.batches.create(requests=requests) + assert result.id - AnthropicIntegration.setup() - assert first_sync_init is inspect.getattr_static(anthropic.Anthropic, "__init__") - assert first_async_init is inspect.getattr_static(anthropic.AsyncAnthropic, "__init__") + spans = memory_logger.pop() + assert len(spans) == 1 + span = spans[0] + assert "model" not in span["metadata"] + assert span["metadata"]["models"] == sorted(["claude-3-haiku-20240307", "claude-3-5-haiku-latest"]) - def test_setup_creates_spans(self): - """`AnthropicIntegration.setup()` should create spans when making API calls.""" - init_test_logger("test-auto") - with logger._internal_with_memory_background_logger() as memory_logger: - AnthropicIntegration.setup() - client = anthropic.Anthropic() +class TestBatchesResultsSpans: + """Tests verifying that batches.results() produces correct spans. - import braintrust + Mocked because the batch results API requires a completed batch, and batches + can take up to 24 hours to finish processing. + """ - with braintrust.start_span(name="test"): - try: - client.messages.create( - model="claude-3-5-haiku-latest", - max_tokens=100, - messages=[{"role": "user", "content": "hi"}], - ) - except Exception: - pass + def test_sync_batches_results_produces_span(self, memory_logger): + assert not memory_logger.pop() - spans = memory_logger.pop() - assert len(spans) >= 1, f"Expected spans, got {spans}" + client = wrap_anthropic(_get_client()) + mock_decoder = unittest.mock.MagicMock() + with unittest.mock.patch( + "anthropic.resources.messages.batches.Batches.results", + return_value=mock_decoder, + ): + result = client.messages.batches.results("msgbatch_abc123") + assert result is mock_decoder -class TestPatchAnthropicSpans: - """VCR-based tests verifying that `AnthropicIntegration.setup()` produces spans.""" + spans = memory_logger.pop() + assert len(spans) == 1 + span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.results" + assert span["span_attributes"]["type"] == "task" + assert span["metadata"]["provider"] == "anthropic" + assert span["input"]["message_batch_id"] == "msgbatch_abc123" + assert span["output"]["type"] == "jsonl_stream" - @pytest.mark.vcr - def test_patch_anthropic_creates_spans(self, memory_logger): - """`AnthropicIntegration.setup()` should create spans when making API calls.""" + @pytest.mark.asyncio + async def test_async_batches_results_produces_span(self, memory_logger): assert not memory_logger.pop() - AnthropicIntegration.setup() - client = anthropic.Anthropic() - response = client.messages.create( - model="claude-3-5-haiku-latest", - max_tokens=100, - messages=[{"role": "user", "content": "Say hi"}], - ) - assert response.content[0].text + client = wrap_anthropic(_get_async_client()) + mock_decoder = unittest.mock.MagicMock() + with unittest.mock.patch( + "anthropic.resources.messages.batches.AsyncBatches.results", + return_value=mock_decoder, + ): + result = await client.messages.batches.results(message_batch_id="msgbatch_abc456") + + assert result is mock_decoder - # Verify span was created spans = memory_logger.pop() assert len(spans) == 1 span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.results" assert span["metadata"]["provider"] == "anthropic" - assert "claude" in span["metadata"]["model"] - assert span["input"] + assert span["input"]["message_batch_id"] == "msgbatch_abc456" + def test_sync_batches_results_logs_error_on_failure(self, memory_logger): + assert not memory_logger.pop() + + client = wrap_anthropic(_get_client()) + with unittest.mock.patch( + "anthropic.resources.messages.batches.Batches.results", + side_effect=Exception("results fetch failed"), + ): + with pytest.raises(Exception, match="results fetch failed"): + client.messages.batches.results("msgbatch_abc123") + + spans = memory_logger.pop() + assert len(spans) == 1 + span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.results" + assert "results fetch failed" in span["error"] + + +class TestBetaBatchesCreateSpans: + """Tests verifying that beta.messages.batches.create() produces correct spans.""" + + @pytest.mark.vcr + def test_sync_beta_batches_create_produces_span(self, memory_logger): + assert not memory_logger.pop() + + client = wrap_anthropic(_get_client()) + result = client.beta.messages.batches.create(requests=_make_batch_requests()) + + assert result.id + assert result.processing_status == "in_progress" -class TestPatchAnthropicAsyncSpans: - """VCR-based tests verifying that `AnthropicIntegration.setup()` produces spans for async clients.""" + spans = memory_logger.pop() + assert len(spans) == 1 + span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.create" + assert span["span_attributes"]["type"] == "task" + assert span["metadata"]["provider"] == "anthropic" + assert span["metadata"]["num_requests"] == 2 + assert span["metadata"]["model"] == MODEL + assert span["input"] == [{"custom_id": "req-1"}, {"custom_id": "req-2"}] + assert span["output"]["id"] == result.id + assert span["output"]["processing_status"] == "in_progress" + assert span["output"]["request_counts"]["processing"] == 2 @pytest.mark.vcr @pytest.mark.asyncio - async def test_patch_anthropic_async_creates_spans(self, memory_logger): - """`AnthropicIntegration.setup()` should create spans for async API calls.""" + async def test_async_beta_batches_create_produces_span(self, memory_logger): + assert not memory_logger.pop() + + client = wrap_anthropic(_get_async_client()) + result = await client.beta.messages.batches.create(requests=_make_batch_requests()) + + assert result.id + assert result.processing_status == "in_progress" + + spans = memory_logger.pop() + assert len(spans) == 1 + span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.create" + assert span["span_attributes"]["type"] == "task" + assert span["metadata"]["provider"] == "anthropic" + assert span["metadata"]["num_requests"] == 2 + assert span["metadata"]["model"] == MODEL + assert span["input"] == [{"custom_id": "req-1"}, {"custom_id": "req-2"}] + assert span["output"]["id"] == result.id + + +class TestBetaBatchesResultsSpans: + """Tests verifying that beta.messages.batches.results() produces correct spans. + + Mocked because the batch results API requires a completed batch, and batches + can take up to 24 hours to finish processing. + """ + + def test_sync_beta_batches_results_produces_span(self, memory_logger): + assert not memory_logger.pop() + + client = wrap_anthropic(_get_client()) + mock_decoder = unittest.mock.MagicMock() + with unittest.mock.patch( + "anthropic.resources.beta.messages.batches.Batches.results", + return_value=mock_decoder, + ): + result = client.beta.messages.batches.results("msgbatch_beta123") + + assert result is mock_decoder + + spans = memory_logger.pop() + assert len(spans) == 1 + span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.results" + assert span["span_attributes"]["type"] == "task" + assert span["metadata"]["provider"] == "anthropic" + assert span["input"]["message_batch_id"] == "msgbatch_beta123" + assert span["output"]["type"] == "jsonl_stream" + + @pytest.mark.asyncio + async def test_async_beta_batches_results_produces_span(self, memory_logger): assert not memory_logger.pop() - AnthropicIntegration.setup() - client = anthropic.AsyncAnthropic() - response = await client.messages.create( - model="claude-3-5-haiku-latest", - max_tokens=100, - messages=[{"role": "user", "content": "Say hi async"}], - ) - assert response.content[0].text + client = wrap_anthropic(_get_async_client()) + mock_decoder = unittest.mock.MagicMock() + with unittest.mock.patch( + "anthropic.resources.beta.messages.batches.AsyncBatches.results", + return_value=mock_decoder, + ): + result = await client.beta.messages.batches.results(message_batch_id="msgbatch_beta456") + + assert result is mock_decoder - # Verify span was created spans = memory_logger.pop() assert len(spans) == 1 span = spans[0] + assert span["span_attributes"]["name"] == "anthropic.messages.batches.results" assert span["metadata"]["provider"] == "anthropic" - assert "claude" in span["metadata"]["model"] - assert span["input"] + assert span["input"]["message_batch_id"] == "msgbatch_beta456" diff --git a/py/src/braintrust/integrations/anthropic/tracing.py b/py/src/braintrust/integrations/anthropic/tracing.py index de352018..8f0e09b7 100644 --- a/py/src/braintrust/integrations/anthropic/tracing.py +++ b/py/src/braintrust/integrations/anthropic/tracing.py @@ -59,6 +59,10 @@ def __init__(self, messages): super().__init__(messages) self.__messages = messages + @property + def batches(self): + return AsyncBatches(self.__messages.batches) + async def create(self, *args, **kwargs): if kwargs.get("stream", False): return await self.__create_with_stream_true(*args, **kwargs) @@ -148,6 +152,10 @@ def __init__(self, messages): super().__init__(messages) self.__messages = messages + @property + def batches(self): + return Batches(self.__messages.batches) + def stream(self, *args, **kwargs): return self.__trace_stream(self.__messages.stream, *args, **kwargs) @@ -185,6 +193,78 @@ def messages(self): return Messages(self.__beta.messages) +class Batches(Wrapper): + """Wrapper for sync Anthropic Messages Batches resource.""" + + def __init__(self, batches): + super().__init__(batches) + self.__batches = batches + + def create(self, *args, **kwargs): + span = _start_batch_create_span(kwargs) + try: + result = self.__batches.create(*args, **kwargs) + with _catch_exceptions(): + _log_batch_create_to_span(result, span) + return result + except Exception as e: + with _catch_exceptions(): + span.log(error=e) + raise + finally: + span.end() + + def results(self, *args, **kwargs): + span = _start_batch_results_span(args, kwargs) + try: + result = self.__batches.results(*args, **kwargs) + with _catch_exceptions(): + span.log(output={"type": "jsonl_stream"}) + return result + except Exception as e: + with _catch_exceptions(): + span.log(error=e) + raise + finally: + span.end() + + +class AsyncBatches(Wrapper): + """Wrapper for async Anthropic Messages Batches resource.""" + + def __init__(self, batches): + super().__init__(batches) + self.__batches = batches + + async def create(self, *args, **kwargs): + span = _start_batch_create_span(kwargs) + try: + result = await self.__batches.create(*args, **kwargs) + with _catch_exceptions(): + _log_batch_create_to_span(result, span) + return result + except Exception as e: + with _catch_exceptions(): + span.log(error=e) + raise + finally: + span.end() + + async def results(self, *args, **kwargs): + span = _start_batch_results_span(args, kwargs) + try: + result = await self.__batches.results(*args, **kwargs) + with _catch_exceptions(): + span.log(output={"type": "jsonl_stream"}) + return result + except Exception as e: + with _catch_exceptions(): + span.log(error=e) + raise + finally: + span.end() + + class TracedMessageStreamManager(Wrapper): def __init__(self, msg_stream_mgr, span, request_start_time: float): super().__init__(msg_stream_mgr) @@ -276,6 +356,64 @@ def __process_message(self, m): self.__snapshot = accumulate_event(event=m, current_snapshot=self.__snapshot) +def _start_batch_create_span(kwargs): + with _catch_exceptions(): + requests = list(kwargs.get("requests", [])) + # Extract models from the batch requests for metadata + models = set() + for req in requests: + params = req.get("params", {}) if isinstance(req, dict) else getattr(req, "params", {}) + model = params.get("model") if isinstance(params, dict) else getattr(params, "model", None) + if model: + models.add(model) + + metadata = {"provider": "anthropic", "num_requests": len(requests)} + if len(models) == 1: + metadata["model"] = next(iter(models)) + elif models: + metadata["models"] = sorted(models) + + _input = [ + {"custom_id": req.get("custom_id") if isinstance(req, dict) else getattr(req, "custom_id", None)} + for req in requests + ] + + return start_span(name="anthropic.messages.batches.create", type="task", metadata=metadata, input=_input) + + return NOOP_SPAN + + +def _log_batch_create_to_span(result, span): + with _catch_exceptions(): + output = {} + if hasattr(result, "id"): + output["id"] = result.id + if hasattr(result, "processing_status"): + output["processing_status"] = result.processing_status + if hasattr(result, "request_counts"): + rc = result.request_counts + output["request_counts"] = { + "processing": getattr(rc, "processing", 0), + "succeeded": getattr(rc, "succeeded", 0), + "errored": getattr(rc, "errored", 0), + "canceled": getattr(rc, "canceled", 0), + "expired": getattr(rc, "expired", 0), + } + + span.log(output=output) + + +def _start_batch_results_span(args, kwargs): + with _catch_exceptions(): + # message_batch_id can be passed as first positional arg or as kwarg + batch_id = args[0] if args else kwargs.get("message_batch_id") + metadata = {"provider": "anthropic"} + _input = {"message_batch_id": batch_id} + return start_span(name="anthropic.messages.batches.results", type="task", metadata=metadata, input=_input) + + return NOOP_SPAN + + def _get_input_from_kwargs(kwargs): msgs = list(kwargs.get("messages", [])) kwargs["messages"] = msgs.copy()