diff --git a/py/src/braintrust/api/cassettes/test_projects_end_to_end_with_real_backend.yaml b/py/src/braintrust/api/cassettes/test_projects_end_to_end_with_real_backend.yaml index 14202064..b0c861cf 100644 --- a/py/src/braintrust/api/cassettes/test_projects_end_to_end_with_real_backend.yaml +++ b/py/src/braintrust/api/cassettes/test_projects_end_to_end_with_real_backend.yaml @@ -34,7 +34,7 @@ interactions: - '376' Content-Security-Policy: - 'script-src ''self'' ''unsafe-eval'' ''wasm-unsafe-eval'' ''strict-dynamic'' - ''nonce-MzEzZmE3Y2ItOWJiYS00OWU1LWI4OTMtOGQ4NjA2Y2Y1YjUz'' *.js.stripe.com + ''nonce-NWQxZWQ1N2ItYTlkMS00ODNjLTgyMTMtOGMzYjNlYmM0ZmRl'' *.js.stripe.com js.stripe.com maps.googleapis.com ; style-src ''self'' ''unsafe-inline'' *.braintrust.dev btcm6qilbbhv4yi1.public.blob.vercel-storage.com fonts.googleapis.com www.gstatic.com d4tuoctqmanu0.cloudfront.net; font-src ''self'' data: fonts.gstatic.com btcm6qilbbhv4yi1.public.blob.vercel-storage.com @@ -46,7 +46,7 @@ interactions: Content-Type: - application/json; charset=utf-8 Date: - - Fri, 14 Aug 2026 20:02:21 GMT + - Wed, 19 Aug 2026 15:34:34 GMT Etag: - '"13vsc5ye8flag"' Reporting-Endpoints: @@ -55,6 +55,8 @@ interactions: - Vercel Strict-Transport-Security: - max-age=63072000 + X-Bt-Was-Udf-Cached: + - 'true' X-Clerk-Auth-Message: - Invalid JWT form. A JWT consists of three parts separated by dots. (reason=token-invalid, token-carrier=header) @@ -69,11 +71,11 @@ interactions: X-Matched-Path: - /api/apikey/login X-Nonce: - - MzEzZmE3Y2ItOWJiYS00OWU1LWI4OTMtOGQ4NjA2Y2Y1YjUz + - NWQxZWQ1N2ItYTlkMS00ODNjLTgyMTMtOGMzYjNlYmM0ZmRl X-Vercel-Cache: - MISS X-Vercel-Id: - - yul1::iad1::ffbhm-1786737740734-013b054d8dac + - yul1::iad1::nbxrq-1787153674238-664a47219c78 status: code: 200 message: OK @@ -97,25 +99,25 @@ interactions: uri: https://api.braintrust.dev/v1/project response: body: - string: '{"id":"613b27b8-da58-4796-8993-d79cff1afad3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"created - by the Python SDK VCR test","created":"2026-08-14T20:02:21.300Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' + string: '{"id":"7cb22019-3d25-4b51-98a1-63c46abd8bf3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"created + by the Python SDK VCR test","created":"2026-08-19T15:34:34.696Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' headers: Connection: - keep-alive Content-Type: - application/json; charset=utf-8 Date: - - Fri, 14 Aug 2026 20:02:21 GMT + - Wed, 19 Aug 2026 15:34:34 GMT Via: - - 1.1 d38f8e8aaab4437dcb36d4adc5a35cbe.cloudfront.net (CloudFront), 1.1 70fd8dd903406754b301439f9111e256.cloudfront.net + - 1.1 dee47bbe1fe762707eeae94e76255170.cloudfront.net (CloudFront), 1.1 67dd4d73b80aece69a8e725c6d612b6e.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - n1ay16d0BJ_cvkNZS4W6Y20Die9bsM173_DAV567vyqX1oigpIlaog== + - MAymXr7lQWx9PsRr5QuQcHC8vn5Xms7eDvLQ7IGiaxr4gLQ06xwjvQ== X-Amz-Cf-Pop: - YTO53-P2 - YTO50-P2 X-Amzn-Trace-Id: - - Root=1-6a7f744d-25501c3c04858339782f8fde;Parent=575c5e29d6a53b1d;Sampled=0;Lineage=1:24be3d11:0 + - Root=1-6a85cd0a-2da10611309b4b954ebccb4e;Parent=6b5d5ae68a3e1443;Sampled=0;Lineage=1:24be3d11:0 X-Cache: - Miss from cloudfront access-control-allow-credentials: @@ -127,7 +129,7 @@ interactions: content-length: - '307' etag: - - W/"133-J+BLvuGtfyn2Lj4IWFVWVsqLqNc" + - W/"133-AwrbwRApgCQLZaKIW0a3n2CLRn0" expires: - '0' surrogate-control: @@ -135,11 +137,11 @@ interactions: vary: - Origin, Accept-Encoding x-amz-apigw-id: - - CG8cIH6YIAMEu8g= + - CWz5tHgFIAMEXiA= x-amzn-RequestId: - - 654f58b9-9d16-4c57-8616-1ee0dd15ab61 + - b8fba74d-7685-45c8-8871-6c19c1589fb3 x-bt-internal-trace-id: - - 6a7f744d0000000049c801445f2ae4ab + - 6a85cd0a00000000549dd5ecd2a13a2c status: code: 200 message: OK @@ -158,25 +160,25 @@ interactions: uri: https://api.braintrust.dev/v1/project?project_name=python-sdk-generated-projects-vcr&org_name=Braintrust%20SDKs response: body: - string: '{"objects":[{"id":"613b27b8-da58-4796-8993-d79cff1afad3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"created - by the Python SDK VCR test","created":"2026-08-14T20:02:21.300Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}]}' + string: '{"objects":[{"id":"7cb22019-3d25-4b51-98a1-63c46abd8bf3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"created + by the Python SDK VCR test","created":"2026-08-19T15:34:34.696Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}]}' headers: Connection: - keep-alive Content-Type: - application/json; charset=utf-8 Date: - - Fri, 14 Aug 2026 20:02:21 GMT + - Wed, 19 Aug 2026 15:34:34 GMT Via: - - 1.1 8250156022879efefd7a589c8ba8c706.cloudfront.net (CloudFront), 1.1 7293b56f3a0eb541aadcbcaa0146d528.cloudfront.net + - 1.1 dee47bbe1fe762707eeae94e76255170.cloudfront.net (CloudFront), 1.1 cb0c6226aa19d81a39519501df383968.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - f6M-kbi-SL_TPm4YD0yvWJNKubVVNE58UQd8H5TxFGv5gGRnDayRFw== + - Ckpn3O3JQb9FXRZ5_Pt5kwg58NWTlQrHeiLH1Xvcv90Ml9A7yzFusQ== X-Amz-Cf-Pop: - YTO53-P2 - YTO50-P2 X-Amzn-Trace-Id: - - Root=1-6a7f744d-7a079b485298fbba1d379bfe;Parent=67d61fbe4ef2b858;Sampled=0;Lineage=1:24be3d11:0 + - Root=1-6a85cd0a-3b88469b45eb322c7e2566fd;Parent=155a61e32a5f9660;Sampled=0;Lineage=1:24be3d11:0 X-Cache: - Miss from cloudfront access-control-allow-credentials: @@ -188,7 +190,7 @@ interactions: content-length: - '321' etag: - - W/"141-Q/vI9D3l1GEcxigmhdgU4QDoPr4" + - W/"141-gVhNN1zpLB3loJGZSPrywZrJUt0" expires: - '0' surrogate-control: @@ -196,11 +198,11 @@ interactions: vary: - Origin, Accept-Encoding x-amz-apigw-id: - - CG8cMH0EIAMEE6Q= + - CWz5xFVJIAMEMpw= x-amzn-RequestId: - - 15063a57-e16a-432c-92ae-7a4115044629 + - f24c67bb-00f9-44e8-9dcc-8dfab02846c1 x-bt-internal-trace-id: - - 6a7f744d0000000006fc9365f6b00acb + - 6a85cd0a0000000064f1ba66bdca9f3d status: code: 200 message: OK @@ -216,28 +218,28 @@ interactions: User-Agent: - python-requests/2.34.2 method: GET - uri: https://api.braintrust.dev/v1/project/613b27b8-da58-4796-8993-d79cff1afad3 + uri: https://api.braintrust.dev/v1/project/7cb22019-3d25-4b51-98a1-63c46abd8bf3 response: body: - string: '{"id":"613b27b8-da58-4796-8993-d79cff1afad3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"created - by the Python SDK VCR test","created":"2026-08-14T20:02:21.300Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' + string: '{"id":"7cb22019-3d25-4b51-98a1-63c46abd8bf3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"created + by the Python SDK VCR test","created":"2026-08-19T15:34:34.696Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' headers: Connection: - keep-alive Content-Type: - application/json; charset=utf-8 Date: - - Fri, 14 Aug 2026 20:02:21 GMT + - Wed, 19 Aug 2026 15:34:35 GMT Via: - - 1.1 d38f8e8aaab4437dcb36d4adc5a35cbe.cloudfront.net (CloudFront), 1.1 36c050103b969d83a8b90ba7cba12542.cloudfront.net + - 1.1 e7e881849322d751aeeb9605914b08b4.cloudfront.net (CloudFront), 1.1 4f3eaee3896fb5ad2377261bd0d773c8.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - -Ow0f7p7F48nNhJdla7V0_UyJ5iIwurCIqdE0YDDUIv2A79IDow36Q== + - MWgnEPPt-7rWZMaKlzYsiDNXOAdmEeMe2SxTAsD5zvmNJ8_QfiI4Yw== X-Amz-Cf-Pop: - YTO53-P2 - YTO50-P2 X-Amzn-Trace-Id: - - Root=1-6a7f744d-07e24fb532e649db2a93f9e3;Parent=67ee5e76f1f143bc;Sampled=0;Lineage=1:24be3d11:0 + - Root=1-6a85cd0b-7ac3f4614470499523b2f894;Parent=26b34bd8f82cf363;Sampled=0;Lineage=1:24be3d11:0 X-Cache: - Miss from cloudfront access-control-allow-credentials: @@ -249,7 +251,7 @@ interactions: content-length: - '307' etag: - - W/"133-J+BLvuGtfyn2Lj4IWFVWVsqLqNc" + - W/"133-AwrbwRApgCQLZaKIW0a3n2CLRn0" expires: - '0' surrogate-control: @@ -257,11 +259,11 @@ interactions: vary: - Origin, Accept-Encoding x-amz-apigw-id: - - CG8cPEbBIAMEv3Q= + - CWz5zHTwoAMEFBQ= x-amzn-RequestId: - - e77b6c34-4374-4175-9b33-210fe5c99104 + - e80ba743-59ed-4f2a-8a28-470d063ab5db x-bt-internal-trace-id: - - 6a7f744d000000007bd2cb96555aa91a + - 6a85cd0b000000005602f6ad8d3b49cd status: code: 200 message: OK @@ -281,28 +283,236 @@ interactions: User-Agent: - python-requests/2.34.2 method: PATCH - uri: https://api.braintrust.dev/v1/project/613b27b8-da58-4796-8993-d79cff1afad3 + uri: https://api.braintrust.dev/v1/project/7cb22019-3d25-4b51-98a1-63c46abd8bf3 + response: + body: + string: '{"id":"7cb22019-3d25-4b51-98a1-63c46abd8bf3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"updated + by the Python SDK VCR test","created":"2026-08-19T15:34:34.696Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' + headers: + Connection: + - keep-alive + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 19 Aug 2026 15:34:35 GMT + Via: + - 1.1 e7e881849322d751aeeb9605914b08b4.cloudfront.net (CloudFront), 1.1 21c66eb5f493a6e3ddbaa803cebfe014.cloudfront.net + (CloudFront) + X-Amz-Cf-Id: + - XQQj-6zomPxfVtUhyv0YhWsJUiuc9DFWc6RcPv6J21KDGq3cZCFZQg== + X-Amz-Cf-Pop: + - YTO53-P2 + - YTO50-P2 + X-Amzn-Trace-Id: + - Root=1-6a85cd0b-2ba8fdd0042e817c49236677;Parent=6b731128a11bd7ea;Sampled=0;Lineage=1:24be3d11:0 + X-Cache: + - Miss from cloudfront + access-control-allow-credentials: + - 'true' + access-control-expose-headers: + - x-bt-cursor,x-bt-found-existing,x-bt-query-plan,x-bt-api-duration-ms,x-bt-brainstore-duration-ms,x-bt-internal-trace-id,x-bt-error-origin,x-bt-used-endpoint,x-bt-overflow-url + cache-control: + - no-store, no-cache, must-revalidate, proxy-revalidate + content-length: + - '307' + etag: + - W/"133-mL500AMuIB9CcF6OX2d2ioXMSWs" + expires: + - '0' + surrogate-control: + - no-store + vary: + - Origin, Accept-Encoding + x-amz-apigw-id: + - CWz52GVVoAMEoew= + x-amzn-RequestId: + - 16921f3c-04d1-4e52-95e2-e183be8d8028 + x-bt-internal-trace-id: + - 6a85cd0b0000000049fc895c2b9646f5 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, zstd + Connection: + - keep-alive + Content-Length: + - '0' + User-Agent: + - python-requests/2.34.2 + method: POST + uri: https://www.braintrust.dev/api/apikey/login + response: + body: + string: '{"org_info":[{"id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"Braintrust + SDKs","api_url":"https://api.braintrust.dev","git_metadata":{"collect":"some","fields":["commit","branch","tag","dirty","author_name","author_email","commit_message","commit_time"]},"is_universal_api":null,"proxy_url":"https://api.braintrust.dev","realtime_url":"wss://realtime.braintrustapi.com"}]}' + headers: + Access-Control-Allow-Credentials: + - 'true' + Access-Control-Allow-Headers: + - X-CSRF-Token, X-Requested-With, Accept, Accept-Version, Content-Length, Content-MD5, + Content-Type, Date, X-Api-Version + Access-Control-Allow-Methods: + - GET,OPTIONS,PATCH,DELETE,POST,PUT + Access-Control-Allow-Origin: + - '*' + Cache-Control: + - public, max-age=0, must-revalidate + Content-Length: + - '376' + Content-Security-Policy: + - 'script-src ''self'' ''unsafe-eval'' ''wasm-unsafe-eval'' ''strict-dynamic'' + ''nonce-ZWRlY2M3YTgtYTk5Zi00YjFhLWFkMGEtMjJiMDBmOGE5ODFm'' *.js.stripe.com + js.stripe.com maps.googleapis.com ; style-src ''self'' ''unsafe-inline'' *.braintrust.dev + btcm6qilbbhv4yi1.public.blob.vercel-storage.com fonts.googleapis.com www.gstatic.com + d4tuoctqmanu0.cloudfront.net; font-src ''self'' data: fonts.gstatic.com btcm6qilbbhv4yi1.public.blob.vercel-storage.com + cdn.jsdelivr.net d4tuoctqmanu0.cloudfront.net fonts.googleapis.com mintlify-assets.b-cdn.net + fonts.cdnfonts.com; object-src ''none''; base-uri ''self''; form-action ''self'' + https://www.facebook.com; frame-ancestors ''self''; worker-src ''self'' blob:; + report-uri https://o4507221741076480.ingest.us.sentry.io/api/4507221754380288/security/?sentry_key=27fa5ac907cf7c6ce4a1ab2a03f805b4&sentry_environment=production&sentry_release=16; + report-to csp-endpoint-0' + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 19 Aug 2026 15:34:35 GMT + Etag: + - '"13vsc5ye8flag"' + Reporting-Endpoints: + - csp-endpoint-0="https://o4507221741076480.ingest.us.sentry.io/api/4507221754380288/security/?sentry_key=27fa5ac907cf7c6ce4a1ab2a03f805b4&sentry_environment=production&sentry_release=16" + Server: + - Vercel + Strict-Transport-Security: + - max-age=63072000 + X-Bt-Was-Udf-Cached: + - 'true' + X-Clerk-Auth-Message: + - Invalid JWT form. A JWT consists of three parts separated by dots. (reason=token-invalid, + token-carrier=header) + X-Clerk-Auth-Reason: + - token-invalid + X-Clerk-Auth-Status: + - signed-out + X-Content-Type-Options: + - nosniff + X-Frame-Options: + - SAMEORIGIN + X-Matched-Path: + - /api/apikey/login + X-Nonce: + - ZWRlY2M3YTgtYTk5Zi00YjFhLWFkMGEtMjJiMDBmOGE5ODFm + X-Vercel-Cache: + - MISS + X-Vercel-Id: + - yul1::iad1::g2zxp-1787153675642-d1d25011329c + status: + code: 200 + message: OK +- request: + body: '{"name": "python-sdk-generated-projects-vcr", "org_name": "Braintrust SDKs"}' + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, zstd + Connection: + - keep-alive + Content-Length: + - '76' + Content-Type: + - application/json + User-Agent: + - python-requests/2.34.2 + method: POST + uri: https://api.braintrust.dev/v1/project + response: + body: + string: '{"id":"7cb22019-3d25-4b51-98a1-63c46abd8bf3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"updated + by the Python SDK VCR test","created":"2026-08-19T15:34:34.696Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' + headers: + Connection: + - keep-alive + Content-Type: + - application/json; charset=utf-8 + Date: + - Wed, 19 Aug 2026 15:34:35 GMT + Via: + - 1.1 a9372ab148a2984b992fb2eac84148d0.cloudfront.net (CloudFront), 1.1 b734db9b28028c2ed717c3d72b3b45b8.cloudfront.net + (CloudFront) + X-Amz-Cf-Id: + - SOQUO6qCQEBHxx6vf6G2JqQcWyG3nDlthPd6WC_ATnemPiavq7lD4g== + X-Amz-Cf-Pop: + - YTO53-P2 + - YTO50-P2 + X-Amzn-Trace-Id: + - Root=1-6a85cd0b-4f870c206d7b217a49b76bb7;Parent=7d034aa24b961c62;Sampled=0;Lineage=1:24be3d11:0 + X-Cache: + - Miss from cloudfront + access-control-allow-credentials: + - 'true' + access-control-expose-headers: + - x-bt-cursor,x-bt-found-existing,x-bt-query-plan,x-bt-api-duration-ms,x-bt-brainstore-duration-ms,x-bt-internal-trace-id,x-bt-error-origin,x-bt-used-endpoint,x-bt-overflow-url + cache-control: + - no-store, no-cache, must-revalidate, proxy-revalidate + content-length: + - '307' + etag: + - W/"133-mL500AMuIB9CcF6OX2d2ioXMSWs" + expires: + - '0' + surrogate-control: + - no-store + vary: + - Origin, Accept-Encoding + x-amz-apigw-id: + - CWz57FowIAMEU_g= + x-amzn-RequestId: + - 018e8d15-09e1-445a-a0b2-64275074348f + x-bt-found-existing: + - 'true' + x-bt-internal-trace-id: + - 6a85cd0b0000000024e103511f31a356 + status: + code: 200 + message: OK +- request: + body: null + headers: + Accept: + - '*/*' + Accept-Encoding: + - gzip, deflate, zstd + Connection: + - keep-alive + User-Agent: + - python-requests/2.34.2 + method: GET + uri: https://api.braintrust.dev/v1/project/7cb22019-3d25-4b51-98a1-63c46abd8bf3 response: body: - string: '{"id":"613b27b8-da58-4796-8993-d79cff1afad3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"updated - by the Python SDK VCR test","created":"2026-08-14T20:02:21.300Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' + string: '{"id":"7cb22019-3d25-4b51-98a1-63c46abd8bf3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"updated + by the Python SDK VCR test","created":"2026-08-19T15:34:34.696Z","deleted_at":null,"user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' headers: Connection: - keep-alive Content-Type: - application/json; charset=utf-8 Date: - - Fri, 14 Aug 2026 20:02:22 GMT + - Wed, 19 Aug 2026 15:34:36 GMT Via: - - 1.1 d38f8e8aaab4437dcb36d4adc5a35cbe.cloudfront.net (CloudFront), 1.1 6889869bf680fe34cca722f0a05e1106.cloudfront.net + - 1.1 a9372ab148a2984b992fb2eac84148d0.cloudfront.net (CloudFront), 1.1 d03af248468c898a111754f0666c2316.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - sWBfxSN30owXXvYuOuBahaAAhqIi_IHRWJuI-ddXXt0qN-_sY7nQhg== + - qgmzb1VgdvXMYoiDYvgeH7xp6Gl31tj38RhyP4RKDLECh6Pjl8bpAQ== X-Amz-Cf-Pop: - YTO53-P2 - YTO50-P2 X-Amzn-Trace-Id: - - Root=1-6a7f744e-12cd753c03328c487a650ab6;Parent=1d830992c5b5174a;Sampled=0;Lineage=1:24be3d11:0 + - Root=1-6a85cd0b-7f912f34687f94f203e91119;Parent=713671a372c30857;Sampled=0;Lineage=1:24be3d11:0 X-Cache: - Miss from cloudfront access-control-allow-credentials: @@ -314,7 +524,7 @@ interactions: content-length: - '307' etag: - - W/"133-LM8/56xANPuebOLm3mEY13c/1pY" + - W/"133-mL500AMuIB9CcF6OX2d2ioXMSWs" expires: - '0' surrogate-control: @@ -322,11 +532,11 @@ interactions: vary: - Origin, Accept-Encoding x-amz-apigw-id: - - CG8cRHASIAMEgcA= + - CWz58HnzIAMENtQ= x-amzn-RequestId: - - 224f1f09-1d4f-4ebe-8d7b-fdfdbc1cad5e + - 5f55c0b1-8cc2-4746-98ee-fb94ba85c71e x-bt-internal-trace-id: - - 6a7f744e000000000fe9bcac73f7625a + - 6a85cd0c0000000035dee951084ac2ff status: code: 200 message: OK @@ -344,28 +554,28 @@ interactions: User-Agent: - python-requests/2.34.2 method: DELETE - uri: https://api.braintrust.dev/v1/project/613b27b8-da58-4796-8993-d79cff1afad3 + uri: https://api.braintrust.dev/v1/project/7cb22019-3d25-4b51-98a1-63c46abd8bf3 response: body: - string: '{"id":"613b27b8-da58-4796-8993-d79cff1afad3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"updated - by the Python SDK VCR test","created":"2026-08-14T20:02:21.300Z","deleted_at":"2026-08-14T20:02:22.434Z","user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' + string: '{"id":"7cb22019-3d25-4b51-98a1-63c46abd8bf3","org_id":"5abfae3a-7aa7-4653-a9c8-b3efcb18f584","name":"python-sdk-generated-projects-vcr","description":"updated + by the Python SDK VCR test","created":"2026-08-19T15:34:34.696Z","deleted_at":"2026-08-19T15:34:36.559Z","user_id":"c1f71e19-b3ce-4f59-89a9-055901f7755b","settings":null}' headers: Connection: - keep-alive Content-Type: - application/json; charset=utf-8 Date: - - Fri, 14 Aug 2026 20:02:22 GMT + - Wed, 19 Aug 2026 15:34:36 GMT Via: - - 1.1 561ea23d1fbb47b35216fa7040bfaa74.cloudfront.net (CloudFront), 1.1 10f12ad63ad88e4e38e4e73deb3e9570.cloudfront.net + - 1.1 dee47bbe1fe762707eeae94e76255170.cloudfront.net (CloudFront), 1.1 4ec5f8da969dc981ba2067c9dad5dad8.cloudfront.net (CloudFront) X-Amz-Cf-Id: - - TwX8S5DWN5Guvm0GuA7T-IIBL9AIcEARmMjJT1b484o_aVc-XsU8wg== + - HfaAHP6NR58JevZJDSH6b7Ss5H4qIAFnWSdG_zjGAimZjnybv1ZHKQ== X-Amz-Cf-Pop: - YTO53-P2 - YTO50-P2 X-Amzn-Trace-Id: - - Root=1-6a7f744e-4b99ebd85091eeed2c28e93f;Parent=36e67096fb7d4942;Sampled=0;Lineage=1:24be3d11:0 + - Root=1-6a85cd0c-09bfa0304cb07c595d621b81;Parent=33f9ab90bfac2c79;Sampled=0;Lineage=1:24be3d11:0 X-Cache: - Miss from cloudfront access-control-allow-credentials: @@ -377,7 +587,7 @@ interactions: content-length: - '329' etag: - - W/"149-ZEM0N6DPDhpdgarT6R15u2oVDag" + - W/"149-IAr7BkTfeM/AQqK3OB8nCGV1Pto" expires: - '0' surrogate-control: @@ -385,11 +595,11 @@ interactions: vary: - Origin, Accept-Encoding x-amz-apigw-id: - - CG8cUGQboAMEv7Q= + - CWz6BHm6IAMEJjQ= x-amzn-RequestId: - - ff2c1211-e5b7-40ac-ab3f-3aa36933ef0b + - e60f0ef4-e6b2-418a-8262-bab26097d4b1 x-bt-internal-trace-id: - - 6a7f744e0000000015b1866d584ef16c + - 6a85cd0c00000000631ab2c75ed35869 status: code: 200 message: OK diff --git a/py/src/braintrust/api/test_projects.py b/py/src/braintrust/api/test_projects.py index aaa53c97..ca36baba 100644 --- a/py/src/braintrust/api/test_projects.py +++ b/py/src/braintrust/api/test_projects.py @@ -1,5 +1,7 @@ import pytest +from braintrust import init_logger from braintrust.api import BraintrustClient +from braintrust.logger import BraintrustState @pytest.mark.vcr @@ -22,6 +24,14 @@ def test_projects_end_to_end_with_real_backend(api_key): updated = client.openapi.projects.patch_project_id( created["id"], body={"description": "updated by the Python SDK VCR test"} ) + + state = BraintrustState() + state.login(api_key=api_key, org_name=discovery.organization.name) + registered_logger = init_logger(project=project_name, state=state, set_current=False) + looked_up_logger = init_logger(project_id=created["id"], state=state, set_current=False) + assert registered_logger.id == created["id"] + assert looked_up_logger.project.name == project_name + deleted = client.openapi.projects.delete_project_id(created["id"]) assert create_project == { diff --git a/py/src/braintrust/cassettes/test_register_sandbox_single_evaluator.yaml b/py/src/braintrust/cassettes/test_register_sandbox_single_evaluator.yaml index 4ae9371a..4365719c 100644 --- a/py/src/braintrust/cassettes/test_register_sandbox_single_evaluator.yaml +++ b/py/src/braintrust/cassettes/test_register_sandbox_single_evaluator.yaml @@ -1,76 +1,4 @@ interactions: -- request: - body: '{"project_name": "My Project", "org_id": "bf61839e-94bb-4a81-85ed-26b533a24b61"}' - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '80' - Content-Type: - - application/json - User-Agent: - - python-requests/2.32.5 - method: POST - uri: https://www.braintrust.dev/api/project/register - response: - body: - string: '{"project":{"id":"6b770640-a26a-4680-9d69-537262023137","org_id":"bf61839e-94bb-4a81-85ed-26b533a24b61","name":"My - Project","description":null,"created":"2026-03-12T19:49:37.394Z","deleted_at":null,"user_id":"fd67d1e5-c011-43fa-8096-ee5fed2b7df1","settings":null},"found_existing":true}' - headers: - Cache-Control: - - public, max-age=0, must-revalidate - Content-Length: - - '286' - Content-Security-Policy: - - 'script-src ''self'' ''unsafe-eval'' ''wasm-unsafe-eval'' ''strict-dynamic'' - ''nonce-ZDFkNjJkY2EtMWVmOS00ZTZmLThjZTItNGRmMjAzMWZlZDZh'' *.js.stripe.com - js.stripe.com maps.googleapis.com ; style-src ''self'' ''unsafe-inline'' *.braintrust.dev - btcm6qilbbhv4yi1.public.blob.vercel-storage.com fonts.googleapis.com www.gstatic.com - d4tuoctqmanu0.cloudfront.net; font-src ''self'' data: fonts.gstatic.com btcm6qilbbhv4yi1.public.blob.vercel-storage.com - cdn.jsdelivr.net d4tuoctqmanu0.cloudfront.net fonts.googleapis.com mintlify-assets.b-cdn.net - fonts.cdnfonts.com; object-src ''none''; base-uri ''self''; form-action ''self''; - frame-ancestors ''self''; worker-src ''self'' blob:; report-uri https://o4507221741076480.ingest.us.sentry.io/api/4507221754380288/security/?sentry_key=27fa5ac907cf7c6ce4a1ab2a03f805b4&sentry_environment=production&sentry_release=16; - report-to csp-endpoint-0' - Content-Type: - - application/json; charset=utf-8 - Date: - - Thu, 12 Mar 2026 20:26:58 GMT - Etag: - - '"kzpvhxucks7y"' - Reporting-Endpoints: - - csp-endpoint-0="https://o4507221741076480.ingest.us.sentry.io/api/4507221754380288/security/?sentry_key=27fa5ac907cf7c6ce4a1ab2a03f805b4&sentry_environment=production&sentry_release=16" - Server: - - Vercel - Strict-Transport-Security: - - max-age=63072000 - X-Bt-Was-Udf-Cached: - - 'true' - X-Clerk-Auth-Message: - - Invalid JWT form. A JWT consists of three parts separated by dots. (reason=token-invalid, - token-carrier=header) - X-Clerk-Auth-Reason: - - token-invalid - X-Clerk-Auth-Status: - - signed-out - X-Content-Type-Options: - - nosniff - X-Frame-Options: - - SAMEORIGIN - X-Matched-Path: - - /api/project/register - X-Nonce: - - ZDFkNjJkY2EtMWVmOS00ZTZmLThjZTItNGRmMjAzMWZlZDZh - X-Vercel-Cache: - - MISS - X-Vercel-Id: - - sfo1::iad1::tp2pw-1773347218334-903d13f4bb17 - status: - code: 200 - message: OK - request: body: '{"sandbox_spec": {"provider": "modal", "snapshot_ref": "im-icRxmsk1Sz9XPP2f8OblVU"}, "entrypoints": ["./local/js/optimization/evals/btql-generation/btql-queries.eval.ts"], diff --git a/py/src/braintrust/cassettes/test_register_sandbox_with_entrypoints.yaml b/py/src/braintrust/cassettes/test_register_sandbox_with_entrypoints.yaml index 4ae9371a..4365719c 100644 --- a/py/src/braintrust/cassettes/test_register_sandbox_with_entrypoints.yaml +++ b/py/src/braintrust/cassettes/test_register_sandbox_with_entrypoints.yaml @@ -1,76 +1,4 @@ interactions: -- request: - body: '{"project_name": "My Project", "org_id": "bf61839e-94bb-4a81-85ed-26b533a24b61"}' - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '80' - Content-Type: - - application/json - User-Agent: - - python-requests/2.32.5 - method: POST - uri: https://www.braintrust.dev/api/project/register - response: - body: - string: '{"project":{"id":"6b770640-a26a-4680-9d69-537262023137","org_id":"bf61839e-94bb-4a81-85ed-26b533a24b61","name":"My - Project","description":null,"created":"2026-03-12T19:49:37.394Z","deleted_at":null,"user_id":"fd67d1e5-c011-43fa-8096-ee5fed2b7df1","settings":null},"found_existing":true}' - headers: - Cache-Control: - - public, max-age=0, must-revalidate - Content-Length: - - '286' - Content-Security-Policy: - - 'script-src ''self'' ''unsafe-eval'' ''wasm-unsafe-eval'' ''strict-dynamic'' - ''nonce-ZDFkNjJkY2EtMWVmOS00ZTZmLThjZTItNGRmMjAzMWZlZDZh'' *.js.stripe.com - js.stripe.com maps.googleapis.com ; style-src ''self'' ''unsafe-inline'' *.braintrust.dev - btcm6qilbbhv4yi1.public.blob.vercel-storage.com fonts.googleapis.com www.gstatic.com - d4tuoctqmanu0.cloudfront.net; font-src ''self'' data: fonts.gstatic.com btcm6qilbbhv4yi1.public.blob.vercel-storage.com - cdn.jsdelivr.net d4tuoctqmanu0.cloudfront.net fonts.googleapis.com mintlify-assets.b-cdn.net - fonts.cdnfonts.com; object-src ''none''; base-uri ''self''; form-action ''self''; - frame-ancestors ''self''; worker-src ''self'' blob:; report-uri https://o4507221741076480.ingest.us.sentry.io/api/4507221754380288/security/?sentry_key=27fa5ac907cf7c6ce4a1ab2a03f805b4&sentry_environment=production&sentry_release=16; - report-to csp-endpoint-0' - Content-Type: - - application/json; charset=utf-8 - Date: - - Thu, 12 Mar 2026 20:26:58 GMT - Etag: - - '"kzpvhxucks7y"' - Reporting-Endpoints: - - csp-endpoint-0="https://o4507221741076480.ingest.us.sentry.io/api/4507221754380288/security/?sentry_key=27fa5ac907cf7c6ce4a1ab2a03f805b4&sentry_environment=production&sentry_release=16" - Server: - - Vercel - Strict-Transport-Security: - - max-age=63072000 - X-Bt-Was-Udf-Cached: - - 'true' - X-Clerk-Auth-Message: - - Invalid JWT form. A JWT consists of three parts separated by dots. (reason=token-invalid, - token-carrier=header) - X-Clerk-Auth-Reason: - - token-invalid - X-Clerk-Auth-Status: - - signed-out - X-Content-Type-Options: - - nosniff - X-Frame-Options: - - SAMEORIGIN - X-Matched-Path: - - /api/project/register - X-Nonce: - - ZDFkNjJkY2EtMWVmOS00ZTZmLThjZTItNGRmMjAzMWZlZDZh - X-Vercel-Cache: - - MISS - X-Vercel-Id: - - sfo1::iad1::tp2pw-1773347218334-903d13f4bb17 - status: - code: 200 - message: OK - request: body: '{"sandbox_spec": {"provider": "modal", "snapshot_ref": "im-icRxmsk1Sz9XPP2f8OblVU"}, "entrypoints": ["./local/js/optimization/evals/btql-generation/btql-queries.eval.ts"], diff --git a/py/src/braintrust/cassettes/test_register_sandbox_with_metadata_and_description.yaml b/py/src/braintrust/cassettes/test_register_sandbox_with_metadata_and_description.yaml index 563b20ef..0dea4edf 100644 --- a/py/src/braintrust/cassettes/test_register_sandbox_with_metadata_and_description.yaml +++ b/py/src/braintrust/cassettes/test_register_sandbox_with_metadata_and_description.yaml @@ -1,76 +1,4 @@ interactions: -- request: - body: '{"project_name": "My Project", "org_id": "bf61839e-94bb-4a81-85ed-26b533a24b61"}' - headers: - Accept: - - '*/*' - Accept-Encoding: - - gzip, deflate - Connection: - - keep-alive - Content-Length: - - '80' - Content-Type: - - application/json - User-Agent: - - python-requests/2.32.5 - method: POST - uri: https://www.braintrust.dev/api/project/register - response: - body: - string: '{"project":{"id":"6b770640-a26a-4680-9d69-537262023137","org_id":"bf61839e-94bb-4a81-85ed-26b533a24b61","name":"My - Project","description":null,"created":"2026-03-12T19:49:37.394Z","deleted_at":null,"user_id":"fd67d1e5-c011-43fa-8096-ee5fed2b7df1","settings":null},"found_existing":true}' - headers: - Cache-Control: - - public, max-age=0, must-revalidate - Content-Length: - - '286' - Content-Security-Policy: - - 'script-src ''self'' ''unsafe-eval'' ''wasm-unsafe-eval'' ''strict-dynamic'' - ''nonce-OTZlNWI3NDUtOWJjYi00NjA3LWI3MzEtNzhhZWQ0OGNmMmFh'' *.js.stripe.com - js.stripe.com maps.googleapis.com ; style-src ''self'' ''unsafe-inline'' *.braintrust.dev - btcm6qilbbhv4yi1.public.blob.vercel-storage.com fonts.googleapis.com www.gstatic.com - d4tuoctqmanu0.cloudfront.net; font-src ''self'' data: fonts.gstatic.com btcm6qilbbhv4yi1.public.blob.vercel-storage.com - cdn.jsdelivr.net d4tuoctqmanu0.cloudfront.net fonts.googleapis.com mintlify-assets.b-cdn.net - fonts.cdnfonts.com; object-src ''none''; base-uri ''self''; form-action ''self''; - frame-ancestors ''self''; worker-src ''self'' blob:; report-uri https://o4507221741076480.ingest.us.sentry.io/api/4507221754380288/security/?sentry_key=27fa5ac907cf7c6ce4a1ab2a03f805b4&sentry_environment=production&sentry_release=16; - report-to csp-endpoint-0' - Content-Type: - - application/json; charset=utf-8 - Date: - - Thu, 12 Mar 2026 20:26:48 GMT - Etag: - - '"kzpvhxucks7y"' - Reporting-Endpoints: - - csp-endpoint-0="https://o4507221741076480.ingest.us.sentry.io/api/4507221754380288/security/?sentry_key=27fa5ac907cf7c6ce4a1ab2a03f805b4&sentry_environment=production&sentry_release=16" - Server: - - Vercel - Strict-Transport-Security: - - max-age=63072000 - X-Bt-Was-Udf-Cached: - - 'true' - X-Clerk-Auth-Message: - - Invalid JWT form. A JWT consists of three parts separated by dots. (reason=token-invalid, - token-carrier=header) - X-Clerk-Auth-Reason: - - token-invalid - X-Clerk-Auth-Status: - - signed-out - X-Content-Type-Options: - - nosniff - X-Frame-Options: - - SAMEORIGIN - X-Matched-Path: - - /api/project/register - X-Nonce: - - OTZlNWI3NDUtOWJjYi00NjA3LWI3MzEtNzhhZWQ0OGNmMmFh - X-Vercel-Cache: - - MISS - X-Vercel-Id: - - sfo1::iad1::8hdkr-1773347208825-87a220df8e21 - status: - code: 200 - message: OK - request: body: '{"sandbox_spec": {"provider": "modal", "snapshot_ref": "im-icRxmsk1Sz9XPP2f8OblVU"}, "entrypoints": ["./local/js/optimization/evals/btql-generation/btql-queries.eval.ts"], diff --git a/py/src/braintrust/framework2.py b/py/src/braintrust/framework2.py index c5669e8f..4bda4dbb 100644 --- a/py/src/braintrust/framework2.py +++ b/py/src/braintrust/framework2.py @@ -4,7 +4,7 @@ from typing import Any, overload import slugify -from braintrust.logger import api_conn, app_conn, login +from braintrust.logger import _internal_get_global_state, api_conn, login from .framework import _is_lazy_load, bcolors # type: ignore from .generated_types import ( @@ -28,8 +28,9 @@ def __init__(self): def get_by_name(self, project_name: str) -> str: if project_name not in self._name_cache: - resp = app_conn().post_json("api/project/register", {"project_name": project_name}) - self._name_cache[project_name] = resp["project"]["id"] + state = _internal_get_global_state() + project = state.api_client().projects.post_project(body={"name": project_name, "org_name": state.org_name}) + self._name_cache[project_name] = project["id"] return self._name_cache[project_name] def get(self, project: "Project") -> str: diff --git a/py/src/braintrust/logger.py b/py/src/braintrust/logger.py index 3dc88b5f..fbfd90dd 100644 --- a/py/src/braintrust/logger.py +++ b/py/src/braintrust/logger.py @@ -1737,26 +1737,27 @@ def compute_metadata(): ) -def _compute_logger_metadata(project_name: str | None = None, project_id: str | None = None): - login() - org_id = _state.org_id +def _compute_logger_metadata( + project_name: str | None = None, + project_id: str | None = None, + state: BraintrustState | None = None, +): + state = state or _state + state.login() + org_id = state.org_id if project_id is None: - response = _state.app_conn().post_json( - "api/project/register", - { - "project_name": project_name or GLOBAL_PROJECT, - "org_id": org_id, - }, + response = state.api_client().projects.post_project( + body={"name": project_name or GLOBAL_PROJECT, "org_name": state.org_name} ) - resp_project = response["project"] return OrgProjectMetadata( org_id=org_id, - project=ObjectMetadata(id=resp_project["id"], name=resp_project["name"], full_info=resp_project), + project=ObjectMetadata(id=response["id"], name=response["name"], full_info=dict(response)), ) elif project_name is None: - response = _state.app_conn().get_json("api/project", {"id": project_id}) + response = state.api_client().projects.get_project_id(project_id) return OrgProjectMetadata( - org_id=org_id, project=ObjectMetadata(id=project_id, name=response["name"], full_info=response) + org_id=org_id, + project=ObjectMetadata(id=response["id"], name=response["name"], full_info=dict(response)), ) else: return OrgProjectMetadata( @@ -1804,7 +1805,7 @@ def init_logger( def compute_metadata(): state.login(org_name=org_name, api_key=api_key, app_url=app_url, force_login=force_login) - return _compute_logger_metadata(**compute_metadata_args) + return _compute_logger_metadata(**compute_metadata_args, state=state) # For loggers, enable queue size limit enforcement (bounded queue) state.enforce_queue_size_limit(True) @@ -5515,17 +5516,13 @@ def lazy_init(self): if self._id is None or self._name is None: with self.init_lock: if self._id is None: - response = _state.app_conn().post_json( - "api/project/register", - { - "project_name": self._name or GLOBAL_PROJECT, - "org_id": _state.org_id, - }, + response = _state.api_client().projects.post_project( + body={"name": self._name or GLOBAL_PROJECT, "org_name": _state.org_name} ) - self._id = response["project"]["id"] - self._name = response["project"]["name"] + self._id = response["id"] + self._name = response["name"] elif self._name is None: - response = _state.app_conn().get_json("api/project", {"id": self._id}) + response = _state.api_client().projects.get_project_id(self._id) self._name = response["name"] return self diff --git a/py/src/braintrust/sandbox.py b/py/src/braintrust/sandbox.py index d621c355..c2433c5a 100644 --- a/py/src/braintrust/sandbox.py +++ b/py/src/braintrust/sandbox.py @@ -91,10 +91,8 @@ def register_sandbox( state = state or _state state.login(api_key=api_key, app_url=app_url, org_name=org_name) - project_response = state.app_conn().post_json( - "api/project/register", {"project_name": project, "org_id": state.org_id} - ) - project_id = project_response["project"]["id"] + project_response = state.api_client().projects.post_project(body={"name": project, "org_name": state.org_name}) + project_id = project_response["id"] runtime_context = { "runtime": "python", diff --git a/py/src/braintrust/test_framework2.py b/py/src/braintrust/test_framework2.py index 6ae930ba..c49b6d79 100644 --- a/py/src/braintrust/test_framework2.py +++ b/py/src/braintrust/test_framework2.py @@ -1,7 +1,7 @@ """Tests for framework2 module, specifically metadata and tags support.""" import importlib.util -from unittest.mock import MagicMock +from unittest.mock import MagicMock, patch import pytest @@ -13,6 +13,23 @@ HAS_PYDANTIC = importlib.util.find_spec("pydantic") is not None +def test_project_id_cache_uses_generated_project_registration(): + mock_state = MagicMock() + mock_state.org_name = "test-org" + mock_state.api_client.return_value.projects.post_project.return_value = { + "id": "generated-project-id", + "name": "test-project", + } + with patch("braintrust.logger._state", mock_state): + project_id = ProjectIdCache().get_by_name("test-project") + + assert project_id == "generated-project-id" + mock_state.api_client.return_value.projects.post_project.assert_called_once_with( + body={"name": "test-project", "org_name": "test-org"} + ) + mock_state.app_conn.assert_not_called() + + class TestCodeFunctionMetadata: """Tests for CodeFunction metadata support.""" diff --git a/py/src/braintrust/test_helpers.py b/py/src/braintrust/test_helpers.py index 81e99205..585454bc 100644 --- a/py/src/braintrust/test_helpers.py +++ b/py/src/braintrust/test_helpers.py @@ -104,7 +104,7 @@ def init_test_logger(project_name: str): l._lazy_metadata = lazy_metadata # Skip actual login by setting fake metadata directly # Replace the global _compute_logger_metadata function with a resolved LazyValue - def fake_compute_logger_metadata(project_name=None, project_id=None): + def fake_compute_logger_metadata(project_name=None, project_id=None, state=None): if project_id: project_metadata = ObjectMetadata(id=project_id, name=project_name, full_info=dict()) else: diff --git a/py/src/braintrust/test_logger.py b/py/src/braintrust/test_logger.py index 279742e1..4d915afa 100644 --- a/py/src/braintrust/test_logger.py +++ b/py/src/braintrust/test_logger.py @@ -3531,6 +3531,33 @@ def test_extract_attachments_with_json_attachment(self): self.assertEqual(event["input"]["data"], json_attachment.reference) +class TestProjectGeneratedAPI(TestCase): + def test_lazy_project_uses_generated_registration_and_lookup(self): + mock_state = MagicMock() + mock_state.org_name = "test-org" + api_client = mock_state.api_client.return_value + api_client.projects.post_project.return_value = { + "id": "created-project-id", + "name": "created-project", + } + api_client.projects.get_project_id.return_value = { + "id": "looked-up-project-id", + "name": "looked-up-project", + } + + with patch.object(logger, "_state", mock_state): + created = logger.Project(name="created-project") + looked_up = logger.Project(id="looked-up-project-id") + self.assertEqual(created.id, "created-project-id") + self.assertEqual(looked_up.name, "looked-up-project") + + api_client.projects.post_project.assert_called_once_with( + body={"name": "created-project", "org_name": "test-org"} + ) + api_client.projects.get_project_id.assert_called_once_with("looked-up-project-id") + mock_state.app_conn.assert_not_called() + + class TestDatasetGeneratedAPI(TestCase): def test_init_dataset_uses_generated_project_and_dataset_resources(self): mock_state = MagicMock() diff --git a/py/src/braintrust/test_sandbox.py b/py/src/braintrust/test_sandbox.py index 3e5694b3..01cd2a91 100644 --- a/py/src/braintrust/test_sandbox.py +++ b/py/src/braintrust/test_sandbox.py @@ -1,6 +1,7 @@ """Tests for sandbox registration.""" import os +from unittest.mock import MagicMock import pytest @@ -10,11 +11,13 @@ SNAPSHOT_REF = "im-icRxmsk1Sz9XPP2f8OblVU" PROJECT = "My Project" +PROJECT_ID = "6b770640-a26a-4680-9d69-537262023137" ENTRYPOINTS = ["./local/js/optimization/evals/btql-generation/btql-queries.eval.ts"] -# Paths that register_sandbox actually calls — used to filter out -# unrelated background logger traffic that VCR would otherwise capture. -_SANDBOX_PATHS = {"/api/project/register", "/function/sandbox-list", "/insert-functions"} +# Project registration is covered by the Projects API VCR test. Keep these +# cassettes focused on the specialized sandbox endpoints, which require +# organization-specific Modal credentials to record. +_SANDBOX_PATHS = {"/function/sandbox-list", "/insert-functions"} @pytest.fixture(scope="module") @@ -38,7 +41,9 @@ def logged_in_state(): state.logged_in = True token = os.environ.get("BRAINTRUST_API_KEY", "sk-dummy-for-vcr-replay") - state.app_conn().set_token(token) + api_client = MagicMock() + api_client.projects.post_project.return_value = {"id": PROJECT_ID, "name": PROJECT} + state.api_client = MagicMock(return_value=api_client) state.api_conn().set_token(token) state.proxy_conn().set_token(token) return state @@ -60,6 +65,9 @@ def test_register_sandbox_single_evaluator(logged_in_state): assert result.functions[0].eval_name assert result.functions[0].id assert result.functions[0].slug + logged_in_state.api_client.return_value.projects.post_project.assert_called_once_with( + body={"name": PROJECT, "org_name": logged_in_state.org_name} + ) @pytest.mark.vcr