From e1c1e5e44665c2aa0741188ee944e1033e45f90d Mon Sep 17 00:00:00 2001 From: lforst <8118419+lforst@users.noreply.github.com> Date: Fri, 28 Aug 2026 14:44:41 +0000 Subject: [PATCH] ref: Remove deprecated packages --- README.md | 14 +- e2e/README.md | 2 +- e2e/config/pr-comment-scenarios.json | 15 - e2e/package.json | 1 - ...angchain-js-traces-v1-latest.cassette.json | 673 -------- .../wrap-langchain-js-traces-v1.cassette.json | 673 -------- ...ngchain-js-traces-v1-latest.span-tree.json | 1511 ----------------- ...angchain-js-traces-v1-latest.span-tree.txt | 1450 ---------------- ...wrap-langchain-js-traces-v1.span-tree.json | 1511 ----------------- .../wrap-langchain-js-traces-v1.span-tree.txt | 1450 ---------------- .../wrap-langchain-js-traces/assertions.ts | 113 -- .../wrap-langchain-js-traces/package.json | 31 - .../wrap-langchain-js-traces/pnpm-lock.yaml | 296 ---- .../wrap-langchain-js-traces/scenario.test.ts | 76 - .../wrap-langchain-js-traces/scenario.ts | 162 -- integrations/langchain-js/CHANGELOG.md | 31 - integrations/langchain-js/README.md | 59 - integrations/langchain-js/package.json | 64 - .../src/BraintrustCallbackHandler.fixtures.ts | 234 --- .../src/BraintrustCallbackHandler.test.ts | 1239 -------------- .../src/BraintrustCallbackHandler.ts | 259 --- integrations/langchain-js/src/index.ts | 2 - .../langchain-js/src/setGlobalHandler.test.ts | 138 -- .../langchain-js/src/setGlobalHandler.ts | 24 - integrations/langchain-js/src/test/setup.ts | 51 - integrations/langchain-js/src/test/types.ts | 32 - integrations/langchain-js/src/test/utils.ts | 72 - integrations/langchain-js/tsconfig.json | 15 - integrations/langchain-js/tsup.config.ts | 11 - integrations/langchain-js/turbo.json | 8 - integrations/langchain-js/vitest.config.ts | 9 - integrations/openai-agents-js/CHANGELOG.md | 20 - integrations/openai-agents-js/README.md | 74 - integrations/openai-agents-js/package.json | 50 - integrations/openai-agents-js/src/index.ts | 673 -------- .../src/openai-agents-integration.test.ts | 1262 -------------- .../src/openai-agents-trace-processor.test.ts | 288 ---- .../src/openai-agents-types.ts | 150 -- integrations/openai-agents-js/src/types.ts | 120 -- integrations/openai-agents-js/tsconfig.json | 15 - integrations/openai-agents-js/tsup.config.ts | 10 - integrations/openai-agents-js/turbo.json | 12 - .../openai-agents-js/vitest.config.ts | 9 - integrations/vercel-ai-sdk/CHANGELOG.md | 35 - integrations/vercel-ai-sdk/README.md | 24 - integrations/vercel-ai-sdk/package.json | 48 - .../vercel-ai-sdk/src/adapter.test.ts | 80 - integrations/vercel-ai-sdk/src/adapter.ts | 131 -- integrations/vercel-ai-sdk/src/index.ts | 6 - integrations/vercel-ai-sdk/tsconfig.json | 15 - integrations/vercel-ai-sdk/tsup.config.ts | 10 - integrations/vercel-ai-sdk/turbo.json | 8 - integrations/vercel-ai-sdk/vitest.config.ts | 8 - pnpm-lock.yaml | 719 +------- scripts/release/_shared.mjs | 6 - 55 files changed, 57 insertions(+), 13942 deletions(-) delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/__cassettes__/wrap-langchain-js-traces-v1-latest.cassette.json delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/__cassettes__/wrap-langchain-js-traces-v1.cassette.json delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1-latest.span-tree.json delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1-latest.span-tree.txt delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1.span-tree.json delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1.span-tree.txt delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/assertions.ts delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/package.json delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/pnpm-lock.yaml delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/scenario.test.ts delete mode 100644 e2e/scenarios/wrap-langchain-js-traces/scenario.ts delete mode 100644 integrations/langchain-js/CHANGELOG.md delete mode 100644 integrations/langchain-js/README.md delete mode 100644 integrations/langchain-js/package.json delete mode 100644 integrations/langchain-js/src/BraintrustCallbackHandler.fixtures.ts delete mode 100644 integrations/langchain-js/src/BraintrustCallbackHandler.test.ts delete mode 100644 integrations/langchain-js/src/BraintrustCallbackHandler.ts delete mode 100644 integrations/langchain-js/src/index.ts delete mode 100644 integrations/langchain-js/src/setGlobalHandler.test.ts delete mode 100644 integrations/langchain-js/src/setGlobalHandler.ts delete mode 100644 integrations/langchain-js/src/test/setup.ts delete mode 100644 integrations/langchain-js/src/test/types.ts delete mode 100644 integrations/langchain-js/src/test/utils.ts delete mode 100644 integrations/langchain-js/tsconfig.json delete mode 100644 integrations/langchain-js/tsup.config.ts delete mode 100644 integrations/langchain-js/turbo.json delete mode 100644 integrations/langchain-js/vitest.config.ts delete mode 100644 integrations/openai-agents-js/CHANGELOG.md delete mode 100644 integrations/openai-agents-js/README.md delete mode 100644 integrations/openai-agents-js/package.json delete mode 100644 integrations/openai-agents-js/src/index.ts delete mode 100644 integrations/openai-agents-js/src/openai-agents-integration.test.ts delete mode 100644 integrations/openai-agents-js/src/openai-agents-trace-processor.test.ts delete mode 100644 integrations/openai-agents-js/src/openai-agents-types.ts delete mode 100644 integrations/openai-agents-js/src/types.ts delete mode 100644 integrations/openai-agents-js/tsconfig.json delete mode 100644 integrations/openai-agents-js/tsup.config.ts delete mode 100644 integrations/openai-agents-js/turbo.json delete mode 100644 integrations/openai-agents-js/vitest.config.ts delete mode 100644 integrations/vercel-ai-sdk/CHANGELOG.md delete mode 100644 integrations/vercel-ai-sdk/README.md delete mode 100644 integrations/vercel-ai-sdk/package.json delete mode 100644 integrations/vercel-ai-sdk/src/adapter.test.ts delete mode 100644 integrations/vercel-ai-sdk/src/adapter.ts delete mode 100644 integrations/vercel-ai-sdk/src/index.ts delete mode 100644 integrations/vercel-ai-sdk/tsconfig.json delete mode 100644 integrations/vercel-ai-sdk/tsup.config.ts delete mode 100644 integrations/vercel-ai-sdk/turbo.json delete mode 100644 integrations/vercel-ai-sdk/vitest.config.ts diff --git a/README.md b/README.md index 0f7afe649..052ffc529 100644 --- a/README.md +++ b/README.md @@ -42,14 +42,12 @@ BRAINTRUST_API_KEY= npx braintrust eval tutorial.eval.ts ## Packages -| Package | Purpose | npm | Docs | -| --------------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- | -| `braintrust` | Core JavaScript/TypeScript SDK for logging, tracing, evals, and CLI. | [![npm: braintrust](https://img.shields.io/npm/v/braintrust.svg)](https://www.npmjs.com/package/braintrust) | [js/README.md](js/README.md) | -| `@braintrust/browser` | Browser-focused SDK integration with AsyncLocalStorage polyfill support. | [![npm: @braintrust/browser](https://img.shields.io/npm/v/%40braintrust%2Fbrowser.svg)](https://www.npmjs.com/package/@braintrust/browser) | [integrations/browser-js/README.md](integrations/browser-js/README.md) | -| `@braintrust/langchain-js` | Deprecated; use the LangChain integration in `braintrust` instead. | [![npm: @braintrust/langchain-js](https://img.shields.io/npm/v/%40braintrust%2Flangchain-js.svg)](https://www.npmjs.com/package/@braintrust/langchain-js) | [integrations/langchain-js/README.md](integrations/langchain-js/README.md) | -| `@braintrust/openai-agents` | Deprecated; use the OpenAI Agents integration in `braintrust` instead. | [![npm: @braintrust/openai-agents](https://img.shields.io/npm/v/%40braintrust%2Fopenai-agents.svg)](https://www.npmjs.com/package/@braintrust/openai-agents) | [integrations/openai-agents-js/README.md](integrations/openai-agents-js/README.md) | -| `@braintrust/otel` | OpenTelemetry span processor and compatibility helpers for Braintrust tracing. | [![npm: @braintrust/otel](https://img.shields.io/npm/v/%40braintrust%2Fotel.svg)](https://www.npmjs.com/package/@braintrust/otel) | [integrations/otel-js/README.md](integrations/otel-js/README.md) | -| `@braintrust/temporal` | Temporal client/worker plugin and workflow interceptors for Braintrust tracing. | [![npm: @braintrust/temporal](https://img.shields.io/npm/v/%40braintrust%2Ftemporal.svg)](https://www.npmjs.com/package/@braintrust/temporal) | [integrations/temporal-js/README.md](integrations/temporal-js/README.md) | +| Package | Purpose | npm | Docs | +| ---------------------- | ------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ | +| `braintrust` | Core JavaScript/TypeScript SDK for logging, tracing, evals, and CLI. | [![npm: braintrust](https://img.shields.io/npm/v/braintrust.svg)](https://www.npmjs.com/package/braintrust) | [js/README.md](js/README.md) | +| `@braintrust/browser` | Browser-focused SDK integration with AsyncLocalStorage polyfill support. | [![npm: @braintrust/browser](https://img.shields.io/npm/v/%40braintrust%2Fbrowser.svg)](https://www.npmjs.com/package/@braintrust/browser) | [integrations/browser-js/README.md](integrations/browser-js/README.md) | +| `@braintrust/otel` | OpenTelemetry span processor and compatibility helpers for Braintrust tracing. | [![npm: @braintrust/otel](https://img.shields.io/npm/v/%40braintrust%2Fotel.svg)](https://www.npmjs.com/package/@braintrust/otel) | [integrations/otel-js/README.md](integrations/otel-js/README.md) | +| `@braintrust/temporal` | Temporal client/worker plugin and workflow interceptors for Braintrust tracing. | [![npm: @braintrust/temporal](https://img.shields.io/npm/v/%40braintrust%2Ftemporal.svg)](https://www.npmjs.com/package/@braintrust/temporal) | [integrations/temporal-js/README.md](integrations/temporal-js/README.md) | ## CLI `bt` diff --git a/e2e/README.md b/e2e/README.md index d9ac582ea..e4a6510e6 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -188,7 +188,7 @@ After any successful record run, stale cassette variants are cleaned only inside These scenarios have cassette wiring in place and will use cassettes once they're recorded: -`anthropic-bedrock-instrumentation`, `anthropic-instrumentation`, `openai-instrumentation`, `openai-codex-instrumentation`, `ai-sdk-instrumentation`, `ai-sdk-otel-export`, `claude-agent-sdk-instrumentation`, `cohere-instrumentation`, `cursor-sdk-instrumentation`, `github-copilot-instrumentation`, `google-adk-instrumentation`, `google-genai-instrumentation`, `groq-instrumentation`, `huggingface-instrumentation`, `mistral-instrumentation`, `ollama-instrumentation`, `openrouter-agent-instrumentation`, `openrouter-instrumentation`, `wrap-langchain-js-traces`. +`anthropic-bedrock-instrumentation`, `anthropic-instrumentation`, `openai-instrumentation`, `openai-codex-instrumentation`, `ai-sdk-instrumentation`, `ai-sdk-otel-export`, `claude-agent-sdk-instrumentation`, `cohere-instrumentation`, `cursor-sdk-instrumentation`, `github-copilot-instrumentation`, `google-adk-instrumentation`, `google-genai-instrumentation`, `groq-instrumentation`, `huggingface-instrumentation`, `mistral-instrumentation`, `ollama-instrumentation`, `openrouter-agent-instrumentation`, `openrouter-instrumentation`. ### Cassette format diff --git a/e2e/config/pr-comment-scenarios.json b/e2e/config/pr-comment-scenarios.json index 3b2ac8370..9373b9077 100644 --- a/e2e/config/pr-comment-scenarios.json +++ b/e2e/config/pr-comment-scenarios.json @@ -761,20 +761,5 @@ "variantKey": "vitest-evals-reporter" } ] - }, - { - "scenarioDirName": "wrap-langchain-js-traces", - "label": "LangChain JS Wrapper Traces", - "metadataScenario": "wrap-langchain-js-traces", - "variants": [ - { - "variantKey": "wrap-langchain-js-traces-v1", - "label": "v1 pinned" - }, - { - "variantKey": "wrap-langchain-js-traces-v1-latest", - "label": "v1 latest" - } - ] } ] diff --git a/e2e/package.json b/e2e/package.json index 90739572f..fad5c0a8f 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -9,7 +9,6 @@ }, "devDependencies": { "@braintrust/deepseek-harness": "workspace:^", - "@braintrust/langchain-js": "workspace:^", "@braintrust/otel": "workspace:^", "@braintrust/seinfeld": "workspace:^", "@opentelemetry/api": ">=1.9.0", diff --git a/e2e/scenarios/wrap-langchain-js-traces/__cassettes__/wrap-langchain-js-traces-v1-latest.cassette.json b/e2e/scenarios/wrap-langchain-js-traces/__cassettes__/wrap-langchain-js-traces-v1-latest.cassette.json deleted file mode 100644 index bce5a3c83..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/__cassettes__/wrap-langchain-js-traces-v1-latest.cassette.json +++ /dev/null @@ -1,673 +0,0 @@ -{ - "entries": [ - { - "callIndex": 0, - "id": "86e93f0671afd2fb", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:32.278Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_completion_tokens": 512, - "messages": [ - { - "content": "Reply with exactly OK.", - "role": "user" - } - ], - "model": "gpt-5-nano", - "stream": false - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "stop", - "index": 0, - "message": { - "annotations": [], - "content": "OK", - "refusal": null, - "role": "assistant" - } - } - ], - "created": 1787574030, - "id": "chatcmpl-EGNx8tOIBqW4PL9inY296btKeUCr1", - "model": "gpt-5-nano-2025-08-07", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": null, - "usage": { - "completion_tokens": 266, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 256, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 11, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 277 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024fb8088a2fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:32 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "2073", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "180000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "179999994", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_4371b279cf104e0697b031338add78f2" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 1, - "id": "a613a13df4910706", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:32.699Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 32, - "messages": [ - { - "content": "Reply with the single word PARIS and nothing else.", - "role": "user" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0 - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "stop", - "index": 0, - "logprobs": null, - "message": { - "annotations": [], - "content": "PARIS", - "refusal": null, - "role": "assistant" - } - } - ], - "created": 1787574032, - "id": "chatcmpl-EGNxAPgSJsXEVlK5YEAjNpYLSUFML", - "model": "gpt-4o-mini-2024-07-18", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": "fp_fabbcdf5d9", - "usage": { - "completion_tokens": 2, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 18, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 20 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024fc59a5b2fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:32 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "338", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999985", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_6de919b320864952a77010c3682478ac" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 2, - "id": "394c0994869e6e1f", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:33.104Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 32, - "messages": [ - { - "content": "Count from 1 to 3 and include the words one two three.", - "role": "user" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": true, - "stream_options": { - "include_usage": true - }, - "temperature": 0 - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "chunks": [ - "data: {\"id\":\"chatcmpl-EGNxAtVgtjTY51DEVaYfresrdBMst\",\"object\":\"chat.completion.chunk\",\"created\":1787574032,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"wxWQ0eCDL\"}", - "data: {\"id\":\"chatcmpl-EGNxAtVgtjTY51DEVaYfresrdBMst\",\"object\":\"chat.completion.chunk\",\"created\":1787574032,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"One\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"uje9Teyd\"}", - "data: {\"id\":\"chatcmpl-EGNxAtVgtjTY51DEVaYfresrdBMst\",\"object\":\"chat.completion.chunk\",\"created\":1787574032,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\",\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"B0K64QetnP\"}", - "data: {\"id\":\"chatcmpl-EGNxAtVgtjTY51DEVaYfresrdBMst\",\"object\":\"chat.completion.chunk\",\"created\":1787574032,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" two\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"S4dypzF\"}", - "data: {\"id\":\"chatcmpl-EGNxAtVgtjTY51DEVaYfresrdBMst\",\"object\":\"chat.completion.chunk\",\"created\":1787574032,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\",\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"XdqizbjE0P\"}", - "data: {\"id\":\"chatcmpl-EGNxAtVgtjTY51DEVaYfresrdBMst\",\"object\":\"chat.completion.chunk\",\"created\":1787574032,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" three\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"Bq0Q1\"}", - "data: {\"id\":\"chatcmpl-EGNxAtVgtjTY51DEVaYfresrdBMst\",\"object\":\"chat.completion.chunk\",\"created\":1787574032,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"BoOsWxwVOZ\"}", - "data: {\"id\":\"chatcmpl-EGNxAtVgtjTY51DEVaYfresrdBMst\",\"object\":\"chat.completion.chunk\",\"created\":1787574032,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"OgqTv\"}", - "data: {\"id\":\"chatcmpl-EGNxAtVgtjTY51DEVaYfresrdBMst\",\"object\":\"chat.completion.chunk\",\"created\":1787574032,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[],\"usage\":{\"prompt_tokens\":22,\"completion_tokens\":6,\"total_tokens\":28,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"3mFwIC92nf0\"}", - "data: [DONE]" - ], - "kind": "sse" - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024fc82b622fd0-IAD", - "connection": "keep-alive", - "content-type": "text/event-stream; charset=utf-8", - "date": "Mon, 24 Aug 2026 12:20:32 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "244", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999982", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_0be6fc8fedbe4d70805e3b1ab80f25cf" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 3, - "id": "7f1e5cd685579c33", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:33.721Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 128, - "messages": [ - { - "content": "Use the get_weather tool for Paris, France. Do not answer from memory.", - "role": "user" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Get the current weather in a given location", - "name": "get_weather", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "location": { - "description": "The city and state or city and country", - "type": "string" - } - }, - "required": ["location"], - "type": "object" - } - }, - "type": "function" - } - ] - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "tool_calls", - "index": 0, - "logprobs": null, - "message": { - "annotations": [], - "content": null, - "refusal": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"location\":\"Paris, France\"}", - "name": "get_weather" - }, - "id": "call_qjYJazVqODvAan4YaPgZZ8ra", - "type": "function" - } - ] - } - } - ], - "created": 1787574033, - "id": "chatcmpl-EGNxB4gTTflGWk1BHpKwKrYsdlH75", - "model": "gpt-4o-mini-2024-07-18", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": "fp_02ae59e84c", - "usage": { - "completion_tokens": 16, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 72, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 88 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024fcabbd32fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:33 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "546", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999980", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_ded6c40040e749c49af0e74794677f16" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 4, - "id": "92b304bbb4ce5170", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:34.417Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 128, - "messages": [ - { - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "role": "user" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": ["add", "subtract", "multiply", "divide"], - "type": "string" - } - }, - "required": ["operation", "a", "b"], - "type": "object" - } - }, - "type": "function" - } - ] - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "tool_calls", - "index": 0, - "logprobs": null, - "message": { - "annotations": [], - "content": null, - "refusal": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"operation\":\"multiply\",\"a\":127,\"b\":49}", - "name": "calculate" - }, - "id": "call_BMkQ0blzGQeVPEC7pIElPdRs", - "type": "function" - } - ] - } - } - ], - "created": 1787574033, - "id": "chatcmpl-EGNxBqL6zQDAnMT6RdDBaUwkEjbWf", - "model": "gpt-4o-mini-2024-07-18", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": "fp_0950b7f384", - "usage": { - "completion_tokens": 21, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 76, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 97 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024fce88f42fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:34 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "617", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999985", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_d320b09ebbc6405cab6edb5dc6a5e0c1" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 5, - "id": "6fa54a31e7345249", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:34.924Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 128, - "messages": [ - { - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "role": "user" - }, - { - "content": "", - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"operation\":\"multiply\",\"a\":127,\"b\":49}", - "name": "calculate" - }, - "id": "call_BMkQ0blzGQeVPEC7pIElPdRs", - "type": "function" - } - ] - }, - { - "content": "6223", - "role": "tool", - "tool_call_id": "call_BMkQ0blzGQeVPEC7pIElPdRs" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": ["add", "subtract", "multiply", "divide"], - "type": "string" - } - }, - "required": ["operation", "a", "b"], - "type": "object" - } - }, - "type": "function" - } - ] - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "stop", - "index": 0, - "logprobs": null, - "message": { - "annotations": [], - "content": "127 multiplied by 49 is 6223.", - "refusal": null, - "role": "assistant" - } - } - ], - "created": 1787574034, - "id": "chatcmpl-EGNxCT0hwMHK5XDv3ViqjOdQ3sShs", - "model": "gpt-4o-mini-2024-07-18", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": "fp_0950b7f384", - "usage": { - "completion_tokens": 11, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 106, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 117 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024fd2e94a2fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:34 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "444", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999980", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_149ef64cc6b543eaa8b7c0f602c66bea" - }, - "status": 200, - "statusText": "OK" - } - } - ], - "meta": { - "createdAt": "2026-07-06T14:01:28.480Z" - } -} diff --git a/e2e/scenarios/wrap-langchain-js-traces/__cassettes__/wrap-langchain-js-traces-v1.cassette.json b/e2e/scenarios/wrap-langchain-js-traces/__cassettes__/wrap-langchain-js-traces-v1.cassette.json deleted file mode 100644 index 604a9bcdd..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/__cassettes__/wrap-langchain-js-traces-v1.cassette.json +++ /dev/null @@ -1,673 +0,0 @@ -{ - "entries": [ - { - "callIndex": 0, - "id": "86e93f0671afd2fb", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:22.627Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_completion_tokens": 512, - "messages": [ - { - "content": "Reply with exactly OK.", - "role": "user" - } - ], - "model": "gpt-5-nano", - "stream": false - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "stop", - "index": 0, - "message": { - "annotations": [], - "content": "OK", - "refusal": null, - "role": "assistant" - } - } - ], - "created": 1787574021, - "id": "chatcmpl-EGNwzUCt2YhtMXdmxJ7ea1qvFvjhj", - "model": "gpt-5-nano-2025-08-07", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": null, - "usage": { - "completion_tokens": 138, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 128, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 11, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 149 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024f76c8462fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:22 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "1516", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "180000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "179999994", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_35266a070f524ced863bd688a979fb9c" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 1, - "id": "a613a13df4910706", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:23.323Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 32, - "messages": [ - { - "content": "Reply with the single word PARIS and nothing else.", - "role": "user" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0 - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "stop", - "index": 0, - "logprobs": null, - "message": { - "annotations": [], - "content": "PARIS", - "refusal": null, - "role": "assistant" - } - } - ], - "created": 1787574023, - "id": "chatcmpl-EGNx181W8ybisMsX1olUNegho55Z4", - "model": "gpt-4o-mini-2024-07-18", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": "fp_fabbcdf5d9", - "usage": { - "completion_tokens": 2, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 18, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 20 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024f894d132fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:23 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "273", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999985", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_06fe5d162d7f463bb59e50d729b4d20c" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 2, - "id": "394c0994869e6e1f", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:24.024Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 32, - "messages": [ - { - "content": "Count from 1 to 3 and include the words one two three.", - "role": "user" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": true, - "stream_options": { - "include_usage": true - }, - "temperature": 0 - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "chunks": [ - "data: {\"id\":\"chatcmpl-EGNx1I8x3YZKvOGhuC0S5biZ26Gwl\",\"object\":\"chat.completion.chunk\",\"created\":1787574023,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"role\":\"assistant\",\"content\":\"\",\"refusal\":null},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"Oj4Hh3j31\"}", - "data: {\"id\":\"chatcmpl-EGNx1I8x3YZKvOGhuC0S5biZ26Gwl\",\"object\":\"chat.completion.chunk\",\"created\":1787574023,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\"One\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"Il4NOQeI\"}", - "data: {\"id\":\"chatcmpl-EGNx1I8x3YZKvOGhuC0S5biZ26Gwl\",\"object\":\"chat.completion.chunk\",\"created\":1787574023,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\",\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"rVAPk2Cs7X\"}", - "data: {\"id\":\"chatcmpl-EGNx1I8x3YZKvOGhuC0S5biZ26Gwl\",\"object\":\"chat.completion.chunk\",\"created\":1787574023,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" two\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"sFT4iNt\"}", - "data: {\"id\":\"chatcmpl-EGNx1I8x3YZKvOGhuC0S5biZ26Gwl\",\"object\":\"chat.completion.chunk\",\"created\":1787574023,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\",\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"tSa35AR216\"}", - "data: {\"id\":\"chatcmpl-EGNx1I8x3YZKvOGhuC0S5biZ26Gwl\",\"object\":\"chat.completion.chunk\",\"created\":1787574023,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\" three\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"oXtaZ\"}", - "data: {\"id\":\"chatcmpl-EGNx1I8x3YZKvOGhuC0S5biZ26Gwl\",\"object\":\"chat.completion.chunk\",\"created\":1787574023,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{\"content\":\".\"},\"logprobs\":null,\"finish_reason\":null}],\"usage\":null,\"obfuscation\":\"pfwlwCc1UG\"}", - "data: {\"id\":\"chatcmpl-EGNx1I8x3YZKvOGhuC0S5biZ26Gwl\",\"object\":\"chat.completion.chunk\",\"created\":1787574023,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[{\"index\":0,\"delta\":{},\"logprobs\":null,\"finish_reason\":\"stop\"}],\"usage\":null,\"obfuscation\":\"0pszW\"}", - "data: {\"id\":\"chatcmpl-EGNx1I8x3YZKvOGhuC0S5biZ26Gwl\",\"object\":\"chat.completion.chunk\",\"created\":1787574023,\"model\":\"gpt-4o-mini-2024-07-18\",\"service_tier\":\"default\",\"system_fingerprint\":\"fp_0950b7f384\",\"choices\":[],\"usage\":{\"prompt_tokens\":22,\"completion_tokens\":6,\"total_tokens\":28,\"prompt_tokens_details\":{\"cached_tokens\":0,\"audio_tokens\":0},\"completion_tokens_details\":{\"reasoning_tokens\":0,\"audio_tokens\":0,\"accepted_prediction_tokens\":0,\"rejected_prediction_tokens\":0}},\"obfuscation\":\"kalzk5DmKv4\"}", - "data: [DONE]" - ], - "kind": "sse" - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024f8d9d6b2fd0-IAD", - "connection": "keep-alive", - "content-type": "text/event-stream; charset=utf-8", - "date": "Mon, 24 Aug 2026 12:20:23 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "396", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999985", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_a0055ad7660e45bb855ae0977d3a9b46" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 3, - "id": "7f1e5cd685579c33", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:24.773Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 128, - "messages": [ - { - "content": "Use the get_weather tool for Paris, France. Do not answer from memory.", - "role": "user" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Get the current weather in a given location", - "name": "get_weather", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "location": { - "description": "The city and state or city and country", - "type": "string" - } - }, - "required": ["location"], - "type": "object" - } - }, - "type": "function" - } - ] - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "tool_calls", - "index": 0, - "logprobs": null, - "message": { - "annotations": [], - "content": null, - "refusal": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"location\":\"Paris, France\"}", - "name": "get_weather" - }, - "id": "call_vFUhlOlK88FODFbm0RCSfovD", - "type": "function" - } - ] - } - } - ], - "created": 1787574024, - "id": "chatcmpl-EGNx23GX8lOwzBQnZTCKTXwLeUyRG", - "model": "gpt-4o-mini-2024-07-18", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": "fp_02ae59e84c", - "usage": { - "completion_tokens": 16, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 72, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 88 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024f91fd8b2fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:24 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "649", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999977", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_27d9929b8cfa4c40b969771b87ffd0bf" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 4, - "id": "92b304bbb4ce5170", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:27.515Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 128, - "messages": [ - { - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "role": "user" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": ["add", "subtract", "multiply", "divide"], - "type": "string" - } - }, - "required": ["operation", "a", "b"], - "type": "object" - } - }, - "type": "function" - } - ] - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "tool_calls", - "index": 0, - "logprobs": null, - "message": { - "annotations": [], - "content": null, - "refusal": null, - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"operation\":\"multiply\",\"a\":127,\"b\":49}", - "name": "calculate" - }, - "id": "call_dXQI5d9UAYuRygDqNMunj2W2", - "type": "function" - } - ] - } - } - ], - "created": 1787574027, - "id": "chatcmpl-EGNx52wzdb9GL8hdL3YZCMSLvJkQ4", - "model": "gpt-4o-mini-2024-07-18", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": "fp_0950b7f384", - "usage": { - "completion_tokens": 21, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 76, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 97 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024f969f342fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:27 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "729", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999985", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_86b70fed7a674d028ec1293c0e2cd0ec" - }, - "status": 200, - "statusText": "OK" - } - }, - { - "callIndex": 5, - "id": "a57d0dfe4d90c4bd", - "matchKey": "POST api.openai.com/v1/chat/completions", - "recordedAt": "2026-08-24T12:20:28.216Z", - "request": { - "body": { - "kind": "json", - "value": { - "max_tokens": 128, - "messages": [ - { - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "role": "user" - }, - { - "content": "", - "role": "assistant", - "tool_calls": [ - { - "function": { - "arguments": "{\"operation\":\"multiply\",\"a\":127,\"b\":49}", - "name": "calculate" - }, - "id": "call_dXQI5d9UAYuRygDqNMunj2W2", - "type": "function" - } - ] - }, - { - "content": "6223", - "role": "tool", - "tool_call_id": "call_dXQI5d9UAYuRygDqNMunj2W2" - } - ], - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": ["add", "subtract", "multiply", "divide"], - "type": "string" - } - }, - "required": ["operation", "a", "b"], - "type": "object" - } - }, - "type": "function" - } - ] - } - }, - "headers": {}, - "method": "POST", - "url": "https://api.openai.com/v1/chat/completions" - }, - "response": { - "body": { - "kind": "json", - "value": { - "choices": [ - { - "finish_reason": "stop", - "index": 0, - "logprobs": null, - "message": { - "annotations": [], - "content": "127 multiplied by 49 is 6223.", - "refusal": null, - "role": "assistant" - } - } - ], - "created": 1787574027, - "id": "chatcmpl-EGNx5rDFm8HaHv7GCMw3OzQyu24L9", - "model": "gpt-4o-mini-2024-07-18", - "object": "chat.completion", - "service_tier": "default", - "system_fingerprint": "fp_0950b7f384", - "usage": { - "completion_tokens": 11, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 106, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 117 - } - } - }, - "headers": { - "access-control-expose-headers": "X-Request-ID, CF-Ray, CF-Ray", - "alt-svc": "h3=\":443\"; ma=86400", - "cf-cache-status": "DYNAMIC", - "cf-ray": "a3024fa7be702fd0-IAD", - "connection": "keep-alive", - "content-encoding": "gzip", - "content-type": "application/json", - "date": "Mon, 24 Aug 2026 12:20:28 GMT", - "openai-organization": "braintrust-data", - "openai-processing-ms": "592", - "openai-project": "proj_vsCSXafhhByzWOThMrJcZiw9", - "openai-version": "2020-10-01", - "server": "cloudflare", - "set-cookie": "[REDACTED]", - "strict-transport-security": "max-age=31536000; includeSubDomains; preload", - "transfer-encoding": "chunked", - "x-content-type-options": "nosniff", - "x-openai-proxy-wasm": "v0.1", - "x-ratelimit-limit-requests": "30000", - "x-ratelimit-limit-tokens": "150000000", - "x-ratelimit-remaining-requests": "29999", - "x-ratelimit-remaining-tokens": "149999980", - "x-ratelimit-reset-requests": "2ms", - "x-ratelimit-reset-tokens": "0s", - "x-request-id": "req_80ec4ce95cf14b989fddb3c82140dab8" - }, - "status": 200, - "statusText": "OK" - } - } - ], - "meta": { - "createdAt": "2026-07-06T14:01:28.206Z" - } -} diff --git a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1-latest.span-tree.json b/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1-latest.span-tree.json deleted file mode 100644 index 9d47ae91c..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1-latest.span-tree.json +++ /dev/null @@ -1,1511 +0,0 @@ -{ - "span_tree": [ - { - "name": "langchain-wrapper-root", - "type": "task", - "children": [ - { - "name": "langchain-invoke-operation", - "children": [ - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Reply with exactly OK.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "stop" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "OK", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-5-nano-2025-08-07", - "model_provider": "openai", - "tokenUsage": { - "completionTokens": 266, - "promptTokens": 11, - "totalTokens": 277 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 11, - "output_token_details": { - "audio": 0, - "reasoning": 256 - }, - "output_tokens": 266, - "total_tokens": 277 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "OK" - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 266, - "promptTokens": 11, - "totalTokens": 277 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_completion_tokens": 512, - "model": "gpt-5-nano", - "stream": false - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_model_name": "gpt-5-nano", - "ls_model_type": "chat", - "ls_provider": "openai", - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.5.3" - } - }, - "model": "gpt-5-nano-2025-08-07", - "options": {}, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 512, - "model": "gpt-5-nano", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - } - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 256, - "completion_tokens": 266, - "prompt_cached_tokens": 0, - "prompt_tokens": 11, - "tokens": 277 - } - } - ], - "metadata": { - "operation": "invoke", - "testRunId": "" - } - }, - { - "name": "langchain-chain-operation", - "children": [ - { - "name": "RunnableSequence", - "type": "task", - "children": [ - { - "name": "ChatPromptTemplate", - "type": "task", - "children": [], - "input": { - "word": "PARIS" - }, - "output": { - "id": [ - "langchain_core", - "prompt_values", - "ChatPromptValue" - ], - "kwargs": { - "messages": [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Reply with the single word PARIS and nothing else.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - "metadata": { - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "metadata": {}, - "name": "ChatPromptTemplate", - "parent_run_id": "", - "run_id": "", - "run_type": "prompt", - "serialized": { - "id": [ - "langchain_core", - "prompts", - "chat", - "ChatPromptTemplate" - ], - "kwargs": { - "input_variables": [ - "word" - ], - "messages": [ - { - "id": [ - "langchain_core", - "prompts", - "chat", - "HumanMessagePromptTemplate" - ], - "kwargs": { - "prompt": { - "id": [ - "langchain_core", - "prompts", - "prompt", - "PromptTemplate" - ], - "kwargs": { - "input_variables": [ - "word" - ], - "template": "Reply with the single word {word} and nothing else.", - "template_format": "f-string" - }, - "lc": 1, - "type": "constructor" - } - }, - "lc": 1, - "type": "constructor" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - "tags": [ - "seq:step:1" - ] - } - }, - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Reply with the single word PARIS and nothing else.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "stop" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "PARIS", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 2, - "promptTokens": 18, - "totalTokens": 20 - }, - "usage": { - "completion_tokens": 2, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 18, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 20 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 18, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 2, - "total_tokens": 20 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "PARIS" - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 2, - "promptTokens": 18, - "totalTokens": 20 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0 - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 32, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.5.3" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": {}, - "parent_run_id": "", - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [ - "seq:step:2" - ] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 2, - "prompt_cached_tokens": 0, - "prompt_tokens": 18, - "tokens": 20 - } - } - ], - "input": { - "word": "PARIS" - }, - "output": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "PARIS", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 2, - "promptTokens": 18, - "totalTokens": 20 - }, - "usage": { - "completion_tokens": 2, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 18, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 20 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 18, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 2, - "total_tokens": 20 - } - }, - "lc": 1, - "type": "constructor" - }, - "metadata": { - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "metadata": {}, - "run_id": "", - "serialized": { - "id": [ - "langchain_core", - "runnables", - "RunnableSequence" - ], - "kwargs": { - "first": { - "id": [ - "langchain_core", - "prompts", - "chat", - "ChatPromptTemplate" - ], - "kwargs": { - "input_variables": [ - "word" - ], - "messages": [ - { - "id": [ - "langchain_core", - "prompts", - "chat", - "HumanMessagePromptTemplate" - ], - "kwargs": { - "prompt": { - "id": [ - "langchain_core", - "prompts", - "prompt", - "PromptTemplate" - ], - "kwargs": { - "input_variables": [ - "word" - ], - "template": "Reply with the single word {word} and nothing else.", - "template_format": "f-string" - }, - "lc": 1, - "type": "constructor" - } - }, - "lc": 1, - "type": "constructor" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - "last": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - } - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - } - } - ], - "metadata": { - "operation": "chain", - "testRunId": "" - } - }, - { - "name": "langchain-stream-operation", - "children": [ - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Count from 1 to 3 and include the words one two three.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "completion": 0, - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "prompt": 0, - "service_tier": "default", - "system_fingerprint": "" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessageChunk" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "One, two, three.", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "completion": 0, - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "prompt": 0, - "service_tier": "default", - "system_fingerprint": "", - "usage": { - "completion_tokens": 6, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 22, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 28 - } - }, - "tool_call_chunks": [], - "tool_calls": [], - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 22, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 6, - "total_tokens": 28 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "One, two, three." - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 6, - "promptTokens": 22, - "totalTokens": 28 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "stream": true, - "stream_options": { - "include_usage": true - }, - "temperature": 0 - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 32, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.5.3" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": {}, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "streaming": true, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 6, - "prompt_cached_tokens": 0, - "prompt_tokens": 22, - "time_to_first_token": 0, - "tokens": 28 - } - } - ], - "metadata": { - "operation": "stream", - "testRunId": "" - } - }, - { - "name": "langchain-tool-operation", - "children": [ - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Use the get_weather tool for Paris, France. Do not answer from memory.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "tool_calls" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": { - "tool_calls": [ - { - "function": { - "arguments": "{\"location\":\"Paris, France\"}", - "name": "get_weather" - }, - "id": "", - "type": "function" - } - ] - }, - "content": "", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "tool_calls", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 16, - "promptTokens": 72, - "totalTokens": 88 - }, - "usage": { - "completion_tokens": 16, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 72, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 88 - } - }, - "tool_calls": [ - { - "args": { - "location": "Paris, France" - }, - "id": "", - "name": "get_weather", - "type": "tool_call" - } - ], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 72, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 16, - "total_tokens": 88 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "" - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 16, - "promptTokens": 72, - "totalTokens": 88 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Get the current weather in a given location", - "name": "get_weather", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "location": { - "description": "The city and state or city and country", - "type": "string" - } - }, - "required": [ - "location" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 128, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.5.3" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": { - "tools": [ - { - "function": { - "description": "Get the current weather in a given location", - "name": "get_weather", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "location": { - "description": "The city and state or city and country", - "type": "string" - } - }, - "required": [ - "location" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 16, - "prompt_cached_tokens": 0, - "prompt_tokens": 72, - "tokens": 88 - } - } - ], - "metadata": { - "operation": "tool", - "testRunId": "" - } - }, - { - "name": "langchain-tool-result-operation", - "children": [ - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "tool_calls" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": { - "tool_calls": [ - { - "function": { - "arguments": "{\"operation\":\"multiply\",\"a\":127,\"b\":49}", - "name": "calculate" - }, - "id": "", - "type": "function" - } - ] - }, - "content": "", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "tool_calls", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 21, - "promptTokens": 76, - "totalTokens": 97 - }, - "usage": { - "completion_tokens": 21, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 76, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 97 - } - }, - "tool_calls": [ - { - "args": { - "a": 127, - "b": 49, - "operation": "multiply" - }, - "id": "", - "name": "calculate", - "type": "tool_call" - } - ], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 76, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 21, - "total_tokens": 97 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "" - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 21, - "promptTokens": 76, - "totalTokens": 97 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 128, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.5.3" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": { - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 21, - "prompt_cached_tokens": 0, - "prompt_tokens": 76, - "tokens": 97 - } - }, - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - }, - { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "", - "invalid_tool_calls": [], - "response_metadata": {}, - "tool_calls": [ - { - "args": { - "a": 127, - "b": 49, - "operation": "multiply" - }, - "id": "", - "name": "calculate", - "type": "tool_call" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - { - "id": [ - "langchain_core", - "messages", - "ToolMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "6223", - "response_metadata": {}, - "tool_call_id": "call_BMkQ0blzGQeVPEC7pIElPdRs" - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "stop" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "127 multiplied by 49 is 6223.", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 11, - "promptTokens": 106, - "totalTokens": 117 - }, - "usage": { - "completion_tokens": 11, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 106, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 117 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 106, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 11, - "total_tokens": 117 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "127 multiplied by 49 is 6223." - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 11, - "promptTokens": 106, - "totalTokens": 117 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 128, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.5.3" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": { - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 11, - "prompt_cached_tokens": 0, - "prompt_tokens": 106, - "tokens": 117 - } - } - ], - "metadata": { - "operation": "tool-result", - "testRunId": "" - } - } - ], - "metadata": { - "scenario": "wrap-langchain-js-traces", - "testRunId": "" - } - } - ] -} diff --git a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1-latest.span-tree.txt b/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1-latest.span-tree.txt deleted file mode 100644 index d19c42372..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1-latest.span-tree.txt +++ /dev/null @@ -1,1450 +0,0 @@ -span_tree: -└── langchain-wrapper-root [task] - metadata: { - "scenario": "wrap-langchain-js-traces", - "testRunId": "" - } - ├── langchain-invoke-operation - │ metadata: { - │ "operation": "invoke", - │ "testRunId": "" - │ } - │ └── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "Reply with exactly OK.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "finish_reason": "stop" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "OK", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "stop", - │ "model_name": "gpt-5-nano-2025-08-07", - │ "model_provider": "openai", - │ "tokenUsage": { - │ "completionTokens": 266, - │ "promptTokens": 11, - │ "totalTokens": 277 - │ } - │ }, - │ "tool_calls": [], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 11, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 256 - │ }, - │ "output_tokens": 266, - │ "total_tokens": 277 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "OK" - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 266, - │ "promptTokens": 11, - │ "totalTokens": 277 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_completion_tokens": 512, - │ "model": "gpt-5-nano", - │ "stream": false - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_model_name": "gpt-5-nano", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.5.3" - │ } - │ }, - │ "model": "gpt-5-nano-2025-08-07", - │ "options": {}, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 512, - │ "model": "gpt-5-nano", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 256, - │ "completion_tokens": 266, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 11, - │ "tokens": 277 - │ } - ├── langchain-chain-operation - │ metadata: { - │ "operation": "chain", - │ "testRunId": "" - │ } - │ └── RunnableSequence [task] - │ input: { - │ "word": "PARIS" - │ } - │ output: { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "PARIS", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "stop", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "system_fingerprint": "", - │ "tokenUsage": { - │ "completionTokens": 2, - │ "promptTokens": 18, - │ "totalTokens": 20 - │ }, - │ "usage": { - │ "completion_tokens": 2, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 18, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 20 - │ } - │ }, - │ "tool_calls": [], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 18, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 2, - │ "total_tokens": 20 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ metadata: { - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "metadata": {}, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain_core", - │ "runnables", - │ "RunnableSequence" - │ ], - │ "kwargs": { - │ "first": { - │ "id": [ - │ "langchain_core", - │ "prompts", - │ "chat", - │ "ChatPromptTemplate" - │ ], - │ "kwargs": { - │ "input_variables": [ - │ "word" - │ ], - │ "messages": [ - │ { - │ "id": [ - │ "langchain_core", - │ "prompts", - │ "chat", - │ "HumanMessagePromptTemplate" - │ ], - │ "kwargs": { - │ "prompt": { - │ "id": [ - │ "langchain_core", - │ "prompts", - │ "prompt", - │ "PromptTemplate" - │ ], - │ "kwargs": { - │ "input_variables": [ - │ "word" - │ ], - │ "template": "Reply with the single word {word} and nothing else.", - │ "template_format": "f-string" - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "last": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ ├── ChatPromptTemplate [task] - │ │ input: { - │ │ "word": "PARIS" - │ │ } - │ │ output: { - │ │ "id": [ - │ │ "langchain_core", - │ │ "prompt_values", - │ │ "ChatPromptValue" - │ │ ], - │ │ "kwargs": { - │ │ "messages": [ - │ │ { - │ │ "id": [ - │ │ "langchain_core", - │ │ "messages", - │ │ "HumanMessage" - │ │ ], - │ │ "kwargs": { - │ │ "additional_kwargs": {}, - │ │ "content": "Reply with the single word PARIS and nothing else.", - │ │ "response_metadata": {} - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ } - │ │ ] - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ } - │ │ metadata: { - │ │ "braintrust": { - │ │ "integration_name": "langchain-js", - │ │ "sdk_language": "javascript" - │ │ }, - │ │ "metadata": {}, - │ │ "name": "ChatPromptTemplate", - │ │ "parent_run_id": "", - │ │ "run_id": "", - │ │ "run_type": "prompt", - │ │ "serialized": { - │ │ "id": [ - │ │ "langchain_core", - │ │ "prompts", - │ │ "chat", - │ │ "ChatPromptTemplate" - │ │ ], - │ │ "kwargs": { - │ │ "input_variables": [ - │ │ "word" - │ │ ], - │ │ "messages": [ - │ │ { - │ │ "id": [ - │ │ "langchain_core", - │ │ "prompts", - │ │ "chat", - │ │ "HumanMessagePromptTemplate" - │ │ ], - │ │ "kwargs": { - │ │ "prompt": { - │ │ "id": [ - │ │ "langchain_core", - │ │ "prompts", - │ │ "prompt", - │ │ "PromptTemplate" - │ │ ], - │ │ "kwargs": { - │ │ "input_variables": [ - │ │ "word" - │ │ ], - │ │ "template": "Reply with the single word {word} and nothing else.", - │ │ "template_format": "f-string" - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ } - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ } - │ │ ] - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ }, - │ │ "tags": [ - │ │ "seq:step:1" - │ │ ] - │ │ } - │ └── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "Reply with the single word PARIS and nothing else.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "finish_reason": "stop" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "PARIS", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "stop", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "system_fingerprint": "", - │ "tokenUsage": { - │ "completionTokens": 2, - │ "promptTokens": 18, - │ "totalTokens": 20 - │ }, - │ "usage": { - │ "completion_tokens": 2, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 18, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 20 - │ } - │ }, - │ "tool_calls": [], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 18, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 2, - │ "total_tokens": 20 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "PARIS" - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 2, - │ "promptTokens": 18, - │ "totalTokens": 20 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "stream": false, - │ "temperature": 0 - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_max_tokens": 32, - │ "ls_model_name": "gpt-4o-mini-2024-07-18", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "ls_temperature": 0, - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.5.3" - │ } - │ }, - │ "model": "gpt-4o-mini-2024-07-18", - │ "options": {}, - │ "parent_run_id": "", - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [ - │ "seq:step:2" - │ ] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 0, - │ "completion_tokens": 2, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 18, - │ "tokens": 20 - │ } - ├── langchain-stream-operation - │ metadata: { - │ "operation": "stream", - │ "testRunId": "" - │ } - │ └── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "Count from 1 to 3 and include the words one two three.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "completion": 0, - │ "finish_reason": "stop", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "prompt": 0, - │ "service_tier": "default", - │ "system_fingerprint": "" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessageChunk" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "One, two, three.", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "completion": 0, - │ "finish_reason": "stop", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "prompt": 0, - │ "service_tier": "default", - │ "system_fingerprint": "", - │ "usage": { - │ "completion_tokens": 6, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 22, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 28 - │ } - │ }, - │ "tool_call_chunks": [], - │ "tool_calls": [], - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 22, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 6, - │ "total_tokens": 28 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "One, two, three." - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 6, - │ "promptTokens": 22, - │ "totalTokens": 28 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "stream": true, - │ "stream_options": { - │ "include_usage": true - │ }, - │ "temperature": 0 - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_max_tokens": 32, - │ "ls_model_name": "gpt-4o-mini-2024-07-18", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "ls_temperature": 0, - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.5.3" - │ } - │ }, - │ "model": "gpt-4o-mini-2024-07-18", - │ "options": {}, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "streaming": true, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 0, - │ "completion_tokens": 6, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 22, - │ "time_to_first_token": 0, - │ "tokens": 28 - │ } - ├── langchain-tool-operation - │ metadata: { - │ "operation": "tool", - │ "testRunId": "" - │ } - │ └── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "Use the get_weather tool for Paris, France. Do not answer from memory.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "finish_reason": "tool_calls" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": { - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"location\":\"Paris, France\"}", - │ "name": "get_weather" - │ }, - │ "id": "", - │ "type": "function" - │ } - │ ] - │ }, - │ "content": "", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "tool_calls", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "system_fingerprint": "", - │ "tokenUsage": { - │ "completionTokens": 16, - │ "promptTokens": 72, - │ "totalTokens": 88 - │ }, - │ "usage": { - │ "completion_tokens": 16, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 72, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 88 - │ } - │ }, - │ "tool_calls": [ - │ { - │ "args": { - │ "location": "Paris, France" - │ }, - │ "id": "", - │ "name": "get_weather", - │ "type": "tool_call" - │ } - │ ], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 72, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 16, - │ "total_tokens": 88 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "" - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 16, - │ "promptTokens": 72, - │ "totalTokens": 88 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_tokens": 128, - │ "model": "gpt-4o-mini-2024-07-18", - │ "stream": false, - │ "temperature": 0, - │ "tools": [ - │ { - │ "function": { - │ "description": "Get the current weather in a given location", - │ "name": "get_weather", - │ "parameters": { - │ "$schema": "http://json-schema.org/draft-07/schema#", - │ "additionalProperties": false, - │ "properties": { - │ "location": { - │ "description": "The city and state or city and country", - │ "type": "string" - │ } - │ }, - │ "required": [ - │ "location" - │ ], - │ "type": "object" - │ } - │ }, - │ "type": "function" - │ } - │ ] - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_max_tokens": 128, - │ "ls_model_name": "gpt-4o-mini-2024-07-18", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "ls_temperature": 0, - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.5.3" - │ } - │ }, - │ "model": "gpt-4o-mini-2024-07-18", - │ "options": { - │ "tools": [ - │ { - │ "function": { - │ "description": "Get the current weather in a given location", - │ "name": "get_weather", - │ "parameters": { - │ "$schema": "http://json-schema.org/draft-07/schema#", - │ "additionalProperties": false, - │ "properties": { - │ "location": { - │ "description": "The city and state or city and country", - │ "type": "string" - │ } - │ }, - │ "required": [ - │ "location" - │ ], - │ "type": "object" - │ } - │ }, - │ "type": "function" - │ } - │ ] - │ }, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 128, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 0, - │ "completion_tokens": 16, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 72, - │ "tokens": 88 - │ } - └── langchain-tool-result-operation - metadata: { - "operation": "tool-result", - "testRunId": "" - } - ├── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "What is 127 multiplied by 49? Use the calculate tool.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "finish_reason": "tool_calls" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": { - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"operation\":\"multiply\",\"a\":127,\"b\":49}", - │ "name": "calculate" - │ }, - │ "id": "", - │ "type": "function" - │ } - │ ] - │ }, - │ "content": "", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "tool_calls", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "system_fingerprint": "", - │ "tokenUsage": { - │ "completionTokens": 21, - │ "promptTokens": 76, - │ "totalTokens": 97 - │ }, - │ "usage": { - │ "completion_tokens": 21, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 76, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 97 - │ } - │ }, - │ "tool_calls": [ - │ { - │ "args": { - │ "a": 127, - │ "b": 49, - │ "operation": "multiply" - │ }, - │ "id": "", - │ "name": "calculate", - │ "type": "tool_call" - │ } - │ ], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 76, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 21, - │ "total_tokens": 97 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "" - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 21, - │ "promptTokens": 76, - │ "totalTokens": 97 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_tokens": 128, - │ "model": "gpt-4o-mini-2024-07-18", - │ "stream": false, - │ "temperature": 0, - │ "tools": [ - │ { - │ "function": { - │ "description": "Perform a mathematical calculation", - │ "name": "calculate", - │ "parameters": { - │ "$schema": "http://json-schema.org/draft-07/schema#", - │ "additionalProperties": false, - │ "properties": { - │ "a": { - │ "type": "number" - │ }, - │ "b": { - │ "type": "number" - │ }, - │ "operation": { - │ "enum": [ - │ "add", - │ "subtract", - │ "multiply", - │ "divide" - │ ], - │ "type": "string" - │ } - │ }, - │ "required": [ - │ "operation", - │ "a", - │ "b" - │ ], - │ "type": "object" - │ } - │ }, - │ "type": "function" - │ } - │ ] - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_max_tokens": 128, - │ "ls_model_name": "gpt-4o-mini-2024-07-18", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "ls_temperature": 0, - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.5.3" - │ } - │ }, - │ "model": "gpt-4o-mini-2024-07-18", - │ "options": { - │ "tools": [ - │ { - │ "function": { - │ "description": "Perform a mathematical calculation", - │ "name": "calculate", - │ "parameters": { - │ "$schema": "http://json-schema.org/draft-07/schema#", - │ "additionalProperties": false, - │ "properties": { - │ "a": { - │ "type": "number" - │ }, - │ "b": { - │ "type": "number" - │ }, - │ "operation": { - │ "enum": [ - │ "add", - │ "subtract", - │ "multiply", - │ "divide" - │ ], - │ "type": "string" - │ } - │ }, - │ "required": [ - │ "operation", - │ "a", - │ "b" - │ ], - │ "type": "object" - │ } - │ }, - │ "type": "function" - │ } - │ ] - │ }, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 128, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 0, - │ "completion_tokens": 21, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 76, - │ "tokens": 97 - │ } - └── ChatOpenAI [llm] - input: [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - }, - { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "", - "invalid_tool_calls": [], - "response_metadata": {}, - "tool_calls": [ - { - "args": { - "a": 127, - "b": 49, - "operation": "multiply" - }, - "id": "", - "name": "calculate", - "type": "tool_call" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - { - "id": [ - "langchain_core", - "messages", - "ToolMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "6223", - "response_metadata": {}, - "tool_call_id": "call_BMkQ0blzGQeVPEC7pIElPdRs" - }, - "lc": 1, - "type": "constructor" - } - ] - ] - output: { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "stop" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "127 multiplied by 49 is 6223.", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 11, - "promptTokens": 106, - "totalTokens": 117 - }, - "usage": { - "completion_tokens": 11, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 106, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 117 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 106, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 11, - "total_tokens": 117 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "127 multiplied by 49 is 6223." - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 11, - "promptTokens": 106, - "totalTokens": 117 - } - } - } - metadata: { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 128, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.5.3" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": { - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - } - metrics: { - "completion_reasoning_tokens": 0, - "completion_tokens": 11, - "prompt_cached_tokens": 0, - "prompt_tokens": 106, - "tokens": 117 - } diff --git a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1.span-tree.json b/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1.span-tree.json deleted file mode 100644 index f9106045f..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1.span-tree.json +++ /dev/null @@ -1,1511 +0,0 @@ -{ - "span_tree": [ - { - "name": "langchain-wrapper-root", - "type": "task", - "children": [ - { - "name": "langchain-invoke-operation", - "children": [ - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Reply with exactly OK.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "stop" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "OK", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-5-nano-2025-08-07", - "model_provider": "openai", - "tokenUsage": { - "completionTokens": 138, - "promptTokens": 11, - "totalTokens": 149 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 11, - "output_token_details": { - "audio": 0, - "reasoning": 128 - }, - "output_tokens": 138, - "total_tokens": 149 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "OK" - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 138, - "promptTokens": 11, - "totalTokens": 149 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_completion_tokens": 512, - "model": "gpt-5-nano", - "stream": false - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_model_name": "gpt-5-nano", - "ls_model_type": "chat", - "ls_provider": "openai", - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.3.0" - } - }, - "model": "gpt-5-nano-2025-08-07", - "options": {}, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 512, - "model": "gpt-5-nano", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - } - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 128, - "completion_tokens": 138, - "prompt_cached_tokens": 0, - "prompt_tokens": 11, - "tokens": 149 - } - } - ], - "metadata": { - "operation": "invoke", - "testRunId": "" - } - }, - { - "name": "langchain-chain-operation", - "children": [ - { - "name": "RunnableSequence", - "type": "task", - "children": [ - { - "name": "ChatPromptTemplate", - "type": "task", - "children": [], - "input": { - "word": "PARIS" - }, - "output": { - "id": [ - "langchain_core", - "prompt_values", - "ChatPromptValue" - ], - "kwargs": { - "messages": [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Reply with the single word PARIS and nothing else.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - "metadata": { - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "metadata": {}, - "name": "ChatPromptTemplate", - "parent_run_id": "", - "run_id": "", - "run_type": "prompt", - "serialized": { - "id": [ - "langchain_core", - "prompts", - "chat", - "ChatPromptTemplate" - ], - "kwargs": { - "input_variables": [ - "word" - ], - "messages": [ - { - "id": [ - "langchain_core", - "prompts", - "chat", - "HumanMessagePromptTemplate" - ], - "kwargs": { - "prompt": { - "id": [ - "langchain_core", - "prompts", - "prompt", - "PromptTemplate" - ], - "kwargs": { - "input_variables": [ - "word" - ], - "template": "Reply with the single word {word} and nothing else.", - "template_format": "f-string" - }, - "lc": 1, - "type": "constructor" - } - }, - "lc": 1, - "type": "constructor" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - "tags": [ - "seq:step:1" - ] - } - }, - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Reply with the single word PARIS and nothing else.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "stop" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "PARIS", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 2, - "promptTokens": 18, - "totalTokens": 20 - }, - "usage": { - "completion_tokens": 2, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 18, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 20 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 18, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 2, - "total_tokens": 20 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "PARIS" - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 2, - "promptTokens": 18, - "totalTokens": 20 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0 - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 32, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.3.0" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": {}, - "parent_run_id": "", - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [ - "seq:step:2" - ] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 2, - "prompt_cached_tokens": 0, - "prompt_tokens": 18, - "tokens": 20 - } - } - ], - "input": { - "word": "PARIS" - }, - "output": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "PARIS", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 2, - "promptTokens": 18, - "totalTokens": 20 - }, - "usage": { - "completion_tokens": 2, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 18, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 20 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 18, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 2, - "total_tokens": 20 - } - }, - "lc": 1, - "type": "constructor" - }, - "metadata": { - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "metadata": {}, - "run_id": "", - "serialized": { - "id": [ - "langchain_core", - "runnables", - "RunnableSequence" - ], - "kwargs": { - "first": { - "id": [ - "langchain_core", - "prompts", - "chat", - "ChatPromptTemplate" - ], - "kwargs": { - "input_variables": [ - "word" - ], - "messages": [ - { - "id": [ - "langchain_core", - "prompts", - "chat", - "HumanMessagePromptTemplate" - ], - "kwargs": { - "prompt": { - "id": [ - "langchain_core", - "prompts", - "prompt", - "PromptTemplate" - ], - "kwargs": { - "input_variables": [ - "word" - ], - "template": "Reply with the single word {word} and nothing else.", - "template_format": "f-string" - }, - "lc": 1, - "type": "constructor" - } - }, - "lc": 1, - "type": "constructor" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - "last": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - } - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - } - } - ], - "metadata": { - "operation": "chain", - "testRunId": "" - } - }, - { - "name": "langchain-stream-operation", - "children": [ - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Count from 1 to 3 and include the words one two three.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "completion": 0, - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "prompt": 0, - "service_tier": "default", - "system_fingerprint": "" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessageChunk" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "One, two, three.", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "completion": 0, - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "prompt": 0, - "service_tier": "default", - "system_fingerprint": "", - "usage": { - "completion_tokens": 6, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 22, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 28 - } - }, - "tool_call_chunks": [], - "tool_calls": [], - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 22, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 6, - "total_tokens": 28 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "One, two, three." - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 6, - "promptTokens": 22, - "totalTokens": 28 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "stream": true, - "stream_options": { - "include_usage": true - }, - "temperature": 0 - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 32, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.3.0" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": {}, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 32, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "streaming": true, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 6, - "prompt_cached_tokens": 0, - "prompt_tokens": 22, - "time_to_first_token": 0, - "tokens": 28 - } - } - ], - "metadata": { - "operation": "stream", - "testRunId": "" - } - }, - { - "name": "langchain-tool-operation", - "children": [ - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "Use the get_weather tool for Paris, France. Do not answer from memory.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "tool_calls" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": { - "tool_calls": [ - { - "function": { - "arguments": "{\"location\":\"Paris, France\"}", - "name": "get_weather" - }, - "id": "", - "type": "function" - } - ] - }, - "content": "", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "tool_calls", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 16, - "promptTokens": 72, - "totalTokens": 88 - }, - "usage": { - "completion_tokens": 16, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 72, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 88 - } - }, - "tool_calls": [ - { - "args": { - "location": "Paris, France" - }, - "id": "", - "name": "get_weather", - "type": "tool_call" - } - ], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 72, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 16, - "total_tokens": 88 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "" - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 16, - "promptTokens": 72, - "totalTokens": 88 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Get the current weather in a given location", - "name": "get_weather", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "location": { - "description": "The city and state or city and country", - "type": "string" - } - }, - "required": [ - "location" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 128, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.3.0" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": { - "tools": [ - { - "function": { - "description": "Get the current weather in a given location", - "name": "get_weather", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "location": { - "description": "The city and state or city and country", - "type": "string" - } - }, - "required": [ - "location" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 16, - "prompt_cached_tokens": 0, - "prompt_tokens": 72, - "tokens": 88 - } - } - ], - "metadata": { - "operation": "tool", - "testRunId": "" - } - }, - { - "name": "langchain-tool-result-operation", - "children": [ - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "tool_calls" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": { - "tool_calls": [ - { - "function": { - "arguments": "{\"operation\":\"multiply\",\"a\":127,\"b\":49}", - "name": "calculate" - }, - "id": "", - "type": "function" - } - ] - }, - "content": "", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "tool_calls", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 21, - "promptTokens": 76, - "totalTokens": 97 - }, - "usage": { - "completion_tokens": 21, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 76, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 97 - } - }, - "tool_calls": [ - { - "args": { - "a": 127, - "b": 49, - "operation": "multiply" - }, - "id": "", - "name": "calculate", - "type": "tool_call" - } - ], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 76, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 21, - "total_tokens": 97 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "" - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 21, - "promptTokens": 76, - "totalTokens": 97 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 128, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.3.0" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": { - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 21, - "prompt_cached_tokens": 0, - "prompt_tokens": 76, - "tokens": 97 - } - }, - { - "name": "ChatOpenAI", - "type": "llm", - "children": [], - "input": [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - }, - { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "", - "invalid_tool_calls": [], - "response_metadata": {}, - "tool_calls": [ - { - "args": { - "a": 127, - "b": 49, - "operation": "multiply" - }, - "id": "", - "name": "calculate", - "type": "tool_call" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - { - "id": [ - "langchain_core", - "messages", - "ToolMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "6223", - "response_metadata": {}, - "tool_call_id": "call_dXQI5d9UAYuRygDqNMunj2W2" - }, - "lc": 1, - "type": "constructor" - } - ] - ], - "output": { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "stop" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "127 multiplied by 49 is 6223.", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 11, - "promptTokens": 106, - "totalTokens": 117 - }, - "usage": { - "completion_tokens": 11, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 106, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 117 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 106, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 11, - "total_tokens": 117 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "127 multiplied by 49 is 6223." - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 11, - "promptTokens": 106, - "totalTokens": 117 - } - } - }, - "metadata": { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 128, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.3.0" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": { - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - }, - "metrics": { - "completion_reasoning_tokens": 0, - "completion_tokens": 11, - "prompt_cached_tokens": 0, - "prompt_tokens": 106, - "tokens": 117 - } - } - ], - "metadata": { - "operation": "tool-result", - "testRunId": "" - } - } - ], - "metadata": { - "scenario": "wrap-langchain-js-traces", - "testRunId": "" - } - } - ] -} diff --git a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1.span-tree.txt b/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1.span-tree.txt deleted file mode 100644 index 7677c0bdd..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/__snapshots__/wrap-langchain-js-traces-v1.span-tree.txt +++ /dev/null @@ -1,1450 +0,0 @@ -span_tree: -└── langchain-wrapper-root [task] - metadata: { - "scenario": "wrap-langchain-js-traces", - "testRunId": "" - } - ├── langchain-invoke-operation - │ metadata: { - │ "operation": "invoke", - │ "testRunId": "" - │ } - │ └── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "Reply with exactly OK.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "finish_reason": "stop" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "OK", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "stop", - │ "model_name": "gpt-5-nano-2025-08-07", - │ "model_provider": "openai", - │ "tokenUsage": { - │ "completionTokens": 138, - │ "promptTokens": 11, - │ "totalTokens": 149 - │ } - │ }, - │ "tool_calls": [], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 11, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 128 - │ }, - │ "output_tokens": 138, - │ "total_tokens": 149 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "OK" - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 138, - │ "promptTokens": 11, - │ "totalTokens": 149 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_completion_tokens": 512, - │ "model": "gpt-5-nano", - │ "stream": false - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_model_name": "gpt-5-nano", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.3.0" - │ } - │ }, - │ "model": "gpt-5-nano-2025-08-07", - │ "options": {}, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 512, - │ "model": "gpt-5-nano", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 128, - │ "completion_tokens": 138, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 11, - │ "tokens": 149 - │ } - ├── langchain-chain-operation - │ metadata: { - │ "operation": "chain", - │ "testRunId": "" - │ } - │ └── RunnableSequence [task] - │ input: { - │ "word": "PARIS" - │ } - │ output: { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "PARIS", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "stop", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "system_fingerprint": "", - │ "tokenUsage": { - │ "completionTokens": 2, - │ "promptTokens": 18, - │ "totalTokens": 20 - │ }, - │ "usage": { - │ "completion_tokens": 2, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 18, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 20 - │ } - │ }, - │ "tool_calls": [], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 18, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 2, - │ "total_tokens": 20 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ metadata: { - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "metadata": {}, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain_core", - │ "runnables", - │ "RunnableSequence" - │ ], - │ "kwargs": { - │ "first": { - │ "id": [ - │ "langchain_core", - │ "prompts", - │ "chat", - │ "ChatPromptTemplate" - │ ], - │ "kwargs": { - │ "input_variables": [ - │ "word" - │ ], - │ "messages": [ - │ { - │ "id": [ - │ "langchain_core", - │ "prompts", - │ "chat", - │ "HumanMessagePromptTemplate" - │ ], - │ "kwargs": { - │ "prompt": { - │ "id": [ - │ "langchain_core", - │ "prompts", - │ "prompt", - │ "PromptTemplate" - │ ], - │ "kwargs": { - │ "input_variables": [ - │ "word" - │ ], - │ "template": "Reply with the single word {word} and nothing else.", - │ "template_format": "f-string" - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "last": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ ├── ChatPromptTemplate [task] - │ │ input: { - │ │ "word": "PARIS" - │ │ } - │ │ output: { - │ │ "id": [ - │ │ "langchain_core", - │ │ "prompt_values", - │ │ "ChatPromptValue" - │ │ ], - │ │ "kwargs": { - │ │ "messages": [ - │ │ { - │ │ "id": [ - │ │ "langchain_core", - │ │ "messages", - │ │ "HumanMessage" - │ │ ], - │ │ "kwargs": { - │ │ "additional_kwargs": {}, - │ │ "content": "Reply with the single word PARIS and nothing else.", - │ │ "response_metadata": {} - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ } - │ │ ] - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ } - │ │ metadata: { - │ │ "braintrust": { - │ │ "integration_name": "langchain-js", - │ │ "sdk_language": "javascript" - │ │ }, - │ │ "metadata": {}, - │ │ "name": "ChatPromptTemplate", - │ │ "parent_run_id": "", - │ │ "run_id": "", - │ │ "run_type": "prompt", - │ │ "serialized": { - │ │ "id": [ - │ │ "langchain_core", - │ │ "prompts", - │ │ "chat", - │ │ "ChatPromptTemplate" - │ │ ], - │ │ "kwargs": { - │ │ "input_variables": [ - │ │ "word" - │ │ ], - │ │ "messages": [ - │ │ { - │ │ "id": [ - │ │ "langchain_core", - │ │ "prompts", - │ │ "chat", - │ │ "HumanMessagePromptTemplate" - │ │ ], - │ │ "kwargs": { - │ │ "prompt": { - │ │ "id": [ - │ │ "langchain_core", - │ │ "prompts", - │ │ "prompt", - │ │ "PromptTemplate" - │ │ ], - │ │ "kwargs": { - │ │ "input_variables": [ - │ │ "word" - │ │ ], - │ │ "template": "Reply with the single word {word} and nothing else.", - │ │ "template_format": "f-string" - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ } - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ } - │ │ ] - │ │ }, - │ │ "lc": 1, - │ │ "type": "constructor" - │ │ }, - │ │ "tags": [ - │ │ "seq:step:1" - │ │ ] - │ │ } - │ └── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "Reply with the single word PARIS and nothing else.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "finish_reason": "stop" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "PARIS", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "stop", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "system_fingerprint": "", - │ "tokenUsage": { - │ "completionTokens": 2, - │ "promptTokens": 18, - │ "totalTokens": 20 - │ }, - │ "usage": { - │ "completion_tokens": 2, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 18, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 20 - │ } - │ }, - │ "tool_calls": [], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 18, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 2, - │ "total_tokens": 20 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "PARIS" - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 2, - │ "promptTokens": 18, - │ "totalTokens": 20 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "stream": false, - │ "temperature": 0 - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_max_tokens": 32, - │ "ls_model_name": "gpt-4o-mini-2024-07-18", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "ls_temperature": 0, - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.3.0" - │ } - │ }, - │ "model": "gpt-4o-mini-2024-07-18", - │ "options": {}, - │ "parent_run_id": "", - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [ - │ "seq:step:2" - │ ] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 0, - │ "completion_tokens": 2, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 18, - │ "tokens": 20 - │ } - ├── langchain-stream-operation - │ metadata: { - │ "operation": "stream", - │ "testRunId": "" - │ } - │ └── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "Count from 1 to 3 and include the words one two three.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "completion": 0, - │ "finish_reason": "stop", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "prompt": 0, - │ "service_tier": "default", - │ "system_fingerprint": "" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessageChunk" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "One, two, three.", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "completion": 0, - │ "finish_reason": "stop", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "prompt": 0, - │ "service_tier": "default", - │ "system_fingerprint": "", - │ "usage": { - │ "completion_tokens": 6, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 22, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 28 - │ } - │ }, - │ "tool_call_chunks": [], - │ "tool_calls": [], - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 22, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 6, - │ "total_tokens": 28 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "One, two, three." - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 6, - │ "promptTokens": 22, - │ "totalTokens": 28 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "stream": true, - │ "stream_options": { - │ "include_usage": true - │ }, - │ "temperature": 0 - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_max_tokens": 32, - │ "ls_model_name": "gpt-4o-mini-2024-07-18", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "ls_temperature": 0, - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.3.0" - │ } - │ }, - │ "model": "gpt-4o-mini-2024-07-18", - │ "options": {}, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 32, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "streaming": true, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 0, - │ "completion_tokens": 6, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 22, - │ "time_to_first_token": 0, - │ "tokens": 28 - │ } - ├── langchain-tool-operation - │ metadata: { - │ "operation": "tool", - │ "testRunId": "" - │ } - │ └── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "Use the get_weather tool for Paris, France. Do not answer from memory.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "finish_reason": "tool_calls" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": { - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"location\":\"Paris, France\"}", - │ "name": "get_weather" - │ }, - │ "id": "", - │ "type": "function" - │ } - │ ] - │ }, - │ "content": "", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "tool_calls", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "system_fingerprint": "", - │ "tokenUsage": { - │ "completionTokens": 16, - │ "promptTokens": 72, - │ "totalTokens": 88 - │ }, - │ "usage": { - │ "completion_tokens": 16, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 72, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 88 - │ } - │ }, - │ "tool_calls": [ - │ { - │ "args": { - │ "location": "Paris, France" - │ }, - │ "id": "", - │ "name": "get_weather", - │ "type": "tool_call" - │ } - │ ], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 72, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 16, - │ "total_tokens": 88 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "" - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 16, - │ "promptTokens": 72, - │ "totalTokens": 88 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_tokens": 128, - │ "model": "gpt-4o-mini-2024-07-18", - │ "stream": false, - │ "temperature": 0, - │ "tools": [ - │ { - │ "function": { - │ "description": "Get the current weather in a given location", - │ "name": "get_weather", - │ "parameters": { - │ "$schema": "http://json-schema.org/draft-07/schema#", - │ "additionalProperties": false, - │ "properties": { - │ "location": { - │ "description": "The city and state or city and country", - │ "type": "string" - │ } - │ }, - │ "required": [ - │ "location" - │ ], - │ "type": "object" - │ } - │ }, - │ "type": "function" - │ } - │ ] - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_max_tokens": 128, - │ "ls_model_name": "gpt-4o-mini-2024-07-18", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "ls_temperature": 0, - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.3.0" - │ } - │ }, - │ "model": "gpt-4o-mini-2024-07-18", - │ "options": { - │ "tools": [ - │ { - │ "function": { - │ "description": "Get the current weather in a given location", - │ "name": "get_weather", - │ "parameters": { - │ "$schema": "http://json-schema.org/draft-07/schema#", - │ "additionalProperties": false, - │ "properties": { - │ "location": { - │ "description": "The city and state or city and country", - │ "type": "string" - │ } - │ }, - │ "required": [ - │ "location" - │ ], - │ "type": "object" - │ } - │ }, - │ "type": "function" - │ } - │ ] - │ }, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 128, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 0, - │ "completion_tokens": 16, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 72, - │ "tokens": 88 - │ } - └── langchain-tool-result-operation - metadata: { - "operation": "tool-result", - "testRunId": "" - } - ├── ChatOpenAI [llm] - │ input: [ - │ [ - │ { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "HumanMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": {}, - │ "content": "What is 127 multiplied by 49? Use the calculate tool.", - │ "response_metadata": {} - │ }, - │ "lc": 1, - │ "type": "constructor" - │ } - │ ] - │ ] - │ output: { - │ "generations": [ - │ [ - │ { - │ "generationInfo": { - │ "finish_reason": "tool_calls" - │ }, - │ "message": { - │ "id": [ - │ "langchain_core", - │ "messages", - │ "AIMessage" - │ ], - │ "kwargs": { - │ "additional_kwargs": { - │ "tool_calls": [ - │ { - │ "function": { - │ "arguments": "{\"operation\":\"multiply\",\"a\":127,\"b\":49}", - │ "name": "calculate" - │ }, - │ "id": "", - │ "type": "function" - │ } - │ ] - │ }, - │ "content": "", - │ "id": "", - │ "invalid_tool_calls": [], - │ "response_metadata": { - │ "finish_reason": "tool_calls", - │ "model_name": "gpt-4o-mini-2024-07-18", - │ "model_provider": "openai", - │ "system_fingerprint": "", - │ "tokenUsage": { - │ "completionTokens": 21, - │ "promptTokens": 76, - │ "totalTokens": 97 - │ }, - │ "usage": { - │ "completion_tokens": 21, - │ "completion_tokens_details": { - │ "accepted_prediction_tokens": 0, - │ "audio_tokens": 0, - │ "reasoning_tokens": 0, - │ "rejected_prediction_tokens": 0 - │ }, - │ "prompt_tokens": 76, - │ "prompt_tokens_details": { - │ "audio_tokens": 0, - │ "cached_tokens": 0 - │ }, - │ "total_tokens": 97 - │ } - │ }, - │ "tool_calls": [ - │ { - │ "args": { - │ "a": 127, - │ "b": 49, - │ "operation": "multiply" - │ }, - │ "id": "", - │ "name": "calculate", - │ "type": "tool_call" - │ } - │ ], - │ "type": "ai", - │ "usage_metadata": { - │ "input_token_details": { - │ "audio": 0, - │ "cache_read": 0 - │ }, - │ "input_tokens": 76, - │ "output_token_details": { - │ "audio": 0, - │ "reasoning": 0 - │ }, - │ "output_tokens": 21, - │ "total_tokens": 97 - │ } - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "text": "" - │ } - │ ] - │ ], - │ "llmOutput": { - │ "tokenUsage": { - │ "completionTokens": 21, - │ "promptTokens": 76, - │ "totalTokens": 97 - │ } - │ } - │ } - │ metadata: { - │ "batch_size": 1, - │ "braintrust": { - │ "integration_name": "langchain-js", - │ "sdk_language": "javascript" - │ }, - │ "invocation_params": { - │ "max_tokens": 128, - │ "model": "gpt-4o-mini-2024-07-18", - │ "stream": false, - │ "temperature": 0, - │ "tools": [ - │ { - │ "function": { - │ "description": "Perform a mathematical calculation", - │ "name": "calculate", - │ "parameters": { - │ "$schema": "http://json-schema.org/draft-07/schema#", - │ "additionalProperties": false, - │ "properties": { - │ "a": { - │ "type": "number" - │ }, - │ "b": { - │ "type": "number" - │ }, - │ "operation": { - │ "enum": [ - │ "add", - │ "subtract", - │ "multiply", - │ "divide" - │ ], - │ "type": "string" - │ } - │ }, - │ "required": [ - │ "operation", - │ "a", - │ "b" - │ ], - │ "type": "object" - │ } - │ }, - │ "type": "function" - │ } - │ ] - │ }, - │ "metadata": { - │ "ls_integration": "langchain_chat_model", - │ "ls_max_tokens": 128, - │ "ls_model_name": "gpt-4o-mini-2024-07-18", - │ "ls_model_type": "chat", - │ "ls_provider": "openai", - │ "ls_temperature": 0, - │ "versions": { - │ "@langchain/core": "1.2.1", - │ "@langchain/openai": "1.3.0" - │ } - │ }, - │ "model": "gpt-4o-mini-2024-07-18", - │ "options": { - │ "tools": [ - │ { - │ "function": { - │ "description": "Perform a mathematical calculation", - │ "name": "calculate", - │ "parameters": { - │ "$schema": "http://json-schema.org/draft-07/schema#", - │ "additionalProperties": false, - │ "properties": { - │ "a": { - │ "type": "number" - │ }, - │ "b": { - │ "type": "number" - │ }, - │ "operation": { - │ "enum": [ - │ "add", - │ "subtract", - │ "multiply", - │ "divide" - │ ], - │ "type": "string" - │ } - │ }, - │ "required": [ - │ "operation", - │ "a", - │ "b" - │ ], - │ "type": "object" - │ } - │ }, - │ "type": "function" - │ } - │ ] - │ }, - │ "run_id": "", - │ "serialized": { - │ "id": [ - │ "langchain", - │ "chat_models", - │ "openai", - │ "ChatOpenAI" - │ ], - │ "kwargs": { - │ "max_tokens": 128, - │ "model": "gpt-4o-mini-2024-07-18", - │ "openai_api_key": { - │ "id": [ - │ "OPENAI_API_KEY" - │ ], - │ "lc": 1, - │ "type": "secret" - │ }, - │ "temperature": 0 - │ }, - │ "lc": 1, - │ "type": "constructor" - │ }, - │ "tags": [] - │ } - │ metrics: { - │ "completion_reasoning_tokens": 0, - │ "completion_tokens": 21, - │ "prompt_cached_tokens": 0, - │ "prompt_tokens": 76, - │ "tokens": 97 - │ } - └── ChatOpenAI [llm] - input: [ - [ - { - "id": [ - "langchain_core", - "messages", - "HumanMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "What is 127 multiplied by 49? Use the calculate tool.", - "response_metadata": {} - }, - "lc": 1, - "type": "constructor" - }, - { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "", - "invalid_tool_calls": [], - "response_metadata": {}, - "tool_calls": [ - { - "args": { - "a": 127, - "b": 49, - "operation": "multiply" - }, - "id": "", - "name": "calculate", - "type": "tool_call" - } - ] - }, - "lc": 1, - "type": "constructor" - }, - { - "id": [ - "langchain_core", - "messages", - "ToolMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "6223", - "response_metadata": {}, - "tool_call_id": "call_dXQI5d9UAYuRygDqNMunj2W2" - }, - "lc": 1, - "type": "constructor" - } - ] - ] - output: { - "generations": [ - [ - { - "generationInfo": { - "finish_reason": "stop" - }, - "message": { - "id": [ - "langchain_core", - "messages", - "AIMessage" - ], - "kwargs": { - "additional_kwargs": {}, - "content": "127 multiplied by 49 is 6223.", - "id": "", - "invalid_tool_calls": [], - "response_metadata": { - "finish_reason": "stop", - "model_name": "gpt-4o-mini-2024-07-18", - "model_provider": "openai", - "system_fingerprint": "", - "tokenUsage": { - "completionTokens": 11, - "promptTokens": 106, - "totalTokens": 117 - }, - "usage": { - "completion_tokens": 11, - "completion_tokens_details": { - "accepted_prediction_tokens": 0, - "audio_tokens": 0, - "reasoning_tokens": 0, - "rejected_prediction_tokens": 0 - }, - "prompt_tokens": 106, - "prompt_tokens_details": { - "audio_tokens": 0, - "cached_tokens": 0 - }, - "total_tokens": 117 - } - }, - "tool_calls": [], - "type": "ai", - "usage_metadata": { - "input_token_details": { - "audio": 0, - "cache_read": 0 - }, - "input_tokens": 106, - "output_token_details": { - "audio": 0, - "reasoning": 0 - }, - "output_tokens": 11, - "total_tokens": 117 - } - }, - "lc": 1, - "type": "constructor" - }, - "text": "127 multiplied by 49 is 6223." - } - ] - ], - "llmOutput": { - "tokenUsage": { - "completionTokens": 11, - "promptTokens": 106, - "totalTokens": 117 - } - } - } - metadata: { - "batch_size": 1, - "braintrust": { - "integration_name": "langchain-js", - "sdk_language": "javascript" - }, - "invocation_params": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "stream": false, - "temperature": 0, - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "metadata": { - "ls_integration": "langchain_chat_model", - "ls_max_tokens": 128, - "ls_model_name": "gpt-4o-mini-2024-07-18", - "ls_model_type": "chat", - "ls_provider": "openai", - "ls_temperature": 0, - "versions": { - "@langchain/core": "1.2.1", - "@langchain/openai": "1.3.0" - } - }, - "model": "gpt-4o-mini-2024-07-18", - "options": { - "tools": [ - { - "function": { - "description": "Perform a mathematical calculation", - "name": "calculate", - "parameters": { - "$schema": "http://json-schema.org/draft-07/schema#", - "additionalProperties": false, - "properties": { - "a": { - "type": "number" - }, - "b": { - "type": "number" - }, - "operation": { - "enum": [ - "add", - "subtract", - "multiply", - "divide" - ], - "type": "string" - } - }, - "required": [ - "operation", - "a", - "b" - ], - "type": "object" - } - }, - "type": "function" - } - ] - }, - "run_id": "", - "serialized": { - "id": [ - "langchain", - "chat_models", - "openai", - "ChatOpenAI" - ], - "kwargs": { - "max_tokens": 128, - "model": "gpt-4o-mini-2024-07-18", - "openai_api_key": { - "id": [ - "OPENAI_API_KEY" - ], - "lc": 1, - "type": "secret" - }, - "temperature": 0 - }, - "lc": 1, - "type": "constructor" - }, - "tags": [] - } - metrics: { - "completion_reasoning_tokens": 0, - "completion_tokens": 11, - "prompt_cached_tokens": 0, - "prompt_tokens": 106, - "tokens": 117 - } diff --git a/e2e/scenarios/wrap-langchain-js-traces/assertions.ts b/e2e/scenarios/wrap-langchain-js-traces/assertions.ts deleted file mode 100644 index aed4ce95b..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/assertions.ts +++ /dev/null @@ -1,113 +0,0 @@ -import { expect } from "vitest"; -import type { CapturedLogEvent } from "../../helpers/mock-braintrust-server"; -import { findChildSpans, findLatestSpan } from "../../helpers/trace-selectors"; - -function findNamedChildSpan( - capturedEvents: CapturedLogEvent[], - names: string[], - parentId: string | undefined, -) { - for (const name of names) { - const span = findChildSpans(capturedEvents, name, parentId)[0]; - if (span) { - return span; - } - } - - return undefined; -} - -export function assertLangchainTraces(options: { - capturedEvents: CapturedLogEvent[]; - rootName: string; - scenarioName: string; -}): CapturedLogEvent[] { - const root = findLatestSpan(options.capturedEvents, options.rootName); - const invokeOperation = findLatestSpan( - options.capturedEvents, - "langchain-invoke-operation", - ); - const chainOperation = findLatestSpan( - options.capturedEvents, - "langchain-chain-operation", - ); - const streamOperation = findLatestSpan( - options.capturedEvents, - "langchain-stream-operation", - ); - const toolOperation = findLatestSpan( - options.capturedEvents, - "langchain-tool-operation", - ); - const toolResultOperation = findLatestSpan( - options.capturedEvents, - "langchain-tool-result-operation", - ); - - expect(root).toBeDefined(); - expect(invokeOperation).toBeDefined(); - expect(chainOperation).toBeDefined(); - expect(streamOperation).toBeDefined(); - expect(toolOperation).toBeDefined(); - expect(toolResultOperation).toBeDefined(); - - expect(root?.row.metadata).toMatchObject({ - scenario: options.scenarioName, - }); - - for (const operation of [ - invokeOperation, - chainOperation, - streamOperation, - toolOperation, - toolResultOperation, - ]) { - expect(operation?.span.parentIds).toEqual([root?.span.id ?? ""]); - } - - const invokeSpan = findNamedChildSpan( - options.capturedEvents, - ["ChatOpenAI"], - invokeOperation?.span.id, - ); - expect(invokeSpan).toBeDefined(); - expect(invokeSpan?.span.type).toBe("llm"); - expect(invokeSpan?.metrics?.completion_reasoning_tokens).toBeGreaterThan(0); - - const chainChildren = findChildSpans( - options.capturedEvents, - "RunnableSequence", - chainOperation?.span.id, - ); - expect(chainChildren.length).toBeGreaterThanOrEqual(1); - - const streamSpan = findNamedChildSpan( - options.capturedEvents, - ["ChatOpenAI"], - streamOperation?.span.id, - ); - expect(streamSpan).toBeDefined(); - expect(streamSpan?.span.type).toBe("llm"); - expect(streamSpan?.metrics).toMatchObject({ - time_to_first_token: expect.any(Number), - }); - - const toolSpan = findNamedChildSpan( - options.capturedEvents, - ["ChatOpenAI"], - toolOperation?.span.id, - ); - expect(toolSpan).toBeDefined(); - - const toolOutputStr = JSON.stringify(toolSpan?.output ?? {}); - expect(toolOutputStr).toContain("get_weather"); - - const toolResultSpans = findChildSpans( - options.capturedEvents, - "ChatOpenAI", - toolResultOperation?.span.id, - ); - expect(toolResultSpans.length).toBeGreaterThanOrEqual(2); - - return options.capturedEvents; -} diff --git a/e2e/scenarios/wrap-langchain-js-traces/package.json b/e2e/scenarios/wrap-langchain-js-traces/package.json deleted file mode 100644 index 7ef2d95ee..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/package.json +++ /dev/null @@ -1,31 +0,0 @@ -{ - "name": "@braintrust/e2e-wrap-langchain-js-traces", - "private": true, - "braintrustScenario": { - "bump": { - "dependencies": { - "langchain-core-v1-latest": { - "package": "@langchain/core", - "range": "1" - }, - "langchain-openai-v1-latest": { - "package": "@langchain/openai", - "range": "1" - } - } - } - }, - "dependencies": { - "zod": "3.25.76", - "langchain-core-v1": "npm:@langchain/core@1.1.35", - "langchain-core-v1-latest": "npm:@langchain/core@1.2.1", - "langchain-openai-v1": "npm:@langchain/openai@1.3.0", - "langchain-openai-v1-latest": "npm:@langchain/openai@1.5.3" - }, - "pnpm": { - "overrides": { - "langsmith@<0.3.30": "^0.3.30", - "uuid": "^11.1.1" - } - } -} diff --git a/e2e/scenarios/wrap-langchain-js-traces/pnpm-lock.yaml b/e2e/scenarios/wrap-langchain-js-traces/pnpm-lock.yaml deleted file mode 100644 index 5dcbf1544..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/pnpm-lock.yaml +++ /dev/null @@ -1,296 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -overrides: - langsmith@<0.3.30: ^0.3.30 - uuid: ^11.1.1 - -importers: - - .: - dependencies: - langchain-core-v1-latest: - specifier: npm:@langchain/core@1.2.1 - version: '@langchain/core@1.2.1(openai@6.45.0(zod@3.25.76))' - langchain-core-v1: - specifier: npm:@langchain/core@1.1.35 - version: '@langchain/core@1.1.35(openai@6.45.0(zod@3.25.76))' - langchain-openai-v1-latest: - specifier: npm:@langchain/openai@1.5.3 - version: '@langchain/openai@1.5.3(@langchain/core@1.2.1(openai@6.45.0(zod@3.25.76)))' - langchain-openai-v1: - specifier: npm:@langchain/openai@1.3.0 - version: '@langchain/openai@1.3.0(@langchain/core@1.2.1(openai@6.45.0(zod@3.25.76)))' - zod: - specifier: 3.25.76 - version: 3.25.76 - -packages: - - '@cfworker/json-schema@4.1.1': - resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} - - '@langchain/core@1.1.35': - resolution: {integrity: sha512-TM0idLSAx17IEMqEIN7F8MDdXpN53p4A4vhhjZxf7LedwShB/8Zx1F5D5Nq1Bbn0zh9QDzSHpWyfCrTtggKf7g==} - engines: {node: '>=20'} - - '@langchain/core@1.2.1': - resolution: {integrity: sha512-NNG/cC5FGuHDOAP56h0ddp8Rfk8p+othWzEK5RV9JIG6RvnF5vGa5r0AEGtKfQieed7s1kC42GuIzVOBvMBL/g==} - engines: {node: '>=20'} - - '@langchain/openai@1.3.0': - resolution: {integrity: sha512-FDsF6xKCvFduiZcX57fL2Md+DZ+fJubcUN1iwUaEwJOQnq7zFFYj3a/KuQ7EiOFR3hEsnhPilSfxO1VW85wMZw==} - engines: {node: '>=20'} - peerDependencies: - '@langchain/core': ^1.1.33 - - '@langchain/openai@1.5.3': - resolution: {integrity: sha512-OStS2AUvy9oe/hEf/3ndBOFztUDOfuJYLNXh89m3iiJAI2Cp5Dp0n/pvpO27MO0b+VgENd+xSHVyQZ7fe+ulxg==} - engines: {node: '>=20'} - peerDependencies: - '@langchain/core': ^1.2.1 - - '@standard-schema/spec@1.1.0': - resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - - '@types/uuid@10.0.0': - resolution: {integrity: sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==} - - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - - chalk@5.6.2: - resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} - engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} - - console-table-printer@2.15.0: - resolution: {integrity: sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==} - - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - - js-tiktoken@1.0.21: - resolution: {integrity: sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==} - - langsmith@0.5.11: - resolution: {integrity: sha512-Yio502Ow2vbVt16P1sybNMNpMsr5BMqoeonoi4flrcDsP55No/aCe2zydtBNOv0+kjKQw4WSKAzTsNwenDeD5w==} - peerDependencies: - '@opentelemetry/api': '*' - '@opentelemetry/exporter-trace-otlp-proto': '*' - '@opentelemetry/sdk-trace-base': '*' - openai: '*' - ws: '>=7' - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@opentelemetry/exporter-trace-otlp-proto': - optional: true - '@opentelemetry/sdk-trace-base': - optional: true - openai: - optional: true - ws: - optional: true - - mustache@4.2.0: - resolution: {integrity: sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==} - hasBin: true - - openai@6.32.0: - resolution: {integrity: sha512-j3k+BjydAf8yQlcOI7WUQMQTbbF5GEIMAE2iZYCOzwwB3S2pCheaWYp+XZRNAch4jWVc52PMDGRRjutao3lLCg==} - hasBin: true - peerDependencies: - ws: ^8.18.0 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - ws: - optional: true - zod: - optional: true - - openai@6.45.0: - resolution: {integrity: sha512-5DQVNErssk0afNpTTHUm/qZPU4iKR9OYdNid8Ib4puq4gHNNvGWZht2zY4h9a8JMF949Ik6m8gQutllVPbjdnw==} - peerDependencies: - '@aws-sdk/credential-provider-node': '>=3.972.0 <4' - '@smithy/hash-node': '>=4.3.0 <5' - '@smithy/signature-v4': '>=5.4.0 <6' - ws: ^8.18.0 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@aws-sdk/credential-provider-node': - optional: true - '@smithy/hash-node': - optional: true - '@smithy/signature-v4': - optional: true - ws: - optional: true - zod: - optional: true - - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - - p-queue@6.6.2: - resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} - engines: {node: '>=8'} - - p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - simple-wcswidth@1.1.2: - resolution: {integrity: sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==} - - uuid@11.1.1: - resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} - hasBin: true - - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} - -snapshots: - - '@cfworker/json-schema@4.1.1': {} - - '@langchain/core@1.1.35(openai@6.45.0(zod@3.25.76))': - dependencies: - '@cfworker/json-schema': 4.1.1 - '@standard-schema/spec': 1.1.0 - ansi-styles: 5.2.0 - camelcase: 6.3.0 - decamelize: 1.2.0 - js-tiktoken: 1.0.21 - langsmith: 0.5.11(openai@6.45.0(zod@3.25.76)) - mustache: 4.2.0 - p-queue: 6.6.2 - uuid: 11.1.1 - zod: 3.25.76 - transitivePeerDependencies: - - '@opentelemetry/api' - - '@opentelemetry/exporter-trace-otlp-proto' - - '@opentelemetry/sdk-trace-base' - - openai - - ws - - '@langchain/core@1.2.1(openai@6.45.0(zod@3.25.76))': - dependencies: - '@cfworker/json-schema': 4.1.1 - '@standard-schema/spec': 1.1.0 - js-tiktoken: 1.0.21 - langsmith: 0.5.11(openai@6.45.0(zod@3.25.76)) - mustache: 4.2.0 - p-queue: 6.6.2 - zod: 3.25.76 - transitivePeerDependencies: - - '@opentelemetry/api' - - '@opentelemetry/exporter-trace-otlp-proto' - - '@opentelemetry/sdk-trace-base' - - openai - - ws - - '@langchain/openai@1.3.0(@langchain/core@1.2.1(openai@6.45.0(zod@3.25.76)))': - dependencies: - '@langchain/core': 1.2.1(openai@6.45.0(zod@3.25.76)) - js-tiktoken: 1.0.21 - openai: 6.32.0(zod@3.25.76) - zod: 3.25.76 - transitivePeerDependencies: - - ws - - '@langchain/openai@1.5.3(@langchain/core@1.2.1(openai@6.45.0(zod@3.25.76)))': - dependencies: - '@langchain/core': 1.2.1(openai@6.45.0(zod@3.25.76)) - js-tiktoken: 1.0.21 - openai: 6.45.0(zod@3.25.76) - zod: 3.25.76 - transitivePeerDependencies: - - '@aws-sdk/credential-provider-node' - - '@smithy/hash-node' - - '@smithy/signature-v4' - - ws - - '@standard-schema/spec@1.1.0': {} - - '@types/uuid@10.0.0': {} - - ansi-styles@5.2.0: {} - - base64-js@1.5.1: {} - - camelcase@6.3.0: {} - - chalk@5.6.2: {} - - console-table-printer@2.15.0: - dependencies: - simple-wcswidth: 1.1.2 - - decamelize@1.2.0: {} - - eventemitter3@4.0.7: {} - - js-tiktoken@1.0.21: - dependencies: - base64-js: 1.5.1 - - langsmith@0.5.11(openai@6.45.0(zod@3.25.76)): - dependencies: - '@types/uuid': 10.0.0 - chalk: 5.6.2 - console-table-printer: 2.15.0 - p-queue: 6.6.2 - semver: 7.7.4 - uuid: 11.1.1 - optionalDependencies: - openai: 6.45.0(zod@3.25.76) - - mustache@4.2.0: {} - - openai@6.32.0(zod@3.25.76): - optionalDependencies: - zod: 3.25.76 - - openai@6.45.0(zod@3.25.76): - optionalDependencies: - zod: 3.25.76 - - p-finally@1.0.0: {} - - p-queue@6.6.2: - dependencies: - eventemitter3: 4.0.7 - p-timeout: 3.2.0 - - p-timeout@3.2.0: - dependencies: - p-finally: 1.0.0 - - semver@7.7.4: {} - - simple-wcswidth@1.1.2: {} - - uuid@11.1.1: {} - - zod@3.25.76: {} diff --git a/e2e/scenarios/wrap-langchain-js-traces/scenario.test.ts b/e2e/scenarios/wrap-langchain-js-traces/scenario.test.ts deleted file mode 100644 index db2ed6d51..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/scenario.test.ts +++ /dev/null @@ -1,76 +0,0 @@ -import { expect, test } from "vitest"; -import { resolveFileSnapshotPath } from "../../helpers/file-snapshot"; -import { - prepareScenarioDir, - readInstalledPackageVersion, - resolveScenarioDir, - withScenarioHarness, -} from "../../helpers/scenario-harness"; -import { matchSpanTreeSnapshot } from "../../helpers/span-tree"; - -import { assertLangchainTraces } from "./assertions"; - -const originalScenarioDir = resolveScenarioDir(import.meta.url); -const scenarioDir = await prepareScenarioDir({ - scenarioDir: originalScenarioDir, -}); -const TIMEOUT_MS = 90_000; -const scenarios = await Promise.all( - [ - { - coreDependencyName: "langchain-core-v1", - openAIDependencyName: "langchain-openai-v1", - variantKey: "wrap-langchain-js-traces-v1", - }, - { - coreDependencyName: "langchain-core-v1-latest", - openAIDependencyName: "langchain-openai-v1-latest", - variantKey: "wrap-langchain-js-traces-v1-latest", - }, - ].map(async (scenario) => ({ - ...scenario, - version: await readInstalledPackageVersion( - scenarioDir, - scenario.openAIDependencyName, - ), - })), -); - -for (const scenario of scenarios) { - test( - `wrap-langchain-js-traces captures invoke, chain, stream, and tool spans via BraintrustCallbackHandler (${scenario.version})`, - { - timeout: TIMEOUT_MS, - }, - async () => { - await withScenarioHarness(async ({ events, runScenarioDir }) => { - await runScenarioDir({ - scenarioDir, - timeoutMs: TIMEOUT_MS, - env: { - LANGCHAIN_CORE_PACKAGE_NAME: scenario.coreDependencyName, - LANGCHAIN_OPENAI_PACKAGE_NAME: scenario.openAIDependencyName, - }, - runContext: { - variantKey: scenario.variantKey, - originalScenarioDir, - }, - }); - - const spanTree = assertLangchainTraces({ - capturedEvents: events(), - rootName: "langchain-wrapper-root", - scenarioName: "wrap-langchain-js-traces", - }); - - await matchSpanTreeSnapshot( - spanTree, - resolveFileSnapshotPath( - import.meta.url, - `${scenario.variantKey}.span-tree.json`, - ), - ); - }); - }, - ); -} diff --git a/e2e/scenarios/wrap-langchain-js-traces/scenario.ts b/e2e/scenarios/wrap-langchain-js-traces/scenario.ts deleted file mode 100644 index ffa07bf2e..000000000 --- a/e2e/scenarios/wrap-langchain-js-traces/scenario.ts +++ /dev/null @@ -1,162 +0,0 @@ -import { BraintrustCallbackHandler } from "@braintrust/langchain-js"; -const langchainCorePackageName = - process.env.LANGCHAIN_CORE_PACKAGE_NAME ?? "langchain-core-v1-latest"; -const langchainOpenAIPackageName = - process.env.LANGCHAIN_OPENAI_PACKAGE_NAME ?? "langchain-openai-v1-latest"; -import { z } from "zod"; -import { - collectAsync, - runOperation, - runTracedScenario, -} from "../../helpers/provider-runtime.mjs"; -import { runMain } from "../../helpers/scenario-runtime"; - -const OPENAI_MODEL = "gpt-4o-mini-2024-07-18"; -const OPENAI_REASONING_MODEL = "gpt-5-nano"; - -runMain(async () => { - const { AIMessage, HumanMessage, ToolMessage } = await import( - `${langchainCorePackageName}/messages` - ); - const { ChatPromptTemplate } = await import( - `${langchainCorePackageName}/prompts` - ); - const { DynamicStructuredTool } = await import( - `${langchainCorePackageName}/tools` - ); - const { ChatOpenAI } = await import(langchainOpenAIPackageName); - - await runTracedScenario({ - callback: async () => { - // The handler attaches as a child of the current traced span via - // the parent option resolved by runTracedScenario. - const handler = new BraintrustCallbackHandler(); - - await runOperation("langchain-invoke-operation", "invoke", async () => { - const model = new ChatOpenAI({ - model: OPENAI_REASONING_MODEL, - maxTokens: 512, - callbacks: [handler], - }); - await model.invoke([new HumanMessage("Reply with exactly OK.")]); - }); - - await runOperation("langchain-chain-operation", "chain", async () => { - const model = new ChatOpenAI({ - model: OPENAI_MODEL, - maxTokens: 32, - temperature: 0, - }); - const prompt = ChatPromptTemplate.fromTemplate( - "Reply with the single word {word} and nothing else.", - ); - const chain = prompt.pipe(model); - await chain.invoke({ word: "PARIS" }, { callbacks: [handler] }); - }); - - await runOperation("langchain-stream-operation", "stream", async () => { - const model = new ChatOpenAI({ - model: OPENAI_MODEL, - maxTokens: 32, - temperature: 0, - streaming: true, - callbacks: [handler], - }); - const stream = await model.stream([ - new HumanMessage( - "Count from 1 to 3 and include the words one two three.", - ), - ]); - await collectAsync(stream); - }); - - await runOperation("langchain-tool-operation", "tool", async () => { - const getWeatherTool = new DynamicStructuredTool({ - name: "get_weather", - description: "Get the current weather in a given location", - schema: z.object({ - location: z - .string() - .describe("The city and state or city and country"), - }), - func: async ({ location }) => { - return JSON.stringify({ - condition: "sunny", - location, - temperatureC: 22, - }); - }, - }); - - const model = new ChatOpenAI({ - model: OPENAI_MODEL, - maxTokens: 128, - temperature: 0, - callbacks: [handler], - }); - const modelWithTools = model.bindTools([getWeatherTool]); - await modelWithTools.invoke( - "Use the get_weather tool for Paris, France. Do not answer from memory.", - ); - }); - - await runOperation( - "langchain-tool-result-operation", - "tool-result", - async () => { - const calculateTool = new DynamicStructuredTool({ - name: "calculate", - description: "Perform a mathematical calculation", - schema: z.object({ - operation: z.enum(["add", "subtract", "multiply", "divide"]), - a: z.number(), - b: z.number(), - }), - func: async ({ operation, a, b }) => { - const ops = { - add: a + b, - subtract: a - b, - multiply: a * b, - divide: b !== 0 ? a / b : 0, - }; - return String(ops[operation]); - }, - }); - - const model = new ChatOpenAI({ - model: OPENAI_MODEL, - maxTokens: 128, - temperature: 0, - callbacks: [handler], - }); - const modelWithTools = model.bindTools([calculateTool]); - - const firstResult = await modelWithTools.invoke( - "What is 127 multiplied by 49? Use the calculate tool.", - ); - - if (firstResult.tool_calls && firstResult.tool_calls.length > 0) { - const toolCall = firstResult.tool_calls[0]; - const result = String(127 * 49); - - await modelWithTools.invoke([ - new HumanMessage( - "What is 127 multiplied by 49? Use the calculate tool.", - ), - new AIMessage({ content: "", tool_calls: [toolCall] }), - new ToolMessage({ - content: result, - tool_call_id: toolCall.id, - }), - ]); - } - }, - ); - }, - metadata: { - scenario: "wrap-langchain-js-traces", - }, - projectNameBase: "e2e-wrap-langchain", - rootName: "langchain-wrapper-root", - }); -}); diff --git a/integrations/langchain-js/CHANGELOG.md b/integrations/langchain-js/CHANGELOG.md deleted file mode 100644 index 2ab7af78b..000000000 --- a/integrations/langchain-js/CHANGELOG.md +++ /dev/null @@ -1,31 +0,0 @@ -# @braintrust/langchain-js - -## 0.2.5 - -- `@braintrust/langchain-js` is now deprecated. This release marks the last release for this package. (https://github.com/braintrustdata/braintrust-sdk-javascript/pull/2383) -- Updated dependencies: braintrust@3.29.0 - -### Migration - -To migrate, install `braintrust` and import `BraintrustLangChainCallbackHandler` -from `braintrust` instead. See the -[LangChain integration guide](https://www.braintrust.dev/docs/integrations/sdk-integrations/langchain) -for setup examples. For application-wide instrumentation, use the Braintrust -runtime hook described in the -[auto-instrumentation guide](https://www.braintrust.dev/docs/instrument/trace-llm-calls#auto-instrumentation). -`setGlobalHandler` has no direct replacement; use automatic instrumentation or -pass the callback handler explicitly. - -## 0.2.4 - -### Patch Changes - -- feat: Add LangChain and LangGraph auto-instrumentation (https://github.com/braintrustdata/braintrust-sdk-javascript/pull/1897) -- Updated dependencies: braintrust@3.12.0 - -## 0.2.3 - -### Patch Changes - -- Added prompt caching token tracking for LangChain usage metadata. -- Mapped nested `input_token_details` metrics into Braintrust's standard cached-token fields, including cache reads and cache creation tokens. diff --git a/integrations/langchain-js/README.md b/integrations/langchain-js/README.md deleted file mode 100644 index 93f716031..000000000 --- a/integrations/langchain-js/README.md +++ /dev/null @@ -1,59 +0,0 @@ -# @braintrust/langchain-js - -[![npm version](https://img.shields.io/npm/v/%40braintrust%2Flangchain-js.svg)](https://www.npmjs.com/package/@braintrust/langchain-js) - -> [!WARNING] -> This package and all of its exports are deprecated and will stop being -> published after the next release. Use the LangChain integration in -> `braintrust` instead. - -## Installation - -```bash -npm install braintrust -# or -yarn add braintrust -# or -pnpm add braintrust -``` - -## Automatic instrumentation - -For application-wide instrumentation, use the Braintrust runtime hook: - -```bash -node --import braintrust/hook.mjs app.js -``` - -Braintrust also supports Vite, Webpack, esbuild, and Rollup. See the -[Braintrust SDK documentation](../../js/README.md#auto-instrumentation) for -bundler setup. - -## Manual instrumentation - -For scoped instrumentation, pass `BraintrustLangChainCallbackHandler` from -`braintrust` through LangChain's `callbacks` option: - -```typescript -import { ChatOpenAI } from "@langchain/openai"; -import { BraintrustLangChainCallbackHandler } from "braintrust"; - -const handler = new BraintrustLangChainCallbackHandler(); - -const model = new ChatOpenAI({ - callbacks: [handler], -}); - -const response = await model.invoke("Tell me a joke about bears", { - callbacks: [handler], -}); -``` - -`setGlobalHandler` does not have a direct replacement in `braintrust`. Use -automatic instrumentation for application-wide coverage, or pass the handler -explicitly for scoped instrumentation. - -## Documentation - -- Braintrust docs: [https://www.braintrust.dev/docs](https://www.braintrust.dev/docs) -- LangChain callback docs: [https://js.langchain.com/docs/how_to/#callbacks](https://js.langchain.com/docs/how_to/#callbacks) diff --git a/integrations/langchain-js/package.json b/integrations/langchain-js/package.json deleted file mode 100644 index 36e1b88f1..000000000 --- a/integrations/langchain-js/package.json +++ /dev/null @@ -1,64 +0,0 @@ -{ - "name": "@braintrust/langchain-js", - "version": "0.2.5", - "description": "SDK for integrating Braintrust with LangChain.js", - "main": "./dist/index.js", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "module": "./dist/index.mjs", - "require": "./dist/index.js" - } - }, - "scripts": { - "build": "tsup", - "check:typings": "tsc --noEmit", - "watch": "tsup --watch", - "clean": "rm -r dist/*", - "test": "vitest run", - "yalc:publish": "yalc publish" - }, - "author": "Braintrust Data Inc.", - "license": "MIT", - "devDependencies": { - "@langchain/anthropic": "^1.3.1", - "@langchain/core": "^1.1.6", - "@langchain/langgraph": "^1.0.7", - "@langchain/openai": "^1.2.0", - "@types/node": "^20.10.5", - "braintrust": "workspace:*", - "msw": "^2.6.6", - "tsup": "^8.5.1", - "typescript": "^5.3.3", - "vitest": "4.1.5", - "zod": "^3.25.34", - "zod-to-json-schema": "^3.22.5" - }, - "peerDependencies": { - "@langchain/core": ">=0.3.42", - "braintrust": ">=0.4.0" - }, - "peerDependenciesMeta": { - "braintrust": { - "optional": false - }, - "@langchain/core": { - "optional": false - } - }, - "repository": { - "type": "git", - "url": "git+https://github.com/braintrustdata/braintrust-sdk-javascript.git", - "directory": "integrations/langchain-js" - }, - "homepage": "https://www.braintrust.dev/docs", - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/", - "provenance": true - } -} diff --git a/integrations/langchain-js/src/BraintrustCallbackHandler.fixtures.ts b/integrations/langchain-js/src/BraintrustCallbackHandler.fixtures.ts deleted file mode 100644 index f7ec70434..000000000 --- a/integrations/langchain-js/src/BraintrustCallbackHandler.fixtures.ts +++ /dev/null @@ -1,234 +0,0 @@ -export const CHAT_MATH = { - id: "chatcmpl-Aao716hWOt9HBihjWh9iAPGWRpkFd", - object: "chat.completion", - created: 1733335803, - model: "gpt-4o-mini-2024-07-18", - choices: [ - { - index: 0, - message: { - role: "assistant", - content: "1 + 2 equals 3.", - refusal: null, - }, - logprobs: null, - finish_reason: "stop", - }, - ], - usage: { - prompt_tokens: 15, - completion_tokens: 8, - total_tokens: 23, - prompt_tokens_details: { - cached_tokens: 0, - audio_tokens: 0, - }, - completion_tokens_details: { - reasoning_tokens: 0, - audio_tokens: 0, - accepted_prediction_tokens: 0, - rejected_prediction_tokens: 0, - }, - }, - system_fingerprint: "fp_0705bf87c0", -}; - -export const CHAT_STREAM_PARROT = [ - `data: {"id":"chatcmpl-Ab9p7esnOlnH4ywBeHQOp4ScoCcde","object":"chat.completion.chunk","created":1733419261,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"role":"assistant","content":"","refusal":null},"logprobs":null,"finish_reason":null}],"usage":null}`, - `data: {"id":"chatcmpl-Ab9p7esnOlnH4ywBeHQOp4ScoCcde","object":"chat.completion.chunk","created":1733419261,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"content":"Pol"},"logprobs":null,"finish_reason":null}],"usage":null}`, - `data: {"id":"chatcmpl-Ab9p7esnOlnH4ywBeHQOp4ScoCcde","object":"chat.completion.chunk","created":1733419261,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"content":"ly"},"logprobs":null,"finish_reason":null}],"usage":null}`, - `data: {"id":"chatcmpl-Ab9p7esnOlnH4ywBeHQOp4ScoCcde","object":"chat.completion.chunk","created":1733419261,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"content":" wants"},"logprobs":null,"finish_reason":null}],"usage":null}`, - `data: {"id":"chatcmpl-Ab9p7esnOlnH4ywBeHQOp4ScoCcde","object":"chat.completion.chunk","created":1733419261,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"content":" more"},"logprobs":null,"finish_reason":null}],"usage":null}`, - `data: {"id":"chatcmpl-Ab9p7esnOlnH4ywBeHQOp4ScoCcde","object":"chat.completion.chunk","created":1733419261,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"content":" crackers"},"logprobs":null,"finish_reason":null}],"usage":null}`, - `data: {"id":"chatcmpl-Ab9p7esnOlnH4ywBeHQOp4ScoCcde","object":"chat.completion.chunk","created":1733419261,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{"content":"!"},"logprobs":null,"finish_reason":null}],"usage":null}`, - `data: {"id":"chatcmpl-Ab9p7esnOlnH4ywBeHQOp4ScoCcde","object":"chat.completion.chunk","created":1733419261,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[{"index":0,"delta":{},"logprobs":null,"finish_reason":"stop"}],"usage":null}`, - `data: {"id":"chatcmpl-Ab9p7esnOlnH4ywBeHQOp4ScoCcde","object":"chat.completion.chunk","created":1733419261,"model":"gpt-4o-mini-2024-07-18","system_fingerprint":"fp_0705bf87c0","choices":[],"usage":{"prompt_tokens":16,"completion_tokens":6,"total_tokens":22,"prompt_tokens_details":{"cached_tokens":0,"audio_tokens":0},"completion_tokens_details":{"reasoning_tokens":0,"audio_tokens":0,"accepted_prediction_tokens":0,"rejected_prediction_tokens":0}}}`, - `data: [DONE]`, -]; - -export const CHAT_CHAIN_MEMORY = { - id: "chatcmpl-AbAHIqtiUXMz849pZPWxB7RKF9wPh", - object: "chat.completion", - created: 1733421008, - model: "gpt-4o-mini-2024-07-18", - choices: [ - { - index: 0, - message: { - role: "assistant", - content: "Assistant: I'm called Assistant! How can I help you today?", - refusal: null, - }, - logprobs: null, - finish_reason: "stop", - }, - ], - usage: { - prompt_tokens: 24, - completion_tokens: 13, - total_tokens: 37, - prompt_tokens_details: { - cached_tokens: 0, - audio_tokens: 0, - }, - completion_tokens_details: { - reasoning_tokens: 0, - audio_tokens: 0, - accepted_prediction_tokens: 0, - rejected_prediction_tokens: 0, - }, - }, - system_fingerprint: "fp_0705bf87c0", -}; - -export const CHAT_TOOL_CALCULATOR = { - id: "chatcmpl-AbAVR1TojvbDgXRLlDyhz9NYZVitz", - object: "chat.completion", - created: 1733421885, - model: "gpt-4o-mini-2024-07-18", - choices: [ - { - index: 0, - message: { - role: "assistant", - content: null, - tool_calls: [ - { - id: "call_G2Qd8HzTMyFUiMafz5H4fBIi", - type: "function", - function: { - name: "calculator", - arguments: '{"operation":"multiply","number1":3,"number2":12}', - }, - }, - ], - refusal: null, - }, - logprobs: null, - finish_reason: "tool_calls", - }, - ], - usage: { - prompt_tokens: 93, - completion_tokens: 24, - total_tokens: 117, - prompt_tokens_details: { - cached_tokens: 0, - audio_tokens: 0, - }, - completion_tokens_details: { - reasoning_tokens: 0, - audio_tokens: 0, - accepted_prediction_tokens: 0, - rejected_prediction_tokens: 0, - }, - }, - system_fingerprint: "fp_0705bf87c0", -}; - -export const CHAT_BEAR_JOKE = { - id: "chatcmpl-AbCj2kznx4QsGpaocNir4GWdLYYqj", - object: "chat.completion", - created: 1733430416, - model: "gpt-4o-mini-2024-07-18", - choices: [ - { - index: 0, - message: { - role: "assistant", - content: - 'Why did the bear sit on the log?\n\nBecause it wanted to be a "bear-ly" seated customer! 🐻', - refusal: null, - }, - logprobs: null, - finish_reason: "stop", - }, - ], - usage: { - prompt_tokens: 13, - completion_tokens: 26, - total_tokens: 39, - prompt_tokens_details: { - cached_tokens: 0, - audio_tokens: 0, - }, - completion_tokens_details: { - reasoning_tokens: 0, - audio_tokens: 0, - accepted_prediction_tokens: 0, - rejected_prediction_tokens: 0, - }, - }, - system_fingerprint: "fp_bba3c8e70b", -}; - -export const CHAT_BEAR_POEM = { - id: "chatcmpl-AbClwtnbeqLRiWwoe21On10TRqqsW", - object: "chat.completion", - created: 1733430596, - model: "gpt-4o-mini-2024-07-18", - choices: [ - { - index: 0, - message: { - role: "assistant", - content: - "In the forest's hush, a shadow moves near, \nA gentle giant roams, the wise old bear.", - refusal: null, - }, - logprobs: null, - finish_reason: "stop", - }, - ], - usage: { - prompt_tokens: 15, - completion_tokens: 23, - total_tokens: 38, - prompt_tokens_details: { - cached_tokens: 0, - audio_tokens: 0, - }, - completion_tokens_details: { - reasoning_tokens: 0, - audio_tokens: 0, - accepted_prediction_tokens: 0, - rejected_prediction_tokens: 0, - }, - }, - system_fingerprint: "fp_bba3c8e70b", -}; - -export const CHAT_SAY_HELLO = { - id: "chatcmpl-AbFX2khtngBETl7qtntuXawF6RFPt", - object: "chat.completion", - created: 1733441204, - model: "gpt-4o-mini-2024-07-18", - choices: [ - { - index: 0, - message: { - role: "assistant", - content: "Hello! How can I assist you today?", - refusal: null, - }, - logprobs: null, - finish_reason: "stop", - }, - ], - usage: { - prompt_tokens: 9, - completion_tokens: 9, - total_tokens: 18, - prompt_tokens_details: { - cached_tokens: 0, - audio_tokens: 0, - }, - completion_tokens_details: { - reasoning_tokens: 0, - audio_tokens: 0, - accepted_prediction_tokens: 0, - rejected_prediction_tokens: 0, - }, - }, - system_fingerprint: "fp_818c284075", -}; diff --git a/integrations/langchain-js/src/BraintrustCallbackHandler.test.ts b/integrations/langchain-js/src/BraintrustCallbackHandler.test.ts deleted file mode 100644 index e45afe2e6..000000000 --- a/integrations/langchain-js/src/BraintrustCallbackHandler.test.ts +++ /dev/null @@ -1,1239 +0,0 @@ -import { ChatAnthropic } from "@langchain/anthropic"; -import { HumanMessage, SystemMessage } from "@langchain/core/messages"; -import { ChatPromptTemplate, PromptTemplate } from "@langchain/core/prompts"; -import { RunnableMap } from "@langchain/core/runnables"; -import { tool } from "@langchain/core/tools"; -import { Annotation, END, START, StateGraph } from "@langchain/langgraph"; -import { ChatOpenAI } from "@langchain/openai"; -import { flush, initLogger, NOOP_SPAN } from "braintrust"; -import { http, HttpResponse } from "msw"; -import { ReadableStream } from "stream/web"; -import { describe, expect, it } from "vitest"; -import { z } from "zod/v3"; - -import { BraintrustCallbackHandler } from "./BraintrustCallbackHandler"; -import { - CHAT_BEAR_JOKE, - CHAT_BEAR_POEM, - CHAT_CHAIN_MEMORY, - CHAT_MATH, - CHAT_SAY_HELLO, - CHAT_STREAM_PARROT, - CHAT_TOOL_CALCULATOR, -} from "./BraintrustCallbackHandler.fixtures"; -import { server } from "./test/setup"; -import { LogsRequest } from "./test/types"; -import { logsToSpans, withLogging } from "./test/utils"; - -initLogger({ - projectName: "langchain", -}); - -const handler = withLogging(new BraintrustCallbackHandler({ debug: true })); - -const encoder = new TextEncoder(); - -describe("BraintrustCallbackHandler", () => { - it("should handle LLM calls", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post("https://api.openai.com/v1/chat/completions", () => { - return HttpResponse.json(CHAT_MATH); - }), - - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["4bc6305f-2175-4481-bc84-7c55a456b7ea"]); - }), - ); - - const prompt = ChatPromptTemplate.fromTemplate(`What is 1 + {number}?`); - const model = new ChatOpenAI({ - model: "gpt-4o-mini-2024-07-18", - }); - - const chain = prompt.pipe(model); - - const message = await chain.invoke( - { number: "2" }, - { callbacks: [handler] }, - ); - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - expect(spans).toMatchObject([ - { - span_id: root_span_id, - root_span_id: trace_id, - span_attributes: { - name: "RunnableSequence", - type: "task", - }, - input: { - number: "2", - }, - metadata: { - tags: [], - }, - output: { - kwargs: { - content: expect.any(String), // LLM response text - additional_kwargs: expect.any(Object), - response_metadata: expect.any(Object), - tool_calls: expect.any(Array), - invalid_tool_calls: expect.any(Array), - usage_metadata: expect.any(Object), - }, - }, - }, - { - span_attributes: { - name: "ChatPromptTemplate", - }, - input: { - number: "2", - }, - metadata: { - tags: ["seq:step:1"], - }, - output: { - kwargs: { - messages: [ - { - kwargs: { - content: expect.any(String), // Formatted prompt text - additional_kwargs: {}, - response_metadata: {}, - }, - }, - ], - }, - }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - { - span_attributes: { - name: "ChatOpenAI", - type: "llm", - }, - input: [ - [ - { - kwargs: { - content: expect.any(String), // Prompt message content - additional_kwargs: {}, - response_metadata: {}, - }, - }, - ], - ], - metrics: { - start: expect.any(Number), - tokens: expect.any(Number), - prompt_tokens: expect.any(Number), - completion_tokens: expect.any(Number), - end: expect.any(Number), - }, - metadata: { - tags: ["seq:step:2"], - model: "gpt-4o-mini-2024-07-18", - }, - output: { - generations: [ - [ - { - text: expect.any(String), // Generated text - message: { - kwargs: { - content: expect.any(String), // Message content - additional_kwargs: expect.any(Object), - response_metadata: expect.any(Object), - tool_calls: expect.any(Array), - invalid_tool_calls: expect.any(Array), - usage_metadata: expect.any(Object), - }, - }, - }, - ], - ], - llmOutput: { - tokenUsage: { - promptTokens: expect.any(Number), - completionTokens: expect.any(Number), - totalTokens: expect.any(Number), - }, - }, - }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - ]); - - expect(message.content).toEqual(expect.stringContaining("3")); - }); -}); - -it("should handle streaming LLM calls", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post("https://api.openai.com/v1/chat/completions", async () => { - const stream = new ReadableStream({ - start(controller) { - const chunks = CHAT_STREAM_PARROT; - - for (const chunk of chunks) { - controller.enqueue(encoder.encode(chunk + "\n\n")); - } - controller.close(); - }, - }); - - return new Response(stream, { - headers: { - "Content-Type": "text/event-stream; charset=utf-8", - "Transfer-Encoding": "chunked", - }, - }); - }), - - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["stream-span-id"]); - }), - ); - - const prompt = ChatPromptTemplate.fromTemplate( - `tell me a four word joke about {topic}`, - ); - const model = new ChatOpenAI({ - model: "gpt-4o-mini-2024-07-18", - streaming: true, - }); - - const chain = prompt.pipe(model); - - const stream = await chain.stream( - { topic: "parrot" }, - { callbacks: [handler] }, - ); - - const chunks = []; - for await (const chunk of stream) { - chunks.push(chunk); - } - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - expect(spans).toMatchObject([ - { - span_attributes: { - name: "RunnableSequence", - type: "task", - }, - input: { - topic: "parrot", - }, - metadata: { - tags: [], - }, - span_id: root_span_id, - root_span_id: trace_id, - }, - { - span_attributes: { name: "ChatPromptTemplate", type: "task" }, - input: { topic: "parrot" }, - output: expect.objectContaining({ - kwargs: expect.objectContaining({ - messages: expect.arrayContaining([ - expect.objectContaining({ - kwargs: expect.objectContaining({ - content: "tell me a four word joke about parrot", - }), - }), - ]), - }), - }), - metadata: { tags: ["seq:step:1"] }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - { - span_attributes: { name: "ChatOpenAI", type: "llm" }, - input: expect.arrayContaining([ - expect.arrayContaining([ - expect.objectContaining({ - kwargs: expect.objectContaining({ - content: "tell me a four word joke about parrot", - }), - }), - ]), - ]), - output: expect.objectContaining({ - generations: expect.arrayContaining([ - expect.arrayContaining([ - expect.objectContaining({ - message: expect.objectContaining({ - kwargs: expect.objectContaining({ - content: "Polly wants more crackers!", - }), - }), - }), - ]), - ]), - }), - metrics: { - completion_tokens: 6, - end: expect.any(Number), - prompt_tokens: 16, - start: expect.any(Number), - tokens: 22, - }, - metadata: { - tags: ["seq:step:2"], - model: "gpt-4o-mini-2024-07-18", - }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - ]); - - expect(chunks.length).toBeGreaterThan(0); -}); - -it("should track time-to-first-token in streaming calls", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post("https://api.openai.com/v1/chat/completions", async () => { - const stream = new ReadableStream({ - start(controller) { - const chunks = CHAT_STREAM_PARROT; - - for (const chunk of chunks) { - controller.enqueue(encoder.encode(chunk + "\n\n")); - } - controller.close(); - }, - }); - - return new Response(stream, { - headers: { - "Content-Type": "text/event-stream; charset=utf-8", - "Transfer-Encoding": "chunked", - }, - }); - }), - - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["stream-span-id"]); - }), - ); - - const prompt = ChatPromptTemplate.fromTemplate("Count from 1 to 5."); - const model = new ChatOpenAI({ - model: "gpt-4o-mini", - streaming: true, - }); - - const chain = prompt.pipe(model); - - const chunks = []; - const stream = await chain.stream({}, { callbacks: [handler] }); - for await (const chunk of stream) { - chunks.push(chunk); - } - - // Verify we got streaming chunks - expect(chunks.length).toBeGreaterThan(0); - - await flush(); - - const { spans } = logsToSpans(logs); - - // Find the LLM span - const llmSpan = spans.find( - (s) => - s.span_attributes?.name === "ChatOpenAI" && - s.span_attributes?.type === "llm", - ); - - expect(llmSpan).toBeDefined(); - expect(llmSpan?.metrics).toMatchObject({ - time_to_first_token: expect.any(Number), - prompt_tokens: expect.any(Number), - completion_tokens: expect.any(Number), - tokens: expect.any(Number), - }); - - // Verify TTFT is a reasonable value (positive and less than total time) - expect(llmSpan?.metrics?.time_to_first_token).toBeGreaterThan(0); -}); - -it("should handle multi-step chains with memory", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post("https://api.openai.com/v1/chat/completions", () => { - return HttpResponse.json(CHAT_CHAIN_MEMORY); - }), - - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["memory-span-id"]); - }), - ); - - const prompt = ChatPromptTemplate.fromTemplate(`{history} User: {input}`); - const model = new ChatOpenAI({ - model: "gpt-4o-mini-2024-07-18", - }); - - const chain = prompt.pipe(model); - - const memory = { history: "Assistant: Hello! How can I assist you today?" }; - await chain.invoke( - { input: "What's your name?", ...memory }, - { callbacks: [handler], tags: ["test"] }, - ); - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - debugger; - - expect(spans).toMatchObject([ - { - span_attributes: { - name: "RunnableSequence", - type: "task", - }, - input: { - history: "Assistant: Hello! How can I assist you today?", - input: "What's your name?", - }, - metadata: { - tags: ["test"], - }, - span_id: root_span_id, - root_span_id: trace_id, - }, - { - span_attributes: { name: "ChatPromptTemplate", type: "task" }, - input: { - history: "Assistant: Hello! How can I assist you today?", - input: "What's your name?", - }, - output: expect.objectContaining({ - kwargs: expect.objectContaining({ - messages: expect.arrayContaining([ - expect.objectContaining({ - kwargs: expect.objectContaining({ - content: - "Assistant: Hello! How can I assist you today? User: What's your name?", - }), - }), - ]), - }), - }), - metadata: { tags: ["seq:step:1", "test"] }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - { - span_attributes: { name: "ChatOpenAI", type: "llm" }, - input: expect.arrayContaining([ - expect.arrayContaining([ - expect.objectContaining({ - kwargs: expect.objectContaining({ - content: - "Assistant: Hello! How can I assist you today? User: What's your name?", - }), - }), - ]), - ]), - output: expect.objectContaining({ - generations: expect.arrayContaining([ - expect.arrayContaining([ - expect.objectContaining({ - message: expect.objectContaining({ - kwargs: expect.objectContaining({ - content: expect.stringContaining("Assistant"), - }), - }), - }), - ]), - ]), - }), - metrics: { - completion_tokens: expect.any(Number), - end: expect.any(Number), - prompt_tokens: expect.any(Number), - start: expect.any(Number), - tokens: expect.any(Number), - }, - metadata: { - tags: ["seq:step:2", "test"], - model: "gpt-4o-mini-2024-07-18", - }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - ]); -}); - -it("should handle tool/agent usage", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post("https://api.openai.com/v1/chat/completions", async () => { - return HttpResponse.json(CHAT_TOOL_CALCULATOR); - }), - - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["tool-span-id"]); - }), - ); - - const llm = new ChatOpenAI({ - model: "gpt-4o-mini-2024-07-18", - }); - - const calculatorSchema = z.object({ - operation: z - .enum(["add", "subtract", "multiply", "divide"]) - .describe("The type of operation to execute."), - number1: z.number().describe("The first number to operate on."), - number2: z.number().describe("The second number to operate on."), - }); - - const calculatorTool = tool( - ({ operation, number1, number2 }) => { - // Functions must return strings - if (operation === "add") { - return `${number1 + number2}`; - } else if (operation === "subtract") { - return `${number1 - number2}`; - } else if (operation === "multiply") { - return `${number1 * number2}`; - } else if (operation === "divide") { - return `${number1 / number2}`; - } else { - throw new Error("Invalid operation."); - } - }, - { - name: "calculator", - description: "Can perform mathematical operations.", - schema: calculatorSchema, - }, - ); - - const llmWithTools = llm.bindTools([calculatorTool]); - - await llmWithTools.invoke("What is 3 * 12", { - callbacks: [handler], - }); - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - debugger; - - expect(spans).toMatchObject([ - { - span_attributes: { - name: "ChatOpenAI", - type: "llm", - }, - input: expect.arrayContaining([ - expect.arrayContaining([ - expect.objectContaining({ - kwargs: expect.objectContaining({ - content: "What is 3 * 12", - }), - }), - ]), - ]), - output: expect.objectContaining({ - generations: expect.arrayContaining([ - expect.arrayContaining([ - expect.objectContaining({ - message: expect.objectContaining({ - kwargs: expect.objectContaining({ - tool_calls: expect.arrayContaining([ - expect.objectContaining({ - name: "calculator", - args: { - operation: "multiply", - number1: 3, - number2: 12, - }, - }), - ]), - }), - }), - }), - ]), - ]), - }), - metrics: { - completion_tokens: expect.any(Number), - end: expect.any(Number), - prompt_tokens: expect.any(Number), - start: expect.any(Number), - tokens: expect.any(Number), - }, - metadata: { - model: "gpt-4o-mini-2024-07-18", - tags: [], - }, - span_id: root_span_id, - root_span_id: trace_id, - }, - ]); -}); - -it("should handle parallel runnable execution", async () => { - const logs: LogsRequest[] = []; - - const calls = [ - HttpResponse.json(CHAT_BEAR_JOKE), - HttpResponse.json(CHAT_BEAR_POEM), - ]; - - server.use( - http.post("https://api.openai.com/v1/chat/completions", () => { - return calls.shift() || HttpResponse.json({ ok: false }, { status: 500 }); - }), - - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["parallel-span-id"]); - }), - ); - - const model = new ChatOpenAI({ model: "gpt-4o-mini-2024-07-18" }); - - const jokeChain = PromptTemplate.fromTemplate( - "Tell me a joke about {topic}", - ).pipe(model); - const poemChain = PromptTemplate.fromTemplate( - "write a 2-line poem about {topic}", - ).pipe(model); - - const mapChain = RunnableMap.from({ - joke: jokeChain, - poem: poemChain, - }); - - await mapChain.invoke({ topic: "bear" }, { callbacks: [handler] }); - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - // Check that we have the expected structure - expect(spans).toMatchObject([ - { - span_attributes: { - name: "RunnableMap", - type: "task", - }, - input: { - input: { - topic: "bear", - }, - }, - metadata: { - tags: [], - }, - span_id: root_span_id, - root_span_id: trace_id, - }, - { - // Updated for LangGraph 1.x API using Annotation - span_attributes: { name: "RunnableSequence", type: "task" }, - input: { topic: "bear" }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - { - span_attributes: { name: "RunnableSequence", type: "task" }, - input: { topic: "bear" }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - // Additional spans for the prompts and models - expect.anything(), - expect.anything(), - expect.anything(), - expect.anything(), - ]); -}); - -it("should handle LangGraph state management", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post("https://api.openai.com/v1/chat/completions", async () => { - return HttpResponse.json(CHAT_SAY_HELLO); - }), - - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["graph-span-id"]); - }), - ); - - const GraphState = Annotation.Root({ - message: Annotation({ - reducer: (_, y) => y, - default: () => "", - }), - }); - - type State = typeof GraphState.State; - type StateUpdate = typeof GraphState.Update; - - const model = new ChatOpenAI({ - model: "gpt-4o-mini-2024-07-18", - callbacks: [handler], - }); - - async function sayHello(_: State): Promise { - const res = await model.invoke("Say hello"); - return { message: typeof res.content === "string" ? res.content : "" }; - } - - function sayBye(_: State): StateUpdate { - console.log(`From the 'sayBye' node: Bye world!`); - return {}; - } - - const graphBuilder = new StateGraph(GraphState) // Add our nodes to the Graph - .addNode("sayHello", sayHello) - .addNode("sayBye", sayBye) // Add the edges between nodes - .addEdge(START, "sayHello") - .addEdge("sayHello", "sayBye") - .addEdge("sayBye", END); - - const helloWorldGraph = graphBuilder.compile(); - - await helloWorldGraph.invoke({}, { callbacks: [handler] }); - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - expect(spans).toMatchObject([ - { - span_attributes: { - name: "LangGraph", - type: "task", - }, - input: {}, - span_id: root_span_id, - root_span_id: trace_id, - }, - { - span_attributes: { name: "sayHello", type: "task" }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - { - span_attributes: { name: "ChatOpenAI", type: "llm" }, - input: expect.arrayContaining([ - expect.arrayContaining([ - expect.objectContaining({ - kwargs: expect.objectContaining({ - content: "Say hello", - }), - }), - ]), - ]), - output: expect.objectContaining({ - generations: expect.anything(), - }), - root_span_id: trace_id, - span_parents: expect.anything(), - }, - { - span_attributes: { name: "sayBye", type: "task" }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - ]); -}); - -it("should have correctly typed constructor parameters", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post("https://api.openai.com/v1/chat/completions", () => { - return HttpResponse.json(CHAT_MATH); - }), - - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["4bc6305f-2175-4481-bc84-7c55a456b7ea"]); - }), - ); - - const handler = new BraintrustCallbackHandler({ - logger: NOOP_SPAN, - }); - - handler.handleLLMStart( - { - name: "test", - lc: 1, - type: "secret", - id: ["test"], - }, - ["test"], - "test", - "test", - ); - - await flush(); - - expect(logs).toEqual([]); -}); - -it("should handle chain inputs/outputs with null/undefined values", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["null-span-id"]); - }), - ); - - // Test chain with null/undefined inputs - await handler.handleChainStart( - { id: ["TestChain"], lc: 1, type: "not_implemented" }, - { input1: "value1", input2: null, input3: undefined }, - "run-1", - undefined, - ["test"], - ); - - await handler.handleChainEnd( - { output1: "value1", output2: null, output3: undefined }, - "run-1", - undefined, - ["test"], - ); - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - expect(spans).toMatchObject([ - { - span_attributes: { - name: "TestChain", - type: "task", - }, - input: { - input1: "value1", - input2: null, - }, - output: { - output1: "value1", - output2: null, - }, - metadata: { - tags: ["test"], - }, - span_id: root_span_id, - root_span_id: trace_id, - }, - ]); -}); - -it("should handle agent action and agent end callbacks", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["agent-span-id"]); - }), - ); - - const agentAction = { - tool: "calculator", - toolInput: { operation: "multiply", a: 3, b: 12 }, - log: "Invoking calculator with multiply 3 * 12", - }; - - const agentFinish = { - returnValues: { output: "The result is 36" }, - log: "Final answer: 36", - }; - - await handler.handleAgentAction(agentAction, "agent-run-1", undefined, [ - "agent-test", - ]); - - await handler.handleAgentEnd(agentFinish, "agent-run-1", undefined, [ - "agent-test", - ]); - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - expect(spans).toMatchObject([ - { - span_attributes: { - name: "calculator", - type: "llm", - }, - input: { - tool: "calculator", - toolInput: { operation: "multiply", a: 3, b: 12 }, - log: "Invoking calculator with multiply 3 * 12", - }, - output: { - returnValues: { output: "The result is 36" }, - log: "Final answer: 36", - }, - metadata: { - tags: ["agent-test"], - }, - span_id: root_span_id, - root_span_id: trace_id, - }, - ]); -}); - -it("should handle agent action with string toolInput", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["agent-string-span-id"]); - }), - ); - - const agentAction = { - tool: "search", - toolInput: "What is the weather in Paris?", - log: "Searching for weather information", - }; - - const agentFinish = { - returnValues: { output: "It is sunny and 22 degrees in Paris" }, - log: "Search complete", - }; - - await handler.handleAgentAction(agentAction, "agent-run-2", undefined, [ - "search-test", - ]); - - await handler.handleAgentEnd(agentFinish, "agent-run-2", undefined, [ - "search-test", - ]); - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - expect(spans).toMatchObject([ - { - span_attributes: { - name: "search", - type: "llm", - }, - input: { - tool: "search", - toolInput: "What is the weather in Paris?", - log: "Searching for weather information", - }, - output: { - returnValues: { output: "It is sunny and 22 degrees in Paris" }, - log: "Search complete", - }, - metadata: { - tags: ["search-test"], - }, - span_id: root_span_id, - root_span_id: trace_id, - }, - ]); -}); - -it("should handle nested agent action with parent run id", async () => { - const logs: LogsRequest[] = []; - - server.use( - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["nested-agent-span-id"]); - }), - ); - - await handler.handleChainStart( - { id: ["AgentExecutor"], lc: 1, type: "not_implemented" }, - { input: "Calculate 3 * 12" }, - "parent-run-1", - undefined, - ["agent-executor"], - ); - - const agentAction = { - tool: "calculator", - toolInput: { a: 3, b: 12, operation: "multiply" }, - log: "Using calculator tool", - }; - - await handler.handleAgentAction( - agentAction, - "child-agent-run", - "parent-run-1", - ["agent-action"], - ); - - const agentFinish = { - returnValues: { output: "36" }, - log: "Calculation complete", - }; - - await handler.handleAgentEnd(agentFinish, "child-agent-run", "parent-run-1", [ - "agent-action", - ]); - - await handler.handleChainEnd( - { output: "The answer is 36" }, - "parent-run-1", - undefined, - ["agent-executor"], - ); - - await flush(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - expect(spans.length).toBe(2); - - expect(spans[0]).toMatchObject({ - span_attributes: { - name: "AgentExecutor", - type: "task", - }, - input: { input: "Calculate 3 * 12" }, - output: { output: "The answer is 36" }, - metadata: { - tags: ["agent-executor"], - }, - span_id: root_span_id, - root_span_id: trace_id, - }); - - expect(spans[1]).toMatchObject({ - span_attributes: { - name: "calculator", - type: "llm", - }, - input: { - tool: "calculator", - toolInput: { a: 3, b: 12, operation: "multiply" }, - log: "Using calculator tool", - }, - output: { - returnValues: { output: "36" }, - log: "Calculation complete", - }, - metadata: { - tags: ["agent-action"], - }, - root_span_id: trace_id, - span_parents: [root_span_id], - }); -}); - -it("should extract prompt caching tokens from Anthropic response", async () => { - const logs: LogsRequest[] = []; - - const ANTHROPIC_RESPONSE_WITH_CACHE = { - model: "claude-sonnet-4-5-20250929", - id: "msg_01PKL7azhWzdzdXC5yojHG6V", - type: "message", - role: "assistant", - content: [ - { - type: "text", - text: 'The first type of testing mentioned is **Unit Testing**, which is described as "Testing individual components or functions in isolation."', - }, - ], - stop_reason: "end_turn", - stop_sequence: null, - usage: { - input_tokens: 14, - cache_creation_input_tokens: 2042, - cache_read_input_tokens: 0, - cache_creation: { - ephemeral_5m_input_tokens: 0, - ephemeral_1h_input_tokens: 0, - }, - output_tokens: 27, - service_tier: "standard", - }, - }; - - const ANTHROPIC_RESPONSE_WITH_CACHE_READ = { - model: "claude-sonnet-4-5-20250929", - id: "msg_01PjHqPkFstyG2Bcqum9XKD8", - type: "message", - role: "assistant", - content: [ - { - type: "text", - text: 'Based on the document:\n\n1. **First type of testing mentioned:** Unit Testing - described as "Testing individual components or functions in isolation"\n\n2. **Python testing frameworks mentioned:** \n - pytest (described as "Feature-rich testing framework")\n - unittest (described as "Built-in Python testing module")', - }, - ], - stop_reason: "end_turn", - stop_sequence: null, - usage: { - input_tokens: 23, - cache_creation_input_tokens: 0, - cache_read_input_tokens: 2042, - cache_creation: { - ephemeral_5m_input_tokens: 0, - ephemeral_1h_input_tokens: 0, - }, - output_tokens: 71, - service_tier: "standard", - }, - }; - - const anthropicResponses = [ - ANTHROPIC_RESPONSE_WITH_CACHE, - ANTHROPIC_RESPONSE_WITH_CACHE_READ, - ]; - - server.use( - http.post("https://api.anthropic.com/v1/messages", ({ request }) => { - const message = anthropicResponses.shift(); - return HttpResponse.json(message); - }), - - http.post(/.+logs.+/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - console.log(logs.slice(-1)[0]); - return HttpResponse.json(["cache-tokens-span-id"]); - }), - ); - - const model = new ChatAnthropic({ - model: "claude-sonnet-4-5-20250929", - anthropicApiKey: "test-key", - }); - - // Use a long system message to simulate cache-eligible content - const longText = ` -# Comprehensive Guide to Software Testing Methods - -Software testing is a critical component of the software development lifecycle. -This guide covers various testing methodologies, best practices, and tools. - -## Types of Testing -- Unit Testing: Testing individual components or functions in isolation -- Integration Testing: Testing how components work together -- End-to-End Testing: Testing the entire application flow - -## Python Testing Tools -- pytest: Feature-rich testing framework -- unittest: Built-in Python testing module -`.repeat(20); // Repeat to ensure enough tokens for caching - - const messages = [ - new SystemMessage({ - content: [ - { - type: "text", - text: longText, - cache_control: { type: "ephemeral" }, - }, - ], - }), - new HumanMessage("What is the first type of testing mentioned?"), - ]; - - // First invocation - should create cache - await model.invoke(messages, { callbacks: [handler] }); - - await flush(); - - const { spans: firstSpans } = logsToSpans(logs); - logs.length = 0; // Clear logs for next invocation - - const firstLlmSpan = firstSpans.find( - (s) => s.span_attributes?.type === "llm", - ); - - expect(firstLlmSpan).toBeDefined(); - expect(firstLlmSpan?.metrics).toMatchObject({ - prompt_tokens: 14, - completion_tokens: 27, - }); - - console.log(firstLlmSpan?.metrics); - - // After fix: should have cache creation tokens - expect(firstLlmSpan?.metrics?.prompt_cache_creation_tokens).toBe(2042); - - // Second invocation - should read from cache - await model.invoke( - [ - ...messages, - new HumanMessage("What testing framework is mentioned for Python?"), - ], - { callbacks: [handler] }, - ); - - await flush(); - - const { spans: secondSpans } = logsToSpans(logs); - - const secondLlmSpan = secondSpans.find( - (s) => s.span_attributes?.type === "llm", - ); - - expect(secondLlmSpan).toBeDefined(); - expect(secondLlmSpan?.metrics).toMatchObject({ - prompt_tokens: 23, - completion_tokens: 71, - }); - - // After fix: should have cache read tokens - expect(secondLlmSpan?.metrics?.prompt_cached_tokens).toBe(2042); -}); diff --git a/integrations/langchain-js/src/BraintrustCallbackHandler.ts b/integrations/langchain-js/src/BraintrustCallbackHandler.ts deleted file mode 100644 index fa88fea7e..000000000 --- a/integrations/langchain-js/src/BraintrustCallbackHandler.ts +++ /dev/null @@ -1,259 +0,0 @@ -import { BaseCallbackHandler } from "@langchain/core/callbacks/base"; -import { AgentAction, AgentFinish } from "@langchain/core/dist/agents"; -import { DocumentInterface } from "@langchain/core/dist/documents/document"; -import { Serialized } from "@langchain/core/dist/load/serializable"; -import { BaseMessage } from "@langchain/core/dist/messages/base"; -import { ChatResult, LLMResult } from "@langchain/core/dist/outputs"; -import { ChainValues } from "@langchain/core/dist/utils/types"; -import { RunnableConfig } from "@langchain/core/runnables"; -import { - BraintrustLangChainCallbackHandler, - type LangChainCallbackHandlerOptions, -} from "braintrust"; - -type BraintrustCallbackHandlerOptions = - LangChainCallbackHandlerOptions; - -/** - * A Braintrust tracer for LangChain.js that logs LLM calls, chains, and tools. - * - * @deprecated Use automatic LangChain instrumentation from `braintrust`, or - * `BraintrustLangChainCallbackHandler` from `braintrust` for manual callbacks. - * This package will stop being published after the next release. - */ -export class BraintrustCallbackHandler< - IsAsyncFlush extends boolean, -> extends BaseCallbackHandler { - name = "BraintrustCallbackHandler"; - private inner: BraintrustLangChainCallbackHandler; - - constructor( - options?: Partial>, - ) { - super(); - this.inner = new BraintrustLangChainCallbackHandler(options); - } - - handleLLMStart( - llm: Serialized, - prompts: string[], - runId: string, - parentRunId?: string, - extraParams?: { - options: RunnableConfig; - invocation_params?: Record; - batch_size: number; - cache?: boolean; - }, - tags?: string[], - metadata?: Record, - runName?: string, - ): Promise { - return this.inner.handleLLMStart( - llm, - prompts, - runId, - parentRunId, - extraParams, - tags, - metadata, - runName, - ); - } - - handleLLMError( - err: Error, - runId: string, - parentRunId?: string, - tags?: string[], - ): Promise { - return this.inner.handleLLMError(err, runId, parentRunId, tags); - } - - handleLLMEnd( - output: LLMResult | ChatResult, - runId: string, - parentRunId?: string, - tags?: string[], - ): Promise { - return this.inner.handleLLMEnd(output, runId, parentRunId, tags); - } - - handleLLMNewToken( - token: string, - idx: { prompt: number; completion: number }, - runId: string, - parentRunId?: string, - tags?: string[], - ): Promise { - return this.inner.handleLLMNewToken(token, idx, runId, parentRunId, tags); - } - - handleChatModelStart( - llm: Serialized, - messages: BaseMessage[][], - runId: string, - parentRunId?: string, - extraParams?: { - options: RunnableConfig; - invocation_params?: Record; - batch_size: number; - cache?: boolean; - }, - tags?: string[], - metadata?: Record, - runName?: string, - ): Promise { - return this.inner.handleChatModelStart( - llm, - messages, - runId, - parentRunId, - extraParams, - tags, - metadata, - runName, - ); - } - - handleChainStart( - chain: Serialized, - inputs: ChainValues, - runId: string, - parentRunId?: string, - tags?: string[], - metadata?: Record, - runType?: string, - runName?: string, - ): Promise { - return this.inner.handleChainStart( - chain, - inputs, - runId, - parentRunId, - tags, - metadata, - runType, - runName, - ); - } - - handleChainError( - err: Error, - runId: string, - parentRunId?: string, - tags?: string[], - kwargs?: { inputs?: Record }, - ): Promise { - return this.inner.handleChainError(err, runId, parentRunId, tags, kwargs); - } - - handleChainEnd( - outputs: ChainValues, - runId: string, - parentRunId?: string, - tags?: string[], - kwargs?: { inputs?: Record }, - ): Promise { - return this.inner.handleChainEnd(outputs, runId, parentRunId, tags, kwargs); - } - - handleToolStart( - tool: Serialized, - input: string, - runId: string, - parentRunId?: string, - tags?: string[], - metadata?: Record, - runName?: string, - ): Promise { - return this.inner.handleToolStart( - tool, - input, - runId, - parentRunId, - tags, - metadata, - runName, - ); - } - - handleToolError( - err: Error, - runId: string, - parentRunId?: string, - tags?: string[], - ): Promise { - return this.inner.handleToolError(err, runId, parentRunId, tags); - } - - handleToolEnd( - output: unknown, - runId: string, - parentRunId?: string, - tags?: string[], - ): Promise { - return this.inner.handleToolEnd(output, runId, parentRunId, tags); - } - - handleAgentAction( - action: AgentAction, - runId: string, - parentRunId?: string, - tags?: string[], - ): Promise { - return this.inner.handleAgentAction( - action as unknown as Record, - runId, - parentRunId, - tags, - ); - } - - handleAgentEnd( - action: AgentFinish, - runId: string, - parentRunId?: string, - tags?: string[], - ): Promise { - return this.inner.handleAgentEnd(action, runId, parentRunId, tags); - } - - handleRetrieverStart( - retriever: Serialized, - query: string, - runId: string, - parentRunId?: string, - tags?: string[], - metadata?: Record, - name?: string, - ): Promise { - return this.inner.handleRetrieverStart( - retriever, - query, - runId, - parentRunId, - tags, - metadata, - name, - ); - } - - handleRetrieverEnd( - documents: DocumentInterface[], - runId: string, - parentRunId?: string, - tags?: string[], - ): Promise { - return this.inner.handleRetrieverEnd(documents, runId, parentRunId, tags); - } - - handleRetrieverError( - err: Error, - runId: string, - parentRunId?: string, - tags?: string[], - ): Promise { - return this.inner.handleRetrieverError(err, runId, parentRunId, tags); - } -} diff --git a/integrations/langchain-js/src/index.ts b/integrations/langchain-js/src/index.ts deleted file mode 100644 index ad7af8003..000000000 --- a/integrations/langchain-js/src/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { BraintrustCallbackHandler } from "./BraintrustCallbackHandler"; -export { setGlobalHandler } from "./setGlobalHandler"; diff --git a/integrations/langchain-js/src/setGlobalHandler.test.ts b/integrations/langchain-js/src/setGlobalHandler.test.ts deleted file mode 100644 index 42cdffb96..000000000 --- a/integrations/langchain-js/src/setGlobalHandler.test.ts +++ /dev/null @@ -1,138 +0,0 @@ -import { CallbackManager } from "@langchain/core/callbacks/manager"; -import { ChatPromptTemplate } from "@langchain/core/prompts"; -import { ChatOpenAI } from "@langchain/openai"; -import { flush as flushBraintrustLogs } from "braintrust"; -import { http, HttpResponse } from "msw"; -import { afterEach, describe, expect, it } from "vitest"; -import { BraintrustCallbackHandler } from "./BraintrustCallbackHandler"; -import { CHAT_MATH } from "./BraintrustCallbackHandler.fixtures"; -import { clearGlobalHandler, setGlobalHandler } from "./setGlobalHandler"; -import { server } from "./test/setup"; -import { LogsRequest } from "./test/types"; -import { logsToSpans, withLogging } from "./test/utils"; - -const handler = withLogging(new BraintrustCallbackHandler({ debug: true })); - -describe("setGlobalHandler", () => { - afterEach(() => { - clearGlobalHandler(); - }); - - it("should register the BraintrustCallbackHandler", async () => { - setGlobalHandler(handler); - - // Make sure the handler is registered in the LangChain.js library. - const manager = CallbackManager.configure(); - expect( - manager?.handlers.filter( - (handler) => handler instanceof BraintrustCallbackHandler, - )[0], - ).toBe(handler); - - const logs: LogsRequest[] = []; - - // Intercept calls to confirm our tracing is working. - server.use( - http.post("https://api.openai.com/v1/chat/completions", () => { - return HttpResponse.json(CHAT_MATH); - }), - - http.post(/.+logs/, async ({ request }) => { - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - logs.push((await request.json()) as LogsRequest); - return HttpResponse.json(["4bc6305f-2175-4481-bc84-7c55a456b7ea"]); - }), - ); - - // Here's what a typical user would do. - const prompt = ChatPromptTemplate.fromTemplate(`What is 1 + {number}?`); - const model = new ChatOpenAI({ - model: "gpt-4o-mini-2024-07-18", - }); - - const chain = prompt.pipe(model); - - const message = await chain.invoke({ number: "2" }); - - // Not normally needed by users, but we need it for our tests. - await flushBraintrustLogs(); - - const { spans, trace_id, root_span_id } = logsToSpans(logs); - - // Spans would be empty if the handler was not registered, let's make sure it logged what we expect. - expect(spans).toMatchObject([ - { - span_attributes: { - name: "RunnableSequence", - type: "task", - }, - input: { - number: "2", - }, - metadata: { - tags: [], - }, - span_id: root_span_id, - root_span_id: trace_id, - }, - { - span_attributes: { name: "ChatPromptTemplate", type: "task" }, - input: { number: "2" }, - output: expect.objectContaining({ - kwargs: expect.objectContaining({ - messages: expect.arrayContaining([ - expect.objectContaining({ - kwargs: expect.objectContaining({ - content: "What is 1 + 2?", - }), - }), - ]), - }), - }), - metadata: { tags: ["seq:step:1"] }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - { - span_attributes: { name: "ChatOpenAI", type: "llm" }, - input: expect.arrayContaining([ - expect.arrayContaining([ - expect.objectContaining({ - kwargs: expect.objectContaining({ - content: "What is 1 + 2?", - }), - }), - ]), - ]), - output: expect.objectContaining({ - generations: expect.arrayContaining([ - expect.arrayContaining([ - expect.objectContaining({ - message: expect.objectContaining({ - kwargs: expect.objectContaining({ - content: "1 + 2 equals 3.", - }), - }), - }), - ]), - ]), - }), - metrics: { - completion_tokens: 8, - end: expect.any(Number), - prompt_tokens: 15, - start: expect.any(Number), - tokens: 23, - }, - metadata: { - tags: ["seq:step:2"], - model: "gpt-4o-mini-2024-07-18", - }, - root_span_id: trace_id, - span_parents: [root_span_id], - }, - ]); - - expect(message.content).toBe("1 + 2 equals 3."); - }); -}); diff --git a/integrations/langchain-js/src/setGlobalHandler.ts b/integrations/langchain-js/src/setGlobalHandler.ts deleted file mode 100644 index e109d5e91..000000000 --- a/integrations/langchain-js/src/setGlobalHandler.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { BaseCallbackHandler } from "@langchain/core/callbacks/base"; -import { - registerConfigureHook, - setContextVariable, -} from "@langchain/core/context"; - -const BT_HANDLER = "BT_HANDLER"; - -/** - * @deprecated Use automatic LangChain instrumentation from `braintrust` for - * application-wide coverage. This package will stop being published after the - * next release. - */ -export const setGlobalHandler = (handler: BaseCallbackHandler) => { - setContextVariable(BT_HANDLER, handler); - - registerConfigureHook({ - contextVar: BT_HANDLER, - }); -}; - -export const clearGlobalHandler = () => { - setContextVariable(BT_HANDLER, undefined); -}; diff --git a/integrations/langchain-js/src/test/setup.ts b/integrations/langchain-js/src/test/setup.ts deleted file mode 100644 index 440a1435b..000000000 --- a/integrations/langchain-js/src/test/setup.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { http, HttpResponse } from "msw"; -import { setupServer } from "msw/node"; -import { afterAll, afterEach, beforeAll } from "vitest"; - -export const server = setupServer( - http.post(/.+\/api\/apikey\/login/, () => { - return HttpResponse.json({ - org_info: [ - { - id: "abb9f3e4-7fdd-4ccc-af40-f7e894fd4125", - name: "braintrustdata.com", - api_url: "http://0.0.0.0:8000", - git_metadata: null, - is_universal_api: null, - proxy_url: "http://0.0.0.0:8000", - realtime_url: "ws://0.0.0.0:8788", - }, - ], - }); - }), - - http.post(/.+\/api\/project\/register/, () => { - return HttpResponse.json({ - project: { - id: "209220fc-d3bd-4fab-b1a4-af5827d69200", - org_id: "abb9f3e4-7fdd-4ccc-af40-f7e894fd4125", - name: "Global", - created: "2024-12-04T16:14:11.122Z", - deleted_at: null, - user_id: "fac36c53-c882-458b-bf80-60d06c3e8a0d", - settings: null, - }, - }); - }), -); - -beforeAll(() => { - process.env.BRAINTRUST_API_KEY = "braintrust-api-key"; - process.env.BRAINTRUST_APP_URL = "http://braintrust.local"; - process.env.BRAINTRUST_ORG_NAME = "braintrustdata.com"; - process.env.OPENAI_API_KEY = "openai-api-key"; - - server.listen({ - onUnhandledRequest: (req) => { - throw new Error(`Unhandled request ${req.method}, ${req.url}`); - }, - }); -}); - -afterEach(() => server.resetHandlers()); -afterAll(() => server.close()); diff --git a/integrations/langchain-js/src/test/types.ts b/integrations/langchain-js/src/test/types.ts deleted file mode 100644 index 10b42c2df..000000000 --- a/integrations/langchain-js/src/test/types.ts +++ /dev/null @@ -1,32 +0,0 @@ -type SpanAttributes = { - name: string; - exec_counter: number; - type?: string; -}; - -type Metadata = { - tags: string[]; - params: Record; - [key: string]: unknown; -}; - -type Output = { - parsed: string; - raw: Record; -}; - -type LogRow = { - span_attributes?: SpanAttributes; - input?: Record; - metadata?: Metadata; - output?: Output; - metrics?: Record; - span_id: string; - root_span_id: string; - span_parents: string[]; -}; - -export type LogsRequest = { - rows: LogRow[]; - api_version: number; -}; diff --git a/integrations/langchain-js/src/test/utils.ts b/integrations/langchain-js/src/test/utils.ts deleted file mode 100644 index 823dc5b71..000000000 --- a/integrations/langchain-js/src/test/utils.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { mergeDicts } from "braintrust/util"; -import { BaseCallbackHandler } from "@langchain/core/callbacks/base"; -import { LogsRequest } from "./types"; - -export const logsToSpans = (logs: LogsRequest[]) => { - if (logs.length === 0) { - throw new Error("No logs to convert to spans"); - } - - // Logs include partial updates (merges) for previous rows. - // We need to dedupe these and merge them. So we can see the final state like - // we do in the UI. - - const seenIds = new Set(); - const spans = logs - .flatMap((log) => log.rows) - .reduce( - (acc, row) => { - if (!seenIds.has(row.span_id)) { - seenIds.add(row.span_id); - acc.push(row); - } else { - const existingSpan = acc.find((span) => span.span_id === row.span_id); - if (existingSpan) { - mergeDicts(existingSpan, row); - } - } - return acc; - }, - // eslint-disable-next-line @typescript-eslint/consistent-type-assertions - [] as LogsRequest["rows"], - ); - - return { - spans, - trace_id: spans[0].root_span_id, - root_span_id: spans[0].span_id, - root_run_id: spans[0].metadata?.runId, - }; -}; - -export const withLogging = (handler: T): T => { - if (process.env.VERBOSE === "false") { - return handler; - } - - let depth = 0; - return new Proxy(handler, { - get(target, prop) { - // @ts-expect-error - const originalMethod = target[prop]; - if (typeof originalMethod === "function") { - // @ts-expect-error - return function (...args) { - const propString = String(prop); - if (!["startSpan", "endSpan"].includes(propString)) { - console.log(new Array(depth * 2).join(" ") + propString); - - if (propString.endsWith("Start")) { - depth++; - } else if (propString.endsWith("End")) { - depth = Math.max(0, depth - 1); - } - } - // @ts-expect-error - return originalMethod.apply(this, args); - }; - } - return originalMethod; - }, - }); -}; diff --git a/integrations/langchain-js/tsconfig.json b/integrations/langchain-js/tsconfig.json deleted file mode 100644 index ea6ad1861..000000000 --- a/integrations/langchain-js/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "declaration": true, - "lib": ["es2022"], - "module": "commonjs", - "target": "es2022", - "moduleResolution": "node", - "strict": true, - "noUnusedLocals": true, - "esModuleInterop": true, - "skipLibCheck": true - }, - "include": ["."], - "exclude": ["node_modules/**", "**/dist/**"] -} diff --git a/integrations/langchain-js/tsup.config.ts b/integrations/langchain-js/tsup.config.ts deleted file mode 100644 index 4c5a7bd9b..000000000 --- a/integrations/langchain-js/tsup.config.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { defineConfig } from "tsup"; - -export default defineConfig([ - { - entry: ["src/index.ts"], - format: ["cjs", "esm"], - outDir: "dist", - external: ["braintrust", "@langchain/core", "@langchain/langgraph"], - dts: true, - }, -]); diff --git a/integrations/langchain-js/turbo.json b/integrations/langchain-js/turbo.json deleted file mode 100644 index 3e50dc890..000000000 --- a/integrations/langchain-js/turbo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "build": { - "outputs": ["**/dist/**"] - } - } -} diff --git a/integrations/langchain-js/vitest.config.ts b/integrations/langchain-js/vitest.config.ts deleted file mode 100644 index 9c517c86c..000000000 --- a/integrations/langchain-js/vitest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - reporters: ["default"], - setupFiles: ["./src/test/setup.ts"], - include: ["tests/**/*.test.ts", "src/**/*.test.ts"], - }, -}); diff --git a/integrations/openai-agents-js/CHANGELOG.md b/integrations/openai-agents-js/CHANGELOG.md deleted file mode 100644 index 39db538e6..000000000 --- a/integrations/openai-agents-js/CHANGELOG.md +++ /dev/null @@ -1,20 +0,0 @@ -# @braintrust/openai-agents - -## 0.1.6 - -- `@braintrust/openai-agents` is now deprecated. This release marks the last release for this package. (https://github.com/braintrustdata/braintrust-sdk-javascript/pull/2383) -- Updated dependencies: braintrust@3.29.0 - -### Migration - -To migrate, install `braintrust` and import `OpenAIAgentsTraceProcessor` from -`braintrust/instrumentation` instead. See the -[OpenAI Agents SDK integration guide](https://www.braintrust.dev/docs/integrations/agent-frameworks/openai-agents-sdk) -for setup examples. - -## 0.1.5 - -### Patch Changes - -- Ensured the root span is flushed during `onTraceEnd()` so traces are marked complete even in short-lived and serverless processes. -- Added test coverage around trace completion and root span flushing behavior. diff --git a/integrations/openai-agents-js/README.md b/integrations/openai-agents-js/README.md deleted file mode 100644 index fe0e3b011..000000000 --- a/integrations/openai-agents-js/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# @braintrust/openai-agents - -[![npm version](https://img.shields.io/npm/v/%40braintrust%2Fopenai-agents.svg)](https://www.npmjs.com/package/@braintrust/openai-agents) - -SDK for integrating Braintrust with OpenAI Agents. - -> [!WARNING] -> This package is deprecated and will stop being published after the next -> release. Use the OpenAI Agents integration in `braintrust` instead. - -## Installation - -```bash -npm install braintrust @openai/agents -# or -yarn add braintrust @openai/agents -# or -pnpm add braintrust @openai/agents -``` - -## Requirements - -- `braintrust` -- `@openai/agents` - -## Quickstart - -```typescript -import { initLogger } from "braintrust"; -import { OpenAIAgentsTraceProcessor } from "braintrust/instrumentation"; -import { Agent, run, addTraceProcessor } from "@openai/agents"; - -// Initialize Braintrust logger -const logger = initLogger({ - projectName: "my-agents-project", -}); - -// Create the tracing processor -const processor = new OpenAIAgentsTraceProcessor({ logger }); - -// Add the processor to OpenAI Agents -addTraceProcessor(processor); - -// Create and run your agent -const agent = new Agent({ - name: "my-agent", - model: "gpt-4o-mini", - instructions: "You are a helpful assistant.", -}); - -const result = await run(agent, "Hello, world!"); -``` - -## API - -### `OpenAIAgentsTraceProcessor` - -A tracing processor that logs traces from the OpenAI Agents SDK to Braintrust. - -#### Constructor - -```typescript -new OpenAIAgentsTraceProcessor(options?: OpenAIAgentsTraceProcessorOptions) -``` - -**Options:** - -- `logger?: Logger` - A Braintrust `Span`, `Experiment`, or `Logger` to use for logging. If undefined, the current span, experiment, or logger will be selected exactly as in `startSpan`. -- `maxTraces?: number` - Maximum number of concurrent traces to keep in memory (default: 10000). When exceeded, oldest traces are evicted using LRU policy to prevent memory leaks. - -## Documentation - -- Braintrust docs: [https://www.braintrust.dev/docs](https://www.braintrust.dev/docs) -- OpenAI Agents docs: [https://openai.github.io/openai-agents-js/](https://openai.github.io/openai-agents-js/) diff --git a/integrations/openai-agents-js/package.json b/integrations/openai-agents-js/package.json deleted file mode 100644 index f9f57ea10..000000000 --- a/integrations/openai-agents-js/package.json +++ /dev/null @@ -1,50 +0,0 @@ -{ - "name": "@braintrust/openai-agents", - "version": "0.1.6", - "description": "SDK for integrating Braintrust with OpenAI Agents", - "main": "./dist/index.js", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "module": "./dist/index.mjs", - "require": "./dist/index.js" - } - }, - "scripts": { - "build": "tsup", - "check:typings": "tsc --noEmit", - "watch": "tsup --watch", - "clean": "rm -r dist/*", - "test": "vitest run", - "yalc:publish": "yalc publish" - }, - "author": "Braintrust Data Inc.", - "license": "MIT", - "devDependencies": { - "@openai/agents": "^0.0.15", - "@types/node": "^20.10.5", - "braintrust": "workspace:^", - "tsup": "^8.5.1", - "typescript": "^5.3.3", - "vitest": "4.1.5", - "zod": "^3.25.34" - }, - "peerDependencies": { - "braintrust": ">=0.4.0" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/braintrustdata/braintrust-sdk-javascript.git", - "directory": "integrations/openai-agents-js" - }, - "homepage": "https://www.braintrust.dev/docs", - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/", - "provenance": true - } -} diff --git a/integrations/openai-agents-js/src/index.ts b/integrations/openai-agents-js/src/index.ts deleted file mode 100644 index e40b95173..000000000 --- a/integrations/openai-agents-js/src/index.ts +++ /dev/null @@ -1,673 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import { SpanTypeAttribute } from "braintrust/util"; -import { - Span as BraintrustSpan, - startSpan, - Logger, - currentSpan, - NOOP_SPAN, - Attachment, -} from "braintrust"; -import { - SpanType, - AgentsTrace, - AgentsSpan, - SpanInput, - SpanOutput, - TraceMetadata, - OpenAIAgentsTraceProcessorOptions, - isResponseSpanData, - isGenerationSpanData, - isAgentSpanData, - isFunctionSpanData, - isHandoffSpanData, - isGuardrailSpanData, - isCustomSpanData, - isMCPListToolsSpanData, - isTranscriptionSpanData, - isSpeechSpanData, - isSpeechGroupSpanData, -} from "./types"; - -function spanTypeFromAgents(span: AgentsSpan): SpanTypeAttribute { - const spanType = span.spanData.type; - - if ( - spanType === SpanType.AGENT || - spanType === SpanType.HANDOFF || - spanType === SpanType.CUSTOM || - spanType === SpanType.SPEECH_GROUP - ) { - return SpanTypeAttribute.TASK; - } - - if ( - spanType === SpanType.FUNCTION || - spanType === SpanType.GUARDRAIL || - spanType === SpanType.MCP_TOOLS - ) { - return SpanTypeAttribute.TOOL; - } - - if ( - spanType === SpanType.GENERATION || - spanType === SpanType.RESPONSE || - spanType === SpanType.TRANSCRIPTION || - spanType === SpanType.SPEECH - ) { - return SpanTypeAttribute.LLM; - } - - return SpanTypeAttribute.TASK; -} - -function spanNameFromAgents(span: AgentsSpan): string { - const spanData = span.spanData; - - if ("name" in spanData && spanData.name) { - return spanData.name; - } - - switch (spanData.type) { - case SpanType.GENERATION: - return "Generation"; - case SpanType.RESPONSE: - return "Response"; - case SpanType.HANDOFF: - return "Handoff"; - case SpanType.MCP_TOOLS: - if (isMCPListToolsSpanData(spanData) && spanData.server) { - return `List Tools (${spanData.server})`; - } - return "MCP List Tools"; - case SpanType.TRANSCRIPTION: - return "Transcription"; - case SpanType.SPEECH: - return "Speech"; - case SpanType.SPEECH_GROUP: - return "Speech Group"; - case SpanType.AGENT: - case SpanType.FUNCTION: - case SpanType.GUARDRAIL: - case SpanType.CUSTOM: - return "name" in spanData && spanData.name ? spanData.name : "Unknown"; - default: - return "Unknown"; - } -} - -function getTimeElapsed(end?: string, start?: string): number | undefined { - if (!start || !end) return undefined; - const startTime = new Date(start).getTime(); - const endTime = new Date(end).getTime(); - if (isNaN(startTime) || isNaN(endTime)) return undefined; - return (endTime - startTime) / 1000; -} - -/** - * `OpenAIAgentsTraceProcessor` is a tracing processor that logs traces from the OpenAI Agents SDK to Braintrust. - * - * Args: - * options: Configuration options including: - * - logger: A `Span`, `Experiment`, or `Logger` to use for logging. - * If `undefined`, the current span, experiment, or logger will be selected exactly as in `startSpan`. - * - maxTraces: Maximum number of concurrent traces to keep in memory (default: 1000). - * When exceeded, oldest traces are evicted using LRU policy. - * - * @deprecated Import `OpenAIAgentsTraceProcessor` from `braintrust/instrumentation` instead. This package will stop being published after the next release. - * */ - -export class OpenAIAgentsTraceProcessor { - private static readonly DEFAULT_MAX_TRACES = 10000; - - private logger?: Logger; - private maxTraces: number; - private traceSpans = new Map< - string, - { - rootSpan: BraintrustSpan; - childSpans: Map; - metadata: TraceMetadata; - } - >(); - private traceOrder: string[] = []; - - // Expose for testing purposes - public readonly _traceSpans = this.traceSpans; - - constructor(options: OpenAIAgentsTraceProcessorOptions = {}) { - this.logger = options.logger; - this.maxTraces = - options.maxTraces ?? OpenAIAgentsTraceProcessor.DEFAULT_MAX_TRACES; - } - - private processInputImages(input: any): any { - if (Array.isArray(input)) { - return input.map((item) => this.processInputImages(item)); - } - - if (input && typeof input === "object") { - // Handle input_image type with base64 image data - if (input.type === "input_image" && typeof input.image === "string") { - let imageData = input.image; - - // Strip data URI prefix if present (e.g., "data:image/png;base64,") - const dataUriMatch = imageData.match(/^data:image\/(\w+);base64,(.*)$/); - let contentType = "image/png"; - let fileExtension = "png"; - - if (dataUriMatch) { - fileExtension = dataUriMatch[1]; - contentType = `image/${fileExtension}`; - imageData = dataUriMatch[2]; // Extract just the base64 part - } - - const filename = `input_image.${fileExtension}`; - - try { - // Convert base64 string to Blob - const binaryString = atob(imageData); - const bytes = new Uint8Array(binaryString.length); - for (let i = 0; i < binaryString.length; i++) { - bytes[i] = binaryString.charCodeAt(i); - } - const blob = new Blob([bytes], { type: contentType }); - - const attachment = new Attachment({ - data: blob, - filename: filename, - contentType: contentType, - }); - - return { - ...input, - image: attachment, - }; - } catch (e) { - console.error("Failed to process input image:", e); - console.error("Image data sample:", input.image.substring(0, 200)); - return input; - } - } - - // Recursively process nested objects - const result: any = {}; - for (const [key, value] of Object.entries(input)) { - result[key] = this.processInputImages(value); - } - return result; - } - - return input; - } - - private processOutputImages(output: any): any { - if (Array.isArray(output)) { - return output.map((item) => this.processOutputImages(item)); - } - - if (output && typeof output === "object") { - // Handle image_generation_call type - convert result to attachment - if (output.type === "image_generation_call" && output.result) { - let resultData = output.result; - - // Use output_format from the response - const fileExtension = output.output_format || "png"; - const contentType = `image/${fileExtension}`; - - // Strip data URI prefix if present (e.g., "data:image/png;base64,") - const dataUriMatch = resultData.match(/^data:image\/\w+;base64,(.*)$/); - if (dataUriMatch) { - resultData = dataUriMatch[1]; // Extract just the base64 part - } - - const baseFilename = - output.revised_prompt && typeof output.revised_prompt === "string" - ? output.revised_prompt.slice(0, 50).replace(/[^a-zA-Z0-9]/g, "_") - : "generated_image"; - const filename = `${baseFilename}.${fileExtension}`; - - try { - // Convert base64 string to Blob - const binaryString = atob(resultData); - const bytes = new Uint8Array(binaryString.length); - for (let i = 0; i < binaryString.length; i++) { - bytes[i] = binaryString.charCodeAt(i); - } - const blob = new Blob([bytes], { type: contentType }); - - const attachment = new Attachment({ - data: blob, - filename: filename, - contentType: contentType, - }); - - return { - ...output, - result: attachment, - }; - } catch (e) { - console.error("Failed to process output image:", e); - console.error("Result data sample:", output.result.substring(0, 200)); - return output; - } - } - - // Recursively process nested objects - const result: any = {}; - for (const [key, value] of Object.entries(output)) { - result[key] = this.processOutputImages(value); - } - return result; - } - - return output; - } - - private evictOldestTrace(): void { - if (this.traceOrder.length === 0) return; - - const oldestTraceId = this.traceOrder.shift()!; - this.traceSpans.delete(oldestTraceId); - } - - onTraceStart(trace: AgentsTrace): Promise { - if (this.traceOrder.length >= this.maxTraces) { - this.evictOldestTrace(); - } - - // Detect parent span from current execution context - let span: BraintrustSpan; - const current = currentSpan(); - - if (current && current !== NOOP_SPAN) { - // Create as child of current span - span = current.startSpan({ - name: trace.name, - type: SpanTypeAttribute.TASK, - }); - } else { - // No parent span available, create as root - span = this.logger - ? this.logger.startSpan({ - name: trace.name, - type: SpanTypeAttribute.TASK, - }) - : startSpan({ - name: trace.name, - type: SpanTypeAttribute.TASK, - }); - } - - span.log({ - input: "Agent workflow started", - metadata: { - group_id: trace.groupId, - ...(trace.metadata || {}), - }, - }); - - this.traceSpans.set(trace.traceId, { - rootSpan: span, - childSpans: new Map(), - metadata: { - firstInput: null, - lastOutput: null, - }, - }); - this.traceOrder.push(trace.traceId); - - return Promise.resolve(); - } - - async onTraceEnd(trace: AgentsTrace): Promise { - const traceData = this.traceSpans.get(trace.traceId); - - if (traceData) { - for (const [spanId, braintrustSpan] of traceData.childSpans) { - braintrustSpan.end(); - traceData.childSpans.delete(spanId); - } - - try { - traceData.rootSpan.log({ - input: traceData.metadata.firstInput, - output: traceData.metadata.lastOutput, - }); - traceData.rootSpan.end(); - await traceData.rootSpan.flush(); - } finally { - this.traceSpans.delete(trace.traceId); - const orderIndex = this.traceOrder.indexOf(trace.traceId); - if (orderIndex > -1) { - this.traceOrder.splice(orderIndex, 1); - } - } - } - } - - private extractAgentLogData(span: AgentsSpan): Record { - const spanData = span.spanData; - if (!isAgentSpanData(spanData)) { - return {}; - } - - const data: Record = { - metadata: { - tools: spanData.tools, - handoffs: spanData.handoffs, - output_type: spanData.output_type, - }, - }; - - return data; - } - - private extractResponseLogData(span: AgentsSpan): Record { - const spanData = span.spanData; - const data: Record = {}; - - if (!isResponseSpanData(spanData)) { - return data; - } - - if (spanData._input !== undefined) { - data.input = this.processInputImages(spanData._input); - } - - if (spanData._response !== undefined) { - data.output = this.processOutputImages(spanData._response.output); - } - - if (spanData._response) { - const { output, metadata, usage, output_text, ...otherFields } = - spanData._response; - data.metadata = otherFields; - } - - data.metrics = {}; - const ttft = getTimeElapsed( - span.endedAt ?? undefined, - span.startedAt ?? undefined, - ); - if (ttft !== undefined) { - data.metrics.time_to_first_token = ttft; - } - - let usage: any = null; - if (spanData._response?.usage) { - usage = spanData._response.usage; - } - - if (usage) { - if (usage.total_tokens) data.metrics.tokens = usage.total_tokens; - if (usage.input_tokens) data.metrics.prompt_tokens = usage.input_tokens; - if (usage.output_tokens) - data.metrics.completion_tokens = usage.output_tokens; - - if (!data.metrics.tokens && usage.totalTokens) - data.metrics.tokens = usage.totalTokens; - if (!data.metrics.prompt_tokens && usage.inputTokens) - data.metrics.prompt_tokens = usage.inputTokens; - if (!data.metrics.prompt_tokens && usage.promptTokens) - data.metrics.prompt_tokens = usage.promptTokens; - if (!data.metrics.completion_tokens && usage.outputTokens) - data.metrics.completion_tokens = usage.outputTokens; - if (!data.metrics.completion_tokens && usage.completionTokens) - data.metrics.completion_tokens = usage.completionTokens; - if (usage.input_tokens_details?.cached_tokens != null) - data.metrics.prompt_cached_tokens = - usage.input_tokens_details.cached_tokens; - if (usage.input_tokens_details?.cache_write_tokens != null) - data.metrics.prompt_cache_creation_tokens = - usage.input_tokens_details.cache_write_tokens; - } - - return data; - } - - private extractFunctionLogData(span: AgentsSpan): Record { - const spanData = span.spanData; - if (!isFunctionSpanData(spanData)) { - return {}; - } - - return { - input: spanData.input, - output: spanData.output, - }; - } - - private extractHandoffLogData(span: AgentsSpan): Record { - const spanData = span.spanData; - if (!isHandoffSpanData(spanData)) { - return {}; - } - return { - metadata: { - from_agent: spanData.from_agent, - to_agent: spanData.to_agent, - }, - }; - } - - private extractGuardrailLogData(span: AgentsSpan): Record { - const spanData = span.spanData; - if (!isGuardrailSpanData(spanData)) { - return {}; - } - return { - metadata: { - triggered: spanData.triggered, - }, - }; - } - - private extractGenerationLogData(span: AgentsSpan): Record { - const spanData = span.spanData; - if (!isGenerationSpanData(spanData)) { - return {}; - } - - const metrics: Record = {}; - - const ttft = getTimeElapsed( - span.endedAt ?? undefined, - span.startedAt ?? undefined, - ); - if (ttft !== undefined) { - metrics.time_to_first_token = ttft; - } - - const usage = spanData.usage || {}; - if (usage.prompt_tokens) metrics.prompt_tokens = usage.prompt_tokens; - else if (usage.input_tokens) metrics.prompt_tokens = usage.input_tokens; - - if (usage.completion_tokens) - metrics.completion_tokens = usage.completion_tokens; - else if (usage.output_tokens) - metrics.completion_tokens = usage.output_tokens; - - if (usage.total_tokens) metrics.tokens = usage.total_tokens; - else if (usage.input_tokens && usage.output_tokens) { - metrics.tokens = usage.input_tokens + usage.output_tokens; - } - - if (usage.input_tokens_details?.cached_tokens != null) - metrics.prompt_cached_tokens = usage.input_tokens_details.cached_tokens; - - return { - input: spanData.input, - output: spanData.output, - metadata: { - model: spanData.model, - model_config: spanData.model_config, - }, - metrics, - }; - } - - private extractCustomLogData(span: AgentsSpan): Record { - const spanData = span.spanData; - if (!isCustomSpanData(spanData)) { - return {}; - } - return spanData.data || {}; - } - - private extractMCPListToolsLogData( - span: AgentsSpan, - ): Record { - const spanData = span.spanData; - if (!isMCPListToolsSpanData(spanData)) { - return {}; - } - return { - output: spanData.result, - metadata: { - server: spanData.server, - }, - }; - } - - private extractTranscriptionLogData( - span: AgentsSpan, - ): Record { - const spanData = span.spanData; - if (!isTranscriptionSpanData(spanData)) { - return {}; - } - return { - input: spanData.input, - output: spanData.output, - metadata: { - model: spanData.model, - model_config: spanData.model_config, - }, - }; - } - - private extractSpeechLogData(span: AgentsSpan): Record { - const spanData = span.spanData; - if (!isSpeechSpanData(spanData)) { - return {}; - } - return { - input: spanData.input, - output: spanData.output, - metadata: { - model: spanData.model, - model_config: spanData.model_config, - }, - }; - } - - private extractSpeechGroupLogData(span: AgentsSpan): Record { - const spanData = span.spanData; - if (!isSpeechGroupSpanData(spanData)) { - return {}; - } - return { - input: spanData.input, - }; - } - - private extractLogData(span: AgentsSpan): Record { - const spanType = span.spanData?.type; - - switch (spanType) { - case SpanType.AGENT: - return this.extractAgentLogData(span); - case SpanType.RESPONSE: - return this.extractResponseLogData(span); - case SpanType.FUNCTION: - return this.extractFunctionLogData(span); - case SpanType.HANDOFF: - return this.extractHandoffLogData(span); - case SpanType.GUARDRAIL: - return this.extractGuardrailLogData(span); - case SpanType.GENERATION: - return this.extractGenerationLogData(span); - case SpanType.CUSTOM: - return this.extractCustomLogData(span); - case SpanType.MCP_TOOLS: - return this.extractMCPListToolsLogData(span); - case SpanType.TRANSCRIPTION: - return this.extractTranscriptionLogData(span); - case SpanType.SPEECH: - return this.extractSpeechLogData(span); - case SpanType.SPEECH_GROUP: - return this.extractSpeechGroupLogData(span); - default: - return {}; - } - } - - onSpanStart(span: AgentsSpan): Promise { - if (!span.spanId || !span.traceId) return Promise.resolve(); - - const traceData = this.traceSpans.get(span.traceId); - if (!traceData) return Promise.resolve(); - - let parentSpan: BraintrustSpan | undefined; - if (span.parentId) { - parentSpan = traceData.childSpans.get(span.parentId); - } else { - parentSpan = traceData.rootSpan; - } - - if (parentSpan) { - const childSpan = parentSpan.startSpan({ - name: spanNameFromAgents(span), - type: spanTypeFromAgents(span), - }); - traceData.childSpans.set(span.spanId, childSpan); - } - return Promise.resolve(); - } - - onSpanEnd(span: AgentsSpan): Promise { - if (!span.spanId || !span.traceId) return Promise.resolve(); - - const traceData = this.traceSpans.get(span.traceId); - if (!traceData) return Promise.resolve(); - - const braintrustSpan = traceData.childSpans.get(span.spanId); - - if (braintrustSpan) { - const logData = this.extractLogData(span); - braintrustSpan.log({ - error: span.error, - ...logData, - }); - braintrustSpan.end(); - traceData.childSpans.delete(span.spanId); - - const input = logData.input as SpanInput; - const output = logData.output as SpanOutput; - - if (traceData.metadata.firstInput === null && input != null) { - traceData.metadata.firstInput = input; - } - - if (output != null) { - traceData.metadata.lastOutput = output; - } - } else { - console.warn(`No span found for ID: ${span.spanId}`); - } - return Promise.resolve(); - } - - async shutdown(): Promise { - if (this.logger && typeof this.logger.flush === "function") { - await this.logger.flush(); - } - } - - async forceFlush(): Promise { - if (this.logger && typeof this.logger.flush === "function") { - await this.logger.flush(); - } - } -} diff --git a/integrations/openai-agents-js/src/openai-agents-integration.test.ts b/integrations/openai-agents-js/src/openai-agents-integration.test.ts deleted file mode 100644 index 317d04b7e..000000000 --- a/integrations/openai-agents-js/src/openai-agents-integration.test.ts +++ /dev/null @@ -1,1262 +0,0 @@ -import { - test, - assert, - beforeEach, - beforeAll, - afterEach, - describe, -} from "vitest"; -import { OpenAIAgentsTraceProcessor } from "./index"; -import { z } from "zod/v3"; - -// Import necessary types and functions from braintrust -import { - _exportsForTestingOnly, - initLogger, - Logger, - TestBackgroundLogger, - Span as BraintrustSpan, - wrapTraced, - currentSpan, -} from "braintrust"; - -// Test helper functions for backward compatibility -function getSpansMap( - processor: OpenAIAgentsTraceProcessor, -): Map { - const spans = new Map(); - for (const [traceId, traceData] of processor._traceSpans) { - spans.set(traceId, traceData.rootSpan); - for (const [spanId, span] of traceData.childSpans) { - spans.set(`${traceId}:${spanId}`, span); - } - } - return spans; -} - -function getTraceMetadataMap(processor: OpenAIAgentsTraceProcessor) { - const metadata = new Map(); - for (const [traceId, traceData] of processor._traceSpans) { - metadata.set(traceId, traceData.metadata); - } - return metadata; -} - -const TEST_SUITE_OPTIONS = { timeout: 30000, retry: 3 }; - -// Simple test model for OpenAI Agents -const TEST_MODEL = "gpt-4o-mini"; - -// Node.js configuration is automatically handled when importing from braintrust - -// Test with real @openai/agents SDK calls (requires OPENAI_API_KEY) -describe( - "OpenAI Agents tracing processor integration tests", - TEST_SUITE_OPTIONS, - () => { - let backgroundLogger: TestBackgroundLogger; - let _logger: Logger; - let Agent: any; - let run: any; - let tool: any; - let setTraceProcessors: any; - let addTraceProcessor: any; - let setTracingDisabled: any; - - beforeAll(async () => { - await _exportsForTestingOnly.simulateLoginForTests(); - - // Dynamically import @openai/agents to handle cases where it's not available - try { - const agentsModule = await import("@openai/agents"); - Agent = agentsModule.Agent; - run = agentsModule.run; - tool = agentsModule.tool; - setTraceProcessors = agentsModule.setTraceProcessors; - addTraceProcessor = agentsModule.addTraceProcessor; - setTracingDisabled = agentsModule.setTracingDisabled; - } catch (error) { - console.warn( - "@openai/agents not available, skipping integration tests", - ); - } - }); - - beforeEach(() => { - backgroundLogger = _exportsForTestingOnly.useTestBackgroundLogger(); - _logger = initLogger({ - projectName: "openai-agents.test.ts", - projectId: "test-openai-agents", - }); - }); - - afterEach(() => { - if (_logger) { - _logger.flush(); - } - if (setTraceProcessors) { - setTraceProcessors([]); - } - _exportsForTestingOnly.clearTestBackgroundLogger(); - }); - - test("OpenAIAgentsTraceProcessor is instantiable", () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - assert.ok(processor); - - // Test methods exist - assert.ok(typeof processor.onTraceStart === "function"); - assert.ok(typeof processor.onTraceEnd === "function"); - assert.ok(typeof processor.onSpanStart === "function"); - assert.ok(typeof processor.onSpanEnd === "function"); - assert.ok(typeof processor.shutdown === "function"); - assert.ok(typeof processor.forceFlush === "function"); - }); - - test("simple agent run with tracing", async (context) => { - assert.lengthOf(await backgroundLogger.drain(), 0); - - // Set up the OpenAI Agents tracing processor - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - setTracingDisabled(false); - addTraceProcessor(processor); - - try { - // Create a simple agent - const agent = new Agent({ - name: "test-agent", - model: TEST_MODEL, - instructions: "You are a helpful assistant. Be concise.", - }); - - // Run the agent with a simple prompt using the run() function - const result = await run(agent, "What is 2+2? Just give the number."); - assert.ok(result); - assert.ok(result.finalOutput); - - // Verify spans were created - const spans = await backgroundLogger.drain(); - assert.isTrue( - spans.length > 0, - "Expected at least one span to be created", - ); - - // Verify span structure - const traceSpan = spans.find( - (s: any) => s.span_attributes?.type === "task", - ); - assert.ok( - traceSpan, - "Expected to find a task-type span for the agent trace", - ); - assert.equal((traceSpan as any).span_attributes.name, "Agent workflow"); - } finally { - processor.shutdown(); - } - }); - - test("agent with function calling", async (context) => { - assert.lengthOf(await backgroundLogger.drain(), 0); - - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - setTracingDisabled(false); - addTraceProcessor(processor); - - try { - // Create a tool using the proper tool() helper - const getWeatherTool = tool({ - name: "get_weather", - description: "Get the current weather for a city", - parameters: z.object({ city: z.string() }), - execute: async (input: { city: string }) => { - return `The weather in ${input.city} is sunny with temperature 72°F`; - }, - }); - - // Create agent with the tool - const agent = new Agent({ - name: "weather-agent", - model: TEST_MODEL, - instructions: - "You can get the weather for any city. Use the get_weather tool when asked about weather.", - tools: [getWeatherTool], - }); - - const result = await run(agent, "What's the weather in San Francisco?"); - assert.ok(result); - assert.ok(result.finalOutput); - - // Verify spans were created - const spans = await backgroundLogger.drain(); - assert.isTrue(spans.length > 0); - - // Verify span structure - const taskSpans = spans.filter( - (s: any) => s.span_attributes?.type === "task", - ); - assert.isTrue(taskSpans.length > 0, "Expected task-type spans"); - - // Verify tool spans if function calling occurred - const toolSpans = spans.filter( - (s: any) => s.span_attributes?.type === "tool", - ); - if (toolSpans.length > 0) { - const toolSpan = toolSpans[0] as any; - assert.ok( - toolSpan.span_attributes.name, - "Tool span should have a name", - ); - } - } finally { - processor.shutdown(); - } - }); - - test("Cleanup behavior - traces are cleaned up properly and orphaned spans are handled gracefully", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - const trace: any = { - traceId: "test-trace-cleanup", - name: "cleanup-test", - metadata: {}, - }; - - // Start trace and verify it's stored - await processor.onTraceStart(trace); - assert.isTrue( - getSpansMap(processor).has(trace.traceId), - "Root span should be stored", - ); - assert.isTrue( - getTraceMetadataMap(processor).has(trace.traceId), - "Trace metadata should be stored", - ); - - // Add a child span - const span = { - spanId: "test-span-cleanup", - traceId: trace.traceId, - spanData: { type: "agent", name: "test-agent" }, - error: null, - } as any; - - await processor.onSpanStart(span); - const childSpanKey = `${trace.traceId}:${span.spanId}`; - assert.isTrue( - getSpansMap(processor).has(childSpanKey), - "Child span should be stored", - ); - - // End the child span first - await processor.onSpanEnd(span); - - // End the trace normally - await processor.onTraceEnd(trace); - - // Verify cleanup happened - assert.isFalse( - getSpansMap(processor).has(trace.traceId), - "Root span should be removed", - ); - assert.isFalse( - getTraceMetadataMap(processor).has(trace.traceId), - "Trace metadata should be removed", - ); - - // Test that operations with orphaned spans are handled gracefully - const orphanedSpan = { - spanId: "orphaned-span", - traceId: "test-trace-cleanup", // Same traceId but trace is now gone - spanData: { type: "agent", name: "orphaned" }, - error: null, - } as any; - - // These should be no-ops and not throw - await processor.onSpanStart(orphanedSpan); - await processor.onSpanEnd(orphanedSpan); - - // Verify the orphaned operations didn't create any trace data - assert.isFalse( - getSpansMap(processor).has("test-trace-cleanup"), - "Orphaned operations shouldn't recreate root span", - ); - assert.isFalse( - getTraceMetadataMap(processor).has("test-trace-cleanup"), - "Orphaned operations shouldn't recreate metadata", - ); - assert.equal( - getSpansMap(processor).size, - 0, - "No spans should exist after cleanup", - ); - assert.equal( - getTraceMetadataMap(processor).size, - 0, - "No metadata should exist after cleanup", - ); - }); - - test("LRU eviction behavior - oldest traces are evicted when maxTraces is exceeded", async () => { - // Use a small maxTraces for fast testing - const maxTraces = 3; - const processor = new OpenAIAgentsTraceProcessor({ maxTraces }); - - // Create traces up to the limit - const traces: any[] = []; - for (let i = 0; i < maxTraces; i++) { - const trace = { - traceId: `test-trace-${i}`, - name: `test-${i}`, - metadata: {}, - } as any; - traces.push(trace); - await processor.onTraceStart(trace); - } - - // Verify all traces are stored - assert.equal( - getTraceMetadataMap(processor).size, - maxTraces, - "All trace metadata should be stored", - ); - assert.isTrue( - getSpansMap(processor).has("test-trace-0"), - "First trace root span should exist", - ); - assert.isTrue( - getSpansMap(processor).has("test-trace-1"), - "Second trace root span should exist", - ); - assert.isTrue( - getSpansMap(processor).has("test-trace-2"), - "Third trace root span should exist", - ); - - // Add one more trace - this should trigger LRU eviction - const newTrace = { - traceId: "test-trace-new", - name: "test-new", - metadata: {}, - } as any; - await processor.onTraceStart(newTrace); - - // Metadata should still be at max size - assert.equal( - getTraceMetadataMap(processor).size, - maxTraces, - "Metadata should remain at max size after eviction", - ); - - // First trace should be evicted, new trace should exist - assert.isFalse( - getSpansMap(processor).has("test-trace-0"), - "First (oldest) trace should be evicted", - ); - assert.isFalse( - getTraceMetadataMap(processor).has("test-trace-0"), - "First trace metadata should be evicted", - ); - assert.isTrue( - getSpansMap(processor).has("test-trace-new"), - "New trace should exist", - ); - assert.isTrue( - getTraceMetadataMap(processor).has("test-trace-new"), - "New trace metadata should exist", - ); - assert.isTrue( - getSpansMap(processor).has("test-trace-1"), - "Second trace should still exist", - ); - assert.isTrue( - getSpansMap(processor).has("test-trace-2"), - "Third trace should still exist", - ); - }); - - test("Span hierarchy and storage validation - ensures proper parent-child relationships", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - // Create a trace - const trace: any = { - traceId: "test-hierarchy-trace", - name: "hierarchy-test", - metadata: {}, - }; - - await processor.onTraceStart(trace); - assert.isTrue( - getSpansMap(processor).has(trace.traceId), - "Root span should be stored by traceId", - ); - assert.isTrue( - getTraceMetadataMap(processor).has(trace.traceId), - "Trace metadata should be stored", - ); - - // Create parent span (no parentId, should attach to root) - const parentSpan = { - spanId: "parent-span-1", - traceId: trace.traceId, - parentId: null, - spanData: { type: "agent", name: "parent-agent" }, - error: null, - } as any; - - await processor.onSpanStart(parentSpan); - const parentSpanKey = `${trace.traceId}:${parentSpan.spanId}`; - assert.isTrue( - getSpansMap(processor).has(parentSpanKey), - "Parent span should be stored with composite key", - ); - - // Create child span (with parentId, should attach to parent) - const childSpan = { - spanId: "child-span-1", - traceId: trace.traceId, - parentId: parentSpan.spanId, - spanData: { - type: "function", - name: "child-function", - input: "test input", - output: "test output", - }, - error: null, - } as any; - - await processor.onSpanStart(childSpan); - const childSpanKey = `${trace.traceId}:${childSpan.spanId}`; - assert.isTrue( - getSpansMap(processor).has(childSpanKey), - "Child span should be stored with composite key", - ); - - // Create grandchild span - const grandchildSpan = { - spanId: "grandchild-span-1", - traceId: trace.traceId, - parentId: childSpan.spanId, - spanData: { - type: "generation", - name: "grandchild-generation", - input: [{ role: "user", content: "test" }], - output: [{ role: "assistant", content: "response" }], - }, - error: null, - } as any; - - await processor.onSpanStart(grandchildSpan); - const grandchildSpanKey = `${trace.traceId}:${grandchildSpan.spanId}`; - assert.isTrue( - getSpansMap(processor).has(grandchildSpanKey), - "Grandchild span should be stored with composite key", - ); - - // Verify we have the expected number of spans - const allSpanKeys = Array.from(getSpansMap(processor).keys()); - const traceSpans = allSpanKeys.filter((key) => - key.startsWith(trace.traceId), - ); - assert.equal( - traceSpans.length, - 4, - "Should have 4 spans total: 1 root + 3 child spans", - ); - - // End spans in reverse order (grandchild -> child -> parent) - await processor.onSpanEnd(grandchildSpan); - assert.isFalse( - getSpansMap(processor).has(grandchildSpanKey), - "Grandchild span should be removed after ending", - ); - - await processor.onSpanEnd(childSpan); - assert.isFalse( - getSpansMap(processor).has(childSpanKey), - "Child span should be removed after ending", - ); - - await processor.onSpanEnd(parentSpan); - assert.isFalse( - getSpansMap(processor).has(parentSpanKey), - "Parent span should be removed after ending", - ); - - // Root span should still exist - assert.isTrue( - getSpansMap(processor).has(trace.traceId), - "Root span should still exist", - ); - assert.isTrue( - getTraceMetadataMap(processor).has(trace.traceId), - "Trace metadata should still exist", - ); - - // End the trace - await processor.onTraceEnd(trace); - assert.isFalse( - getSpansMap(processor).has(trace.traceId), - "Root span should be removed after trace end", - ); - assert.isFalse( - getTraceMetadataMap(processor).has(trace.traceId), - "Trace metadata should be removed after trace end", - ); - - // Verify all spans are cleaned up - const remainingSpans = Array.from(getSpansMap(processor).keys()).filter( - (key) => key.startsWith(trace.traceId), - ); - assert.equal( - remainingSpans.length, - 0, - "No spans should remain for this trace", - ); - }); - - test("currentSpan() detection creates proper span hierarchy with actual OpenAI SDK", async () => { - // This tests that currentSpan() detection works with the real OpenAI SDK - assert.lengthOf(await backgroundLogger.drain(), 0); - - const testFunction = wrapTraced( - async (instructions: string) => { - // Verify we're in a traced context - const detectedParent = currentSpan(); - assert.ok( - detectedParent, - "Parent span should exist in traced context", - ); - - // Create processor WITHOUT parentSpan - should auto-detect via currentSpan() - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - setTracingDisabled(false); - addTraceProcessor(processor); - - try { - // Create a simple agent - const agent = new Agent({ - name: "test-agent", - model: TEST_MODEL, - instructions: "You are a helpful assistant. Be very concise.", - }); - - // Run the agent - this should create spans as children of detected parent - const result = await run(agent, instructions); - assert.ok(result, "Agent should return a result"); - assert.ok(result.finalOutput, "Result should have finalOutput"); - - return result; - } finally { - processor.shutdown(); - } - }, - { name: "parent_span_test" }, - ); - - // Execute the wrapped function - const result = await testFunction("What is 2+2? Just the number."); - assert.ok(result, "Test function should return a result"); - - // Verify span hierarchy in logged spans - const spans = await backgroundLogger.drain(); - assert.isTrue( - spans.length >= 2, - "Should have at least parent and child spans", - ); - - // Find parent and child spans - const parentSpan = spans.find( - (s: any) => s.span_attributes?.name === "parent_span_test", - ); - const childSpan = spans.find( - (s: any) => s.span_attributes?.name === "Agent workflow", - ); - - assert.ok( - parentSpan, - "Should find parent span with name 'parent_span_test'", - ); - assert.ok(childSpan, "Should find child span with name 'Agent workflow'"); - - // Verify the child span has the parent as its parent - if (childSpan && parentSpan) { - // In Braintrust, parent-child relationships are represented by span_parents array - const childSpanParents = (childSpan as any).span_parents || []; - const parentSpanId = (parentSpan as any).span_id; - - assert.ok( - Array.isArray(childSpanParents) && childSpanParents.length > 0, - "Child span should have span_parents array", - ); - assert.isTrue( - childSpanParents.includes(parentSpanId), - "Child span should include parent span_id in its span_parents array (currentSpan detection)", - ); - - // Verify both spans have the same root_span_id - assert.equal( - (childSpan as any).root_span_id, - (parentSpan as any).root_span_id, - "Parent and child should share the same root_span_id", - ); - } - - // Verify input/output are properly logged on parent span - assert.ok( - (parentSpan as any).input, - "Parent span should have input logged", - ); - assert.ok( - (parentSpan as any).output, - "Parent span should have output logged", - ); - - // Verify that we have child spans beyond just "Agent workflow" - // The OpenAI SDK should generate multiple span types (generation, response, etc.) - const allChildSpans = spans.filter((s: any) => - (s as any).span_parents?.includes((parentSpan as any).span_id), - ); - - assert.isTrue( - allChildSpans.length >= 1, - `Should have at least 1 child span, but found ${allChildSpans.length}`, - ); - - // We should see spans like Generation, Response, etc. from the OpenAI SDK - const spanTypes = allChildSpans.map((s: any) => s.span_attributes?.type); - const hasLLMSpans = spanTypes.includes("llm"); - const hasTaskSpans = spanTypes.includes("task"); - - assert.isTrue( - hasLLMSpans || hasTaskSpans, - "Should have LLM or task type spans from OpenAI SDK", - ); - }); - - test("processor without parentSpan creates root spans (backward compatibility)", async () => { - // This ensures backward compatibility when parentSpan is not provided - assert.lengthOf(await backgroundLogger.drain(), 0); - - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - // No parentSpan provided - should create root spans - }); - - setTracingDisabled(false); - addTraceProcessor(processor); - - try { - const agent = new Agent({ - name: "root-agent", - model: TEST_MODEL, - instructions: "Be concise.", - }); - - const result = await run(agent, "What is 2+2?"); - assert.ok(result); - - const spans = await backgroundLogger.drain(); - assert.isTrue(spans.length > 0, "Should create spans"); - - // Find the Agent workflow span - const agentSpan = spans.find( - (s: any) => s.span_attributes?.name === "Agent workflow", - ); - assert.ok(agentSpan, "Should find Agent workflow span"); - - // Verify it's a root span (no parent_id or parent_id is null) - const isRootSpan = - !(agentSpan as any).parent_id || - (agentSpan as any).parent_id === null; - assert.isTrue( - isRootSpan, - "Agent workflow should be a root span when no parentSpan provided", - ); - } finally { - processor.shutdown(); - } - }); - - test("onTraceEnd logs trace metadata properly", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - const trace: any = { - traceId: "test-trace-metadata", - name: "metadata-test", - metadata: { userId: "test-user-123", sessionId: "session-456" }, - }; - - const span = { - spanId: "test-span", - traceId: trace.traceId, - spanData: { - type: "response", - name: "test-response", - _input: "test input", - _response: { output: "test output" }, - }, - error: null, - } as any; - - // Execute trace lifecycle - await processor.onTraceStart(trace); - await processor.onSpanStart(span); - await processor.onSpanEnd(span); - await processor.onTraceEnd(trace); - - // Verify metadata was logged to root span - const spans = await backgroundLogger.drain(); - const rootSpan = spans.find( - (s: any) => s.span_attributes?.name === "metadata-test", - ); - - assert.ok(rootSpan, "Should find root span"); - const spanMetadata = (rootSpan as any).metadata; - assert.ok(spanMetadata, "Root span should have metadata"); - assert.equal( - spanMetadata.userId, - "test-user-123", - "Should log trace metadata", - ); - assert.equal( - spanMetadata.sessionId, - "session-456", - "Should log trace metadata", - ); - }); - - test("response span with image generation converts base64 to attachments", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - const trace: any = { - traceId: "test-trace-images", - name: "image-test", - metadata: {}, - }; - - // Create a small base64-encoded PNG (1x1 red pixel) - const smallBase64Image = - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8DwHwAFBQIAX8jx0gAAAABJRU5ErkJggg=="; - - // Simulate response span with image input and output - const span = { - spanId: "test-span-images", - traceId: trace.traceId, - spanData: { - type: "response", - name: "test-response-images", - _input: [ - { - role: "user", - content: [ - { - type: "input_text", - text: "Change the background to Japanese style", - }, - { - type: "input_image", - image: `data:image/png;base64,${smallBase64Image}`, - }, - ], - }, - ], - _response: { - output: [ - { - id: "ig_test123", - type: "image_generation_call", - status: "completed", - output_format: "png", - result: smallBase64Image, - revised_prompt: "A Japanese style background", - }, - { - id: "msg_test456", - type: "message", - status: "completed", - content: [ - { - type: "output_text", - text: "Here is the image with a Japanese style background", - }, - ], - role: "assistant", - }, - ], - usage: { - input_tokens: 1555, - output_tokens: 113, - total_tokens: 1668, - }, - }, - }, - error: null, - } as any; - - // Execute trace lifecycle - await processor.onTraceStart(trace); - await processor.onSpanStart(span); - await processor.onSpanEnd(span); - await processor.onTraceEnd(trace); - - // Verify spans were created - const spans = await backgroundLogger.drain(); - const responseSpan = spans.find( - (s: any) => s.span_attributes?.name === "test-response-images", - ); - - assert.ok(responseSpan, "Should find response span"); - - // Verify input image was converted to attachment - const input = (responseSpan as any).input; - assert.ok(input, "Response span should have input"); - assert.ok(Array.isArray(input), "Input should be an array"); - - const userMessage = input[0]; - assert.ok(userMessage.content, "User message should have content"); - - const imageContent = userMessage.content.find( - (c: any) => c.type === "input_image", - ); - assert.ok(imageContent, "Should find input_image in content"); - assert.ok(imageContent.image, "Image content should have image field"); - - // Check if it's an Attachment (has the Braintrust attachment structure) - const imageAttachment = imageContent.image; - assert.ok( - imageAttachment.filename || imageAttachment._data, - "Image should be converted to Attachment object", - ); - - // Verify output image was converted to attachment - const output = (responseSpan as any).output; - assert.ok(output, "Response span should have output"); - assert.ok(Array.isArray(output), "Output should be an array"); - - const imageGenCall = output.find( - (o: any) => o.type === "image_generation_call", - ); - assert.ok(imageGenCall, "Should find image_generation_call in output"); - assert.ok(imageGenCall.result, "Image generation should have result"); - - // Check if result is an Attachment - const resultAttachment = imageGenCall.result; - assert.ok( - resultAttachment.filename || resultAttachment._data, - "Result should be converted to Attachment object", - ); - - // Verify metrics were extracted - const metrics = (responseSpan as any).metrics; - assert.ok(metrics, "Response span should have metrics"); - assert.equal(metrics.prompt_tokens, 1555, "Should have prompt_tokens"); - assert.equal( - metrics.completion_tokens, - 113, - "Should have completion_tokens", - ); - assert.equal(metrics.tokens, 1668, "Should have total tokens"); - }); - - test("Response span extracts cached tokens from usage", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - // Create a mock trace - const trace: any = { - traceId: "test-trace-cached-response", - name: "cached-response-test", - metadata: {}, - }; - - await processor.onTraceStart(trace); - - // Create a mock Response span with cached tokens - const responseSpan: any = { - spanId: "test-response-span", - traceId: trace.traceId, - parentId: null, - startedAt: new Date().toISOString(), - endedAt: new Date(Date.now() + 1000).toISOString(), - spanData: { - type: "response", - _input: "test input", - _response: { - output: "test output", - usage: { - input_tokens: 100, - output_tokens: 50, - total_tokens: 150, - input_tokens_details: { - cached_tokens: 80, // check for this later - }, - }, - }, - }, - error: null, - }; - - // Process the span - await processor.onSpanStart(responseSpan); - await processor.onSpanEnd(responseSpan); - - // Get the logged spans - const spans = await backgroundLogger.drain(); - - // Find the response span - const responseSpanLog = spans.find( - (s: any) => s.span_attributes?.type === "llm", - ); - - assert.ok(responseSpanLog, "Response span should be logged"); - - // Verify metrics were extracted - const metrics = (responseSpanLog as any).metrics; - assert.ok(metrics, "Response span should have metrics"); - assert.equal(metrics.prompt_tokens, 100, "Should have prompt_tokens"); - assert.equal( - metrics.completion_tokens, - 50, - "Should have completion_tokens", - ); - assert.equal(metrics.tokens, 150, "Should have total tokens"); - assert.equal( - metrics.prompt_cached_tokens, - 80, - "Should extract cached_tokens to prompt_cached_tokens", - ); - }); - - test("Response span handles zero cached tokens correctly", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - const trace: any = { - traceId: "test-trace-zero-cached", - name: "zero-cached-test", - metadata: {}, - }; - - await processor.onTraceStart(trace); - - const responseSpan: any = { - spanId: "test-response-span-zero", - traceId: trace.traceId, - parentId: null, - startedAt: new Date().toISOString(), - endedAt: new Date(Date.now() + 1000).toISOString(), - spanData: { - type: "response", - _response: { - usage: { - input_tokens: 100, - output_tokens: 50, - input_tokens_details: { - cached_tokens: 0, // Zero is a valid value - }, - }, - }, - }, - error: null, - }; - - await processor.onSpanStart(responseSpan); - await processor.onSpanEnd(responseSpan); - - const spans = await backgroundLogger.drain(); - const responseSpanLog = spans.find( - (s: any) => s.span_attributes?.type === "llm", - ); - const metrics = (responseSpanLog as any).metrics; - - // Zero should be logged, not skipped - assert.equal( - metrics.prompt_cached_tokens, - 0, - "Should log cached_tokens even when zero", - ); - }); - - test("Response span extracts cache write tokens from usage", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - const trace: any = { - traceId: "test-trace-cache-write-response", - name: "cache-write-response-test", - metadata: {}, - }; - - await processor.onTraceStart(trace); - - const responseSpan: any = { - spanId: "test-response-span-cache-write", - traceId: trace.traceId, - parentId: null, - startedAt: new Date().toISOString(), - endedAt: new Date(Date.now() + 1000).toISOString(), - spanData: { - type: "response", - _response: { - usage: { - input_tokens: 100, - output_tokens: 50, - total_tokens: 150, - input_tokens_details: { - cache_write_tokens: 25, - }, - }, - }, - }, - error: null, - }; - - await processor.onSpanStart(responseSpan); - await processor.onSpanEnd(responseSpan); - - const spans = await backgroundLogger.drain(); - const responseSpanLog = spans.find( - (s: any) => s.span_attributes?.type === "llm", - ); - const metrics = (responseSpanLog as any).metrics; - - assert.equal( - metrics.prompt_cache_creation_tokens, - 25, - "Should extract cache_write_tokens to prompt_cache_creation_tokens", - ); - }); - - test("Response span handles zero cache write tokens correctly", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - const trace: any = { - traceId: "test-trace-zero-cache-write", - name: "zero-cache-write-test", - metadata: {}, - }; - - await processor.onTraceStart(trace); - - const responseSpan: any = { - spanId: "test-response-span-zero-cache-write", - traceId: trace.traceId, - parentId: null, - startedAt: new Date().toISOString(), - endedAt: new Date(Date.now() + 1000).toISOString(), - spanData: { - type: "response", - _response: { - usage: { - input_tokens: 100, - output_tokens: 50, - input_tokens_details: { - cache_write_tokens: 0, - }, - }, - }, - }, - error: null, - }; - - await processor.onSpanStart(responseSpan); - await processor.onSpanEnd(responseSpan); - - const spans = await backgroundLogger.drain(); - const responseSpanLog = spans.find( - (s: any) => s.span_attributes?.type === "llm", - ); - const metrics = (responseSpanLog as any).metrics; - - // Zero should be logged, not skipped - assert.equal( - metrics.prompt_cache_creation_tokens, - 0, - "Should log cache_write_tokens even when zero", - ); - }); - - test("Response span handles missing cache write tokens gracefully", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - const trace: any = { - traceId: "test-trace-no-cache-write", - name: "no-cache-write-test", - metadata: {}, - }; - - await processor.onTraceStart(trace); - - const responseSpan: any = { - spanId: "test-response-span-no-cache-write", - traceId: trace.traceId, - parentId: null, - startedAt: new Date().toISOString(), - endedAt: new Date(Date.now() + 1000).toISOString(), - spanData: { - type: "response", - _response: { - usage: { - input_tokens: 100, - output_tokens: 50, - input_tokens_details: { - cached_tokens: 40, - // No cache_write_tokens - }, - }, - }, - }, - error: null, - }; - - await processor.onSpanStart(responseSpan); - await processor.onSpanEnd(responseSpan); - - const spans = await backgroundLogger.drain(); - const responseSpanLog = spans.find( - (s: any) => s.span_attributes?.type === "llm", - ); - const metrics = (responseSpanLog as any).metrics; - - assert.isUndefined( - metrics.prompt_cache_creation_tokens, - "Should not add prompt_cache_creation_tokens if not in usage", - ); - }); - - test("Response span handles missing cached tokens gracefully", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - const trace: any = { - traceId: "test-trace-no-cached", - name: "no-cached-test", - metadata: {}, - }; - - await processor.onTraceStart(trace); - - const responseSpan: any = { - spanId: "test-response-span-no-cached", - traceId: trace.traceId, - parentId: null, - startedAt: new Date().toISOString(), - endedAt: new Date(Date.now() + 1000).toISOString(), - spanData: { - type: "response", - _response: { - usage: { - input_tokens: 100, - output_tokens: 50, - // No input_tokens_details at all - }, - }, - }, - error: null, - }; - - await processor.onSpanStart(responseSpan); - await processor.onSpanEnd(responseSpan); - - const spans = await backgroundLogger.drain(); - const responseSpanLog = spans.find( - (s: any) => s.span_attributes?.type === "llm", - ); - const metrics = (responseSpanLog as any).metrics; - - // Should not have prompt_cached_tokens if not present in usage - assert.isUndefined( - metrics.prompt_cached_tokens, - "Should not add prompt_cached_tokens if not in usage", - ); - }); - - test("Generation span extracts cached tokens from usage", async () => { - const processor = new OpenAIAgentsTraceProcessor({ - logger: _logger as any, - }); - - const trace: any = { - traceId: "test-trace-cached-generation", - name: "cached-generation-test", - metadata: {}, - }; - - await processor.onTraceStart(trace); - - const generationSpan: any = { - spanId: "test-generation-span", - traceId: trace.traceId, - parentId: null, - startedAt: new Date().toISOString(), - endedAt: new Date(Date.now() + 1000).toISOString(), - spanData: { - type: "generation", - input: [{ role: "user", content: "test" }], - output: [{ role: "assistant", content: "response" }], - model: "gpt-4o-mini", - usage: { - input_tokens: 200, - output_tokens: 75, - total_tokens: 275, - input_tokens_details: { - cached_tokens: 150, // Test Generation span extraction - }, - }, - }, - error: null, - }; - - await processor.onSpanStart(generationSpan); - await processor.onSpanEnd(generationSpan); - - const spans = await backgroundLogger.drain(); - const generationSpanLog = spans.find( - (s: any) => s.span_attributes?.type === "llm", - ); - - assert.ok(generationSpanLog, "Generation span should be logged"); - - const metrics = (generationSpanLog as any).metrics; - assert.ok(metrics, "Generation span should have metrics"); - assert.equal(metrics.prompt_tokens, 200, "Should have prompt_tokens"); - assert.equal( - metrics.completion_tokens, - 75, - "Should have completion_tokens", - ); - assert.equal( - metrics.prompt_cached_tokens, - 150, - "Should extract cached_tokens from Generation span", - ); - }); - }, -); diff --git a/integrations/openai-agents-js/src/openai-agents-trace-processor.test.ts b/integrations/openai-agents-js/src/openai-agents-trace-processor.test.ts deleted file mode 100644 index 3b7fcbdda..000000000 --- a/integrations/openai-agents-js/src/openai-agents-trace-processor.test.ts +++ /dev/null @@ -1,288 +0,0 @@ -import { assert, describe, test } from "vitest"; -import { OpenAIAgentsTraceProcessor } from "./index"; - -function createDeferredPromise(): { - promise: Promise; - resolve: (value: T | PromiseLike) => void; - reject: (reason?: unknown) => void; -} { - let resolve!: (value: T | PromiseLike) => void; - let reject!: (reason?: unknown) => void; - const promise = new Promise((resolvePromise, rejectPromise) => { - resolve = resolvePromise; - reject = rejectPromise; - }); - return { promise, resolve, reject }; -} - -function trackAsyncCompletion(promise: Promise): { - promise: Promise; - isResolved: () => boolean; -} { - let resolved = false; - return { - promise: promise.finally(() => { - resolved = true; - }), - isResolved: () => resolved, - }; -} - -describe("OpenAIAgentsTraceProcessor flush behavior", () => { - test("onTraceEnd waits for root span flush to complete", async () => { - let flushCalls = 0; - let endCalls = 0; - const deferred = createDeferredPromise(); - let rootSpanInput: unknown; - let rootSpanOutput: unknown; - - const childSpan = { - log: () => {}, - end: () => {}, - }; - - const rootSpan = { - log: (data: Record) => { - rootSpanInput = data.input; - rootSpanOutput = data.output; - }, - startSpan: () => childSpan, - end: () => { - endCalls += 1; - }, - flush: () => { - flushCalls += 1; - return deferred.promise; - }, - }; - - const processor = new OpenAIAgentsTraceProcessor({ - logger: { - startSpan: () => rootSpan, - } as any, - }); - - const trace = { - traceId: "trace-1", - name: "test-trace", - groupId: "group-1", - metadata: {}, - } as any; - - await processor.onTraceStart(trace); - - const childOpenAIAgentsSpan = { - spanId: "span-1", - traceId: trace.traceId, - spanData: { - type: "generation", - input: "first-input", - output: "last-output", - }, - error: null, - } as any; - await processor.onSpanStart(childOpenAIAgentsSpan); - await processor.onSpanEnd(childOpenAIAgentsSpan); - - const onTraceEndCompletion = trackAsyncCompletion( - processor.onTraceEnd(trace), - ); - - await Promise.resolve(); - - assert.equal(endCalls, 1, "onTraceEnd should end the root span"); - assert.equal(flushCalls, 1, "onTraceEnd should flush the root span once"); - assert.equal( - rootSpanInput, - "first-input", - "onTraceEnd should log first input", - ); - assert.equal( - rootSpanOutput, - "last-output", - "onTraceEnd should log last output", - ); - assert.isFalse( - onTraceEndCompletion.isResolved(), - "onTraceEnd should not resolve before root span flush resolves", - ); - - deferred.resolve(); - await onTraceEndCompletion.promise; - - assert.isTrue( - onTraceEndCompletion.isResolved(), - "onTraceEnd should resolve after root span flush resolves", - ); - assert.isFalse( - processor._traceSpans.has(trace.traceId), - "onTraceEnd should remove trace state after finishing", - ); - }); - - test("onTraceEnd propagates root span flush failure after cleanup", async () => { - let flushCalls = 0; - let endCalls = 0; - const deferred = createDeferredPromise(); - const failure = new Error("flush failed"); - let rootSpanInput: unknown; - let rootSpanOutput: unknown; - - const childSpan = { - log: () => {}, - end: () => {}, - }; - - const rootSpan = { - log: (data: Record) => { - rootSpanInput = data.input; - rootSpanOutput = data.output; - }, - startSpan: () => childSpan, - end: () => { - endCalls += 1; - }, - flush: () => { - flushCalls += 1; - return deferred.promise; - }, - }; - - const processor = new OpenAIAgentsTraceProcessor({ - logger: { - startSpan: () => rootSpan, - } as any, - }); - - const trace = { - traceId: "trace-2", - name: "test-trace-fail", - groupId: "group-1", - metadata: {}, - } as any; - - await processor.onTraceStart(trace); - - const childOpenAIAgentsSpan = { - spanId: "span-1", - traceId: trace.traceId, - spanData: { - type: "generation", - input: "first-input", - output: "last-output", - }, - error: null, - } as any; - await processor.onSpanStart(childOpenAIAgentsSpan); - await processor.onSpanEnd(childOpenAIAgentsSpan); - - const onTraceEndCompletion = trackAsyncCompletion( - processor.onTraceEnd(trace), - ); - - await Promise.resolve(); - - assert.equal(endCalls, 1, "onTraceEnd should end the root span"); - assert.equal(flushCalls, 1, "onTraceEnd should flush the root span once"); - assert.equal( - rootSpanInput, - "first-input", - "root span log should include first input", - ); - assert.equal( - rootSpanOutput, - "last-output", - "root span log should include last output", - ); - assert.isFalse( - onTraceEndCompletion.isResolved(), - "onTraceEnd should wait for root span flush promise", - ); - - deferred.reject(failure); - try { - await onTraceEndCompletion.promise; - assert.fail("onTraceEnd should reject when root span flush rejects"); - } catch (error) { - assert.instanceOf(error as Error, Error); - assert.equal((error as Error).message, "flush failed"); - } - - assert.isFalse( - processor._traceSpans.has(trace.traceId), - "onTraceEnd should remove trace state even when flush fails", - ); - }); - - test("forceFlush waits for logger.flush to complete", async () => { - let flushCalls = 0; - const deferred = createDeferredPromise(); - - const processor = new OpenAIAgentsTraceProcessor({ - logger: { - flush: () => { - flushCalls += 1; - return deferred.promise; - }, - } as any, - }); - - const forceFlushCompletion = trackAsyncCompletion(processor.forceFlush()); - - await Promise.resolve(); - - assert.equal( - flushCalls, - 1, - "forceFlush should call logger.flush exactly once", - ); - assert.isFalse( - forceFlushCompletion.isResolved(), - "forceFlush should not resolve before logger.flush resolves", - ); - - deferred.resolve(); - await forceFlushCompletion.promise; - - assert.isTrue( - forceFlushCompletion.isResolved(), - "forceFlush should resolve after logger.flush", - ); - }); - - test("shutdown waits for logger.flush to complete", async () => { - let flushCalls = 0; - const deferred = createDeferredPromise(); - - const processor = new OpenAIAgentsTraceProcessor({ - logger: { - flush: () => { - flushCalls += 1; - return deferred.promise; - }, - } as any, - }); - - const shutdownCompletion = trackAsyncCompletion(processor.shutdown()); - - await Promise.resolve(); - - assert.equal( - flushCalls, - 1, - "shutdown should call logger.flush exactly once", - ); - assert.isFalse( - shutdownCompletion.isResolved(), - "shutdown should not resolve before logger.flush resolves", - ); - - deferred.resolve(); - await shutdownCompletion.promise; - - assert.isTrue( - shutdownCompletion.isResolved(), - "shutdown should resolve after logger.flush", - ); - }); -}); diff --git a/integrations/openai-agents-js/src/openai-agents-types.ts b/integrations/openai-agents-js/src/openai-agents-types.ts deleted file mode 100644 index 33a00a7d9..000000000 --- a/integrations/openai-agents-js/src/openai-agents-types.ts +++ /dev/null @@ -1,150 +0,0 @@ -/** - * Type definitions copied from @openai/agents-core. - * - * Original source: https://github.com/openai/openai-agents-js - * - * MIT License - * - * Copyright (c) 2025 OpenAI - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -type SpanDataBase = { - type: string; -}; - -export type AgentSpanData = SpanDataBase & { - type: "agent"; - name: string; - handoffs?: string[]; - tools?: string[]; - output_type?: string; -}; - -export type FunctionSpanData = SpanDataBase & { - type: "function"; - name: string; - input: string; - output: string; - mcp_data?: string; -}; - -export type GenerationSpanData = SpanDataBase & { - type: "generation"; - input?: Array>; - output?: Array>; - model?: string; - model_config?: Record; - usage?: Record; -}; - -export type ResponseSpanData = SpanDataBase & { - type: "response"; - response_id?: string; - /** - * Not used by the OpenAI tracing provider but helpful for other tracing providers. - */ - _input?: string | Record[]; - _response?: Record; -}; - -export type HandoffSpanData = SpanDataBase & { - type: "handoff"; - from_agent?: string; - to_agent?: string; -}; - -export type CustomSpanData = SpanDataBase & { - type: "custom"; - name: string; - data: Record; -}; - -export type GuardrailSpanData = SpanDataBase & { - type: "guardrail"; - name: string; - triggered: boolean; -}; - -export type TranscriptionSpanData = SpanDataBase & { - type: "transcription"; - input: { - data: string; - format: "pcm" | string; - }; - output?: string; - model?: string; - model_config?: Record; -}; - -export type SpeechSpanData = SpanDataBase & { - type: "speech"; - input?: string; - output: { - data: string; - format: "pcm" | string; - }; - model?: string; - model_config?: Record; -}; - -export type SpeechGroupSpanData = SpanDataBase & { - type: "speech_group"; - input?: string; -}; - -export type MCPListToolsSpanData = SpanDataBase & { - type: "mcp_tools"; - server?: string; - result?: string[]; -}; - -export type SpanData = - | AgentSpanData - | FunctionSpanData - | GenerationSpanData - | ResponseSpanData - | HandoffSpanData - | CustomSpanData - | GuardrailSpanData - | TranscriptionSpanData - | SpeechSpanData - | SpeechGroupSpanData - | MCPListToolsSpanData; - -// Simplified versions of Trace and Span types - only including what we actually use -export type Trace = { - type: "trace"; - traceId: string; - name: string; - groupId: string | null; - metadata?: Record; -}; - -export type Span = { - type: "trace.span"; - traceId: string; - spanData: TData; - spanId: string; - parentId: string | null; - startedAt: string | null; - endedAt: string | null; - error: { message: string; data?: Record } | null; -}; diff --git a/integrations/openai-agents-js/src/types.ts b/integrations/openai-agents-js/src/types.ts deleted file mode 100644 index 6834f5111..000000000 --- a/integrations/openai-agents-js/src/types.ts +++ /dev/null @@ -1,120 +0,0 @@ -import { Logger } from "braintrust"; -import type { - SpanData, - AgentSpanData, - FunctionSpanData, - GenerationSpanData, - ResponseSpanData, - HandoffSpanData, - CustomSpanData, - GuardrailSpanData, - MCPListToolsSpanData, - TranscriptionSpanData, - SpeechSpanData, - SpeechGroupSpanData, - Trace, - Span, -} from "./openai-agents-types"; - -export enum SpanType { - AGENT = "agent", - RESPONSE = "response", - FUNCTION = "function", - HANDOFF = "handoff", - GUARDRAIL = "guardrail", - GENERATION = "generation", - CUSTOM = "custom", - MCP_TOOLS = "mcp_tools", - TRANSCRIPTION = "transcription", - SPEECH = "speech", - SPEECH_GROUP = "speech_group", -} - -export type AgentsTrace = Trace; - -export type AgentsSpan = Span; - -export type SpanInput = - | string - | Array> - | Record[]; - -export type SpanOutput = - | string - | Array> - | Record; - -export type TraceMetadata = { - firstInput: SpanInput | null; - lastOutput: SpanOutput | null; -}; - -export interface OpenAIAgentsTraceProcessorOptions { - logger?: Logger; - maxTraces?: number; -} - -// Type guard functions -export function isResponseSpanData( - spanData: SpanData, -): spanData is ResponseSpanData { - return spanData.type === SpanType.RESPONSE; -} - -export function isGenerationSpanData( - spanData: SpanData, -): spanData is GenerationSpanData { - return spanData.type === SpanType.GENERATION; -} - -export function isAgentSpanData(spanData: SpanData): spanData is AgentSpanData { - return spanData.type === SpanType.AGENT; -} - -export function isFunctionSpanData( - spanData: SpanData, -): spanData is FunctionSpanData { - return spanData.type === SpanType.FUNCTION; -} - -export function isHandoffSpanData( - spanData: SpanData, -): spanData is HandoffSpanData { - return spanData.type === SpanType.HANDOFF; -} - -export function isGuardrailSpanData( - spanData: SpanData, -): spanData is GuardrailSpanData { - return spanData.type === SpanType.GUARDRAIL; -} - -export function isCustomSpanData( - spanData: SpanData, -): spanData is CustomSpanData { - return spanData.type === SpanType.CUSTOM; -} - -export function isMCPListToolsSpanData( - spanData: SpanData, -): spanData is MCPListToolsSpanData { - return spanData.type === SpanType.MCP_TOOLS; -} - -export function isTranscriptionSpanData( - spanData: SpanData, -): spanData is TranscriptionSpanData { - return spanData.type === SpanType.TRANSCRIPTION; -} - -export function isSpeechSpanData( - spanData: SpanData, -): spanData is SpeechSpanData { - return spanData.type === SpanType.SPEECH; -} - -export function isSpeechGroupSpanData( - spanData: SpanData, -): spanData is SpeechGroupSpanData { - return spanData.type === SpanType.SPEECH_GROUP; -} diff --git a/integrations/openai-agents-js/tsconfig.json b/integrations/openai-agents-js/tsconfig.json deleted file mode 100644 index ea6ad1861..000000000 --- a/integrations/openai-agents-js/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "declaration": true, - "lib": ["es2022"], - "module": "commonjs", - "target": "es2022", - "moduleResolution": "node", - "strict": true, - "noUnusedLocals": true, - "esModuleInterop": true, - "skipLibCheck": true - }, - "include": ["."], - "exclude": ["node_modules/**", "**/dist/**"] -} diff --git a/integrations/openai-agents-js/tsup.config.ts b/integrations/openai-agents-js/tsup.config.ts deleted file mode 100644 index 7d69621aa..000000000 --- a/integrations/openai-agents-js/tsup.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineConfig } from "tsup"; - -export default defineConfig([ - { - entry: ["src/index.ts"], - format: ["cjs", "esm"], - outDir: "dist", - dts: true, - }, -]); diff --git a/integrations/openai-agents-js/turbo.json b/integrations/openai-agents-js/turbo.json deleted file mode 100644 index 69a3e1241..000000000 --- a/integrations/openai-agents-js/turbo.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "build": { - "dependsOn": ["^build"], - "outputs": ["dist/**"] - }, - "test": { - "dependsOn": ["build"] - } - } -} diff --git a/integrations/openai-agents-js/vitest.config.ts b/integrations/openai-agents-js/vitest.config.ts deleted file mode 100644 index 9b3c6bb43..000000000 --- a/integrations/openai-agents-js/vitest.config.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - reporters: ["default"], - include: ["tests/**/*.test.ts", "src/**/*.test.ts"], - // Add any specific test configuration if needed - }, -}); diff --git a/integrations/vercel-ai-sdk/CHANGELOG.md b/integrations/vercel-ai-sdk/CHANGELOG.md deleted file mode 100644 index 131233302..000000000 --- a/integrations/vercel-ai-sdk/CHANGELOG.md +++ /dev/null @@ -1,35 +0,0 @@ -# @braintrust/vercel-ai-sdk - -## 0.0.7 - -- `@braintrust/vercel-ai-sdk` is now deprecated. This release marks the last release for this package. (https://github.com/braintrustdata/braintrust-sdk-javascript/pull/2383) -- Updated dependencies: braintrust@3.29.0 - -### Migration - -To migrate manual instrumentation, install `braintrust` and use `wrapAISDK` -from `braintrust` as described in the -[Vercel AI SDK integration guide](https://www.braintrust.dev/docs/integrations/sdk-integrations/vercel). -Automatic AI SDK instrumentation is also available through the Braintrust -runtime hook and supported bundler integrations; see the -[auto-instrumentation guide](https://www.braintrust.dev/docs/instrument/trace-llm-calls#auto-instrumentation) -for setup instructions. The legacy `BraintrustAdapter` stream conversion -helpers have no direct replacement; migrate those usages to `BraintrustStream` -from `braintrust`, documented in the -[TypeScript SDK reference](https://www.braintrust.dev/docs/reference/libs/nodejs), -and the current AI SDK stream response APIs. - -## 0.0.6 - -### Patch Changes - -- Update the AI SDK dependency to a patched version and preserve stream adapter compatibility. (https://github.com/braintrustdata/braintrust-sdk-javascript/pull/1992) -- Updated dependencies: braintrust@3.11.0 - -## 0.0.5 - -### Patch Changes - -- Added `toDataStreamResponse()` as the preferred response helper for Braintrust streams. -- Kept `toAIStreamResponse()` as a deprecated alias for compatibility. -- Updated the adapter for compatibility with newer Zod versions. diff --git a/integrations/vercel-ai-sdk/README.md b/integrations/vercel-ai-sdk/README.md deleted file mode 100644 index 73e42908e..000000000 --- a/integrations/vercel-ai-sdk/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# @braintrust/vercel-ai-sdk - -> [!WARNING] -> This package and all of its exports are deprecated and will stop being -> published after the next release. Use the Vercel AI SDK integration in -> `braintrust` instead. - -For manual instrumentation, use `wrapAISDK` from `braintrust`: - -```ts -import * as ai from "ai"; -import { wrapAISDK } from "braintrust"; - -const { generateText, streamText } = wrapAISDK(ai); -``` - -Braintrust also supports automatic AI SDK instrumentation. See the -[Braintrust SDK documentation](../../js/README.md#auto-instrumentation) for -runtime-hook and bundler setup. - -The legacy `BraintrustAdapter` stream conversion helpers do not have a direct -replacement. Migrate those usages to `BraintrustStream` from `braintrust` and -the current AI SDK stream response APIs before this package stops being -published. diff --git a/integrations/vercel-ai-sdk/package.json b/integrations/vercel-ai-sdk/package.json deleted file mode 100644 index dcc0f2bf0..000000000 --- a/integrations/vercel-ai-sdk/package.json +++ /dev/null @@ -1,48 +0,0 @@ -{ - "name": "@braintrust/vercel-ai-sdk", - "version": "0.0.7", - "description": "SDK for integrating Braintrust with Vercel AI SDK", - "main": "./dist/index.js", - "module": "./dist/index.mjs", - "types": "./dist/index.d.ts", - "exports": { - "./package.json": "./package.json", - ".": { - "types": "./dist/index.d.ts", - "import": "./dist/index.mjs", - "module": "./dist/index.mjs", - "require": "./dist/index.js" - } - }, - "scripts": { - "build": "tsup", - "check:typings": "tsc --noEmit", - "watch": "tsup --watch", - "clean": "rm -r dist/*", - "test": "vitest run", - "yalc:publish": "yalc publish" - }, - "author": "Braintrust Data Inc.", - "license": "MIT", - "devDependencies": { - "@types/node": "^20.10.5", - "braintrust": "workspace:*", - "tsup": "^8.5.1", - "typescript": "^5.3.3", - "vitest": "4.1.5" - }, - "peerDependencies": { - "braintrust": ">=0.0.141" - }, - "repository": { - "type": "git", - "url": "git+https://github.com/braintrustdata/braintrust-sdk-javascript.git", - "directory": "integrations/vercel-ai-sdk" - }, - "homepage": "https://www.braintrust.dev/docs", - "publishConfig": { - "access": "public", - "registry": "https://registry.npmjs.org/", - "provenance": true - } -} diff --git a/integrations/vercel-ai-sdk/src/adapter.test.ts b/integrations/vercel-ai-sdk/src/adapter.test.ts deleted file mode 100644 index 14d9e698e..000000000 --- a/integrations/vercel-ai-sdk/src/adapter.test.ts +++ /dev/null @@ -1,80 +0,0 @@ -import { BraintrustStreamChunk } from "braintrust"; -import { ReadableStream, WritableStream } from "stream/web"; -import { expect, test } from "vitest"; -import { BraintrustAdapter } from "."; - -test("text callbacks", async () => { - const textStream = new ReadableStream({ - start(controller) { - controller.enqueue({ type: "text_delta", data: "Hello, " }); - controller.enqueue({ type: "text_delta", data: "world!" }); - controller.close(); - }, - }); - - const { started, completion, final, tokens, text } = - await streamWithCallbacks(textStream); - - expect(started).toBe(true); - expect(completion).toBe("Hello, world!"); - expect(final).toBe("Hello, world!"); - expect(tokens).toEqual(["Hello, ", "world!"]); - expect(text).toEqual(["Hello, ", "world!"]); -}); - -test("json callbacks", async () => { - const toolStream = new ReadableStream({ - start(controller) { - controller.enqueue({ type: "json_delta", data: `{"a":` }); - controller.enqueue({ type: "json_delta", data: ` 1}` }); - controller.close(); - }, - }); - - const { started, completion, final, tokens, text } = - await streamWithCallbacks(toolStream); - - expect(started).toBe(true); - expect(completion).toBe(null); - expect(final && JSON.parse(final)).toEqual({ a: 1 }); - expect(tokens).toEqual([]); - expect(text).toEqual([]); -}); - -async function streamWithCallbacks( - stream: ReadableStream, -) { - let started = false; - let completion: string | null = null; - let final: string | null = null; - let tokens: string[] = []; - let text: string[] = []; - - const vercelStream = BraintrustAdapter.toAIStream(stream, { - onStart: () => { - started = true; - }, - onCompletion: (c) => { - completion = c; - }, - onFinal: (f) => { - final = f; - }, - onToken: (t) => { - tokens.push(t); - }, - onText: (t) => { - text.push(t); - }, - }); - - await vercelStream.pipeTo( - new WritableStream({ - write(chunk) { - console.log(new TextDecoder().decode(chunk)); - }, - }), - ); - - return { started, completion, final, tokens, text }; -} diff --git a/integrations/vercel-ai-sdk/src/adapter.ts b/integrations/vercel-ai-sdk/src/adapter.ts deleted file mode 100644 index 06c091add..000000000 --- a/integrations/vercel-ai-sdk/src/adapter.ts +++ /dev/null @@ -1,131 +0,0 @@ -import { BraintrustStream, BraintrustStreamChunk } from "braintrust"; - -/** - * @deprecated Use the AI SDK integration exported by `braintrust` instead. This package will stop being published after the next release. - */ -export interface AIStreamCallbacksAndOptions { - onStart?: () => void | Promise; - onCompletion?: (completion: string) => void | Promise; - onFinal?: (completion: string) => void | Promise; - onToken?: (token: string) => void | Promise; - onText?: (text: string) => void | Promise; -} - -function formatStreamPart(type: "text" | "data", value: unknown): string { - const code = type === "text" ? "0" : "2"; - return `${code}:${JSON.stringify(value)}\n`; -} - -import { ReadableStream, TransformStream } from "stream/web"; - -/** - * @deprecated Use `BraintrustStream` from `braintrust` with the current AI SDK response APIs instead. This package will stop being published after the next release. - */ -export type BraintrustStreamOrReadable = - | BraintrustStream - | ReadableStream - | ReadableStream - | ReadableStream; - -/** - * @deprecated Use `BraintrustStream` from `braintrust` with the current AI SDK stream APIs instead. This package will stop being published after the next release. - */ -export function toAIStream( - stream: BraintrustStreamOrReadable, - callbacks?: AIStreamCallbacksAndOptions, -): ReadableStream { - const btStream = - stream instanceof BraintrustStream - ? stream - : new BraintrustStream(stream as ReadableStream); - - return btStream - .toReadableStream() - .pipeThrough(btStreamToAISDKTransformStream(callbacks)); -} - -/** - * @deprecated Use `BraintrustStream` from `braintrust` with the current AI SDK response APIs instead. This package will stop being published after the next release. - */ -export function toDataStreamResponse( - stream: BraintrustStreamOrReadable, - init?: ResponseInit, -): Response { - return new Response(toAIStream(stream), { - headers: { - "Content-Type": "text/plain; charset=utf-8", - }, - ...init, - }); -} - -/** - * @deprecated Use `BraintrustStream` from `braintrust` with the current AI SDK response APIs instead. This package will stop being published after the next release. - */ -export function toAIStreamResponse( - stream: BraintrustStreamOrReadable, - init?: ResponseInit, -): Response { - return toDataStreamResponse(stream, init); -} - -function btStreamToAISDKTransformStream( - callbacks?: AIStreamCallbacksAndOptions, -) { - const encoder = new TextEncoder(); - const jsonChunks: string[] = []; - const textChunks: string[] = []; - return new TransformStream({ - async start(controller) { - if (callbacks?.onStart) { - await callbacks.onStart(); - } - }, - async transform(chunk, controller) { - switch (chunk.type) { - case "text_delta": - controller.enqueue( - encoder.encode(formatStreamPart("text", chunk.data)), - ); - - // Call me old fashioned, but I think it's worth checking the existence of - // each function to avoid unnecessary context switches. - if (callbacks?.onToken) { - await callbacks.onToken(chunk.data); - } - if (callbacks?.onText) { - await callbacks.onText(chunk.data); - } - - if (callbacks?.onCompletion || callbacks?.onFinal) { - textChunks.push(chunk.data); - } - - break; - case "json_delta": - jsonChunks.push(chunk.data); - break; - } - }, - async flush(controller) { - if (jsonChunks.length > 0) { - const jsonString = jsonChunks.join(""); - const data = JSON.parse(jsonString); - controller.enqueue(encoder.encode(formatStreamPart("data", data))); - if (callbacks?.onFinal) { - await callbacks.onFinal(jsonString); - } - } else { - const textData = textChunks.join(""); - if (callbacks?.onCompletion) { - await callbacks.onCompletion(textData); - } - if (callbacks?.onFinal) { - await callbacks.onFinal(textData); - } - } - - controller.terminate(); - }, - }); -} diff --git a/integrations/vercel-ai-sdk/src/index.ts b/integrations/vercel-ai-sdk/src/index.ts deleted file mode 100644 index 55a019e33..000000000 --- a/integrations/vercel-ai-sdk/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -import * as adapter from "./adapter"; - -/** - * @deprecated Use the AI SDK integration exported by `braintrust` instead. This package will stop being published after the next release. - */ -export const BraintrustAdapter = adapter; diff --git a/integrations/vercel-ai-sdk/tsconfig.json b/integrations/vercel-ai-sdk/tsconfig.json deleted file mode 100644 index ea6ad1861..000000000 --- a/integrations/vercel-ai-sdk/tsconfig.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "compilerOptions": { - "declaration": true, - "lib": ["es2022"], - "module": "commonjs", - "target": "es2022", - "moduleResolution": "node", - "strict": true, - "noUnusedLocals": true, - "esModuleInterop": true, - "skipLibCheck": true - }, - "include": ["."], - "exclude": ["node_modules/**", "**/dist/**"] -} diff --git a/integrations/vercel-ai-sdk/tsup.config.ts b/integrations/vercel-ai-sdk/tsup.config.ts deleted file mode 100644 index 7d69621aa..000000000 --- a/integrations/vercel-ai-sdk/tsup.config.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { defineConfig } from "tsup"; - -export default defineConfig([ - { - entry: ["src/index.ts"], - format: ["cjs", "esm"], - outDir: "dist", - dts: true, - }, -]); diff --git a/integrations/vercel-ai-sdk/turbo.json b/integrations/vercel-ai-sdk/turbo.json deleted file mode 100644 index 3e50dc890..000000000 --- a/integrations/vercel-ai-sdk/turbo.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "extends": ["//"], - "tasks": { - "build": { - "outputs": ["**/dist/**"] - } - } -} diff --git a/integrations/vercel-ai-sdk/vitest.config.ts b/integrations/vercel-ai-sdk/vitest.config.ts deleted file mode 100644 index 744610c82..000000000 --- a/integrations/vercel-ai-sdk/vitest.config.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { defineConfig } from "vitest/config"; - -export default defineConfig({ - test: { - reporters: ["default"], - include: ["tests/**/*.test.ts", "src/**/*.test.ts"], - }, -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b6234670..9f460b66f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -85,9 +85,6 @@ importers: '@braintrust/deepseek-harness': specifier: workspace:^ version: link:../integrations/deepseek-harness - '@braintrust/langchain-js': - specifier: workspace:^ - version: link:../integrations/langchain-js '@braintrust/otel': specifier: workspace:^ version: link:../integrations/otel-js @@ -181,69 +178,6 @@ importers: specifier: 4.1.5 version: 4.1.5(@opentelemetry/api@1.9.0)(@types/node@22.19.1)(msw@2.13.6(@types/node@22.19.1)(typescript@5.9.3))(vite@8.1.5(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)) - integrations/langchain-js: - devDependencies: - '@langchain/anthropic': - specifier: ^1.3.1 - version: 1.3.5(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1)) - '@langchain/core': - specifier: ^1.1.6 - version: 1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1) - '@langchain/langgraph': - specifier: ^1.0.7 - version: 1.0.7(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76) - '@langchain/openai': - specifier: ^1.2.0 - version: 1.2.1(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1))(ws@8.20.1) - '@types/node': - specifier: ^20.10.5 - version: 20.19.40 - braintrust: - specifier: workspace:* - version: link:../../js - msw: - specifier: ^2.6.6 - version: 2.13.6(@types/node@20.19.40)(typescript@5.9.3) - tsup: - specifier: ^8.5.1 - version: 8.5.1(@swc/core@1.15.8)(jiti@2.6.1)(postcss@8.5.19)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) - typescript: - specifier: ^5.3.3 - version: 5.9.3 - vitest: - specifier: 4.1.5 - version: 4.1.5(@opentelemetry/api@1.9.0)(@types/node@20.19.40)(msw@2.13.6(@types/node@20.19.40)(typescript@5.9.3))(vite@8.1.5(@types/node@20.19.40)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)) - zod: - specifier: ^3.25.34 - version: 3.25.76 - zod-to-json-schema: - specifier: ^3.22.5 - version: 3.25.1(zod@3.25.76) - - integrations/openai-agents-js: - devDependencies: - '@openai/agents': - specifier: ^0.0.15 - version: 0.0.15(ws@8.20.1)(zod@3.25.76) - '@types/node': - specifier: ^20.10.5 - version: 20.19.40 - braintrust: - specifier: workspace:^ - version: link:../../js - tsup: - specifier: ^8.5.1 - version: 8.5.1(@swc/core@1.15.8)(jiti@2.6.1)(postcss@8.5.19)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) - typescript: - specifier: ^5.3.3 - version: 5.9.3 - vitest: - specifier: 4.1.5 - version: 4.1.5(@opentelemetry/api@1.9.0)(@types/node@20.19.40)(msw@2.13.6(@types/node@20.19.40)(typescript@5.9.3))(vite@8.1.5(@types/node@20.19.40)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)) - zod: - specifier: ^3.25.34 - version: 3.25.76 - integrations/otel-js: dependencies: '@opentelemetry/api': @@ -336,24 +270,6 @@ importers: specifier: 4.1.5 version: 4.1.5(@opentelemetry/api@1.9.0)(@types/node@22.19.1)(msw@2.13.6(@types/node@22.19.1)(typescript@5.5.4))(vite@8.1.5(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)) - integrations/vercel-ai-sdk: - devDependencies: - '@types/node': - specifier: ^20.10.5 - version: 20.19.40 - braintrust: - specifier: workspace:* - version: link:../../js - tsup: - specifier: ^8.5.1 - version: 8.5.1(@swc/core@1.15.8)(jiti@2.6.1)(postcss@8.5.19)(tsx@4.21.0)(typescript@5.9.3)(yaml@2.9.0) - typescript: - specifier: ^5.3.3 - version: 5.9.3 - vitest: - specifier: 4.1.5 - version: 4.1.5(@opentelemetry/api@1.9.0)(@types/node@20.19.40)(msw@2.13.6(@types/node@20.19.40)(typescript@5.9.3))(vite@8.1.5(@types/node@20.19.40)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)) - js: dependencies: '@next/env': @@ -606,15 +522,6 @@ packages: resolution: {integrity: sha512-9zu/TXaUy8BZhXedDtt1wT3H4LOlpKDO1/ftiFpeR3N1PCr3KJFKkxxlQWWt1NNp08xSwUNJ3JNY8yhl8av6eQ==} hasBin: true - '@anthropic-ai/sdk@0.71.2': - resolution: {integrity: sha512-TGNDEUuEstk/DKu0/TflXAEt+p+p/WhTlFzEnoosvbaDU2LTjm42igSdlL0VijrKpWejtOKxX0b8A7uc+XiSAQ==} - hasBin: true - peerDependencies: - zod: ^3.25.0 || ^4.0.0 - peerDependenciesMeta: - zod: - optional: true - '@babel/runtime@7.28.4': resolution: {integrity: sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==} engines: {node: '>=6.9.0'} @@ -657,9 +564,6 @@ packages: cpu: [x64] os: [win32] - '@cfworker/json-schema@4.0.3': - resolution: {integrity: sha512-ZykIcDTVv5UNmKWSTLAs3VukO6NDJkkSKxrgUTDPBkAlORVT3H9n5DbRjRl8xIotklscHdbLIa0b9+y3mQq73g==} - '@changesets/apply-release-plan@7.1.0': resolution: {integrity: sha512-yq8ML3YS7koKQ/9bk1PqO0HMzApIFNwjlwCnwFEXMzNe8NpzeeYYKCmnhWJGkN8g7E51MnWaSbqRcTcdIxUgnQ==} @@ -1065,12 +969,6 @@ packages: engines: {node: '>=6'} hasBin: true - '@hono/node-server@1.19.13': - resolution: {integrity: sha512-TsQLe4i2gvoTtrHje625ngThGBySOgSK3Xo2XRYOdqGN1teR8+I7vchQC46uLJi8OF62YTYA3AhSpumtkhsaKQ==} - engines: {node: '>=18.14.1'} - peerDependencies: - hono: ^4.12.27 - '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -1201,69 +1099,12 @@ packages: peerDependencies: tslib: '2' - '@langchain/anthropic@1.3.5': - resolution: {integrity: sha512-oB+sfcBujxW/E3cRhGfR5K23+TwZKPPZPjatEadQOZllyPoen0xefIuNoi0oLWfobz3/LPwoNixGU4si3oyGBQ==} - engines: {node: '>=20'} - peerDependencies: - '@langchain/core': 1.1.10 - - '@langchain/core@1.1.10': - resolution: {integrity: sha512-/G5Uo8EvoZ1AuNjCToMEtDMexbfvjqQ60AaF8nxN2klTgCUlPo83U0M9JP0yRQW6QzkPF5T3VwEik76WcbZ1vg==} - engines: {node: '>=20'} - - '@langchain/langgraph-checkpoint@1.0.0': - resolution: {integrity: sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A==} - engines: {node: '>=18'} - peerDependencies: - '@langchain/core': ^1.0.1 - - '@langchain/langgraph-sdk@1.3.1': - resolution: {integrity: sha512-zTi7DZHwqtMEzapvm3I1FL4Q7OZsxtq9tTXy6s2gcCxyIU3sphqRboqytqVN7dNHLdTCLb8nXy49QKurs2MIBg==} - peerDependencies: - '@langchain/core': ^1.0.1 - react: ^18 || ^19 - react-dom: ^18 || ^19 - peerDependenciesMeta: - '@langchain/core': - optional: true - react: - optional: true - react-dom: - optional: true - - '@langchain/langgraph@1.0.7': - resolution: {integrity: sha512-EBGqNOWoRiEoLUaeuiXRpUM8/DE6QcwiirNyd97XhezStebBoTTilWH8CUt6S94JRGl5zwfBBRHfzotDnZS/eA==} - engines: {node: '>=18'} - peerDependencies: - '@langchain/core': ^1.0.1 - zod: ^3.25.32 || ^4.1.0 - zod-to-json-schema: ^3.x - peerDependenciesMeta: - zod-to-json-schema: - optional: true - - '@langchain/openai@1.2.1': - resolution: {integrity: sha512-eZYPhvXIwz0/8iCjj2LWqeaznQ7DZ6tBdvF+Ebv4sQW2UqJWZqRC8QIdKZgTbs8ffMWPHkSSOidYqu4XfWCNYg==} - engines: {node: '>=20'} - peerDependencies: - '@langchain/core': ^1.0.0 - '@manypkg/find-root@1.1.0': resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} '@manypkg/get-packages@1.1.3': resolution: {integrity: sha512-fo+QhuU3qE/2TQMQmbVMqaQ6EWbMhi4ABWP+O4AM1NqPBuy0OrApV5LO6BrrgnhtAHS2NH6RrVk9OL181tTi8A==} - '@modelcontextprotocol/sdk@1.29.0': - resolution: {integrity: sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==} - engines: {node: '>=18'} - peerDependencies: - '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - '@cfworker/json-schema': - optional: true - '@mswjs/interceptors@0.41.6': resolution: {integrity: sha512-qmDvJIjcNsZ6tXWy2G9yuCgMPTTn35GMA3dPpSLm7QJVpbQzYdw0ALy1bKoivXnEM3U93/OrK+/M719b+fg84Q==} engines: {node: '>=18'} @@ -1301,23 +1142,6 @@ packages: '@open-draft/until@2.1.0': resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==} - '@openai/agents-core@0.0.15': - resolution: {integrity: sha512-ODTqttjW0s0ejBe5PKnYRlFbJSZH2IO6OtUlRhIKmWiWrX6pGRxvpKjTSOXy8DEtpRHBj6Nhky0UoSlO6eOkDQ==} - peerDependencies: - zod: 3.25.40 - 3.25.67 - peerDependenciesMeta: - zod: - optional: true - - '@openai/agents-openai@0.0.15': - resolution: {integrity: sha512-YIX3n98HdmmWKkb/71OB+DCQUYyGEpqfzPjejzdtNLUvAEs3jvXf7nkC8oTISsuCwrirgBz0rQEefeo0oUlyFQ==} - - '@openai/agents-realtime@0.0.15': - resolution: {integrity: sha512-kSZzMyij9Xt3BpMb/9snuVnu7a5qKZLyhtN/kWMA+wmfETvWz23BBz6tbO5xOmurAt9//OktkB+94e0T0RBtlA==} - - '@openai/agents@0.0.15': - resolution: {integrity: sha512-B8y+WyWOeHowflPx09pyCfcqikC4OYWK27HTyNGt1oraXv93CzuamSr76iAaU1nWQ1MPbUwl6LHPX4BPUikVkQ==} - '@opentelemetry/api-logs@0.208.0': resolution: {integrity: sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==} engines: {node: '>=8.0.0'} @@ -2023,9 +1847,6 @@ packages: '@types/range-parser@1.2.7': resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==} - '@types/retry@0.12.0': - resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - '@types/send@0.17.4': resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==} @@ -2038,9 +1859,6 @@ packages: '@types/statuses@2.0.6': resolution: {integrity: sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA==} - '@types/ws@8.18.1': - resolution: {integrity: sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==} - '@typescript-eslint/eslint-plugin@8.50.0': resolution: {integrity: sha512-O7QnmOXYKVtPrfYzMolrCTfkezCJS9+ljLdKW/+DCvRsc3UAz+sbH6Xcsv7p30+0OwUbeWfUDAQE0vpabZ3QLg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} @@ -2234,14 +2052,6 @@ packages: ajv: optional: true - ajv-formats@3.0.1: - resolution: {integrity: sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==} - peerDependencies: - ajv: ^8.0.0 - peerDependenciesMeta: - ajv: - optional: true - ajv-keywords@5.1.0: resolution: {integrity: sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==} peerDependencies: @@ -2279,10 +2089,6 @@ packages: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} - ansi-styles@5.2.0: - resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} - engines: {node: '>=10'} - ansi-styles@6.2.1: resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} engines: {node: '>=12'} @@ -2321,9 +2127,6 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - baseline-browser-mapping@2.9.14: resolution: {integrity: sha512-B0xUquLkiGLgHhpPBqvl7GWegWBUNuujQ6kXd/r1U38ElPT6Ok8KZ8e+FpUGEc2ZoRQUzq/aUnaKFc/svWUGSg==} hasBin: true @@ -2384,10 +2187,6 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - camelcase@6.3.0: - resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} - engines: {node: '>=10'} - caniuse-lite@1.0.30001764: resolution: {integrity: sha512-9JGuzl2M+vPL+pz70gtMF9sHdMFbY9FJaQBi186cHKH3pSzDvzoUJUPV6fqiKIMyXbud9ZLg4F3Yza1vJ1+93g==} @@ -2535,10 +2334,6 @@ packages: supports-color: optional: true - decamelize@1.2.0: - resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==} - engines: {node: '>=0.10.0'} - deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} @@ -2747,9 +2542,6 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} - eventemitter3@4.0.7: - resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} @@ -2765,20 +2557,10 @@ packages: resolution: {integrity: sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==} engines: {node: '>=18.0.0'} - eventsource@3.0.7: - resolution: {integrity: sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==} - engines: {node: '>=18.0.0'} - expect-type@1.3.0: resolution: {integrity: sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==} engines: {node: '>=12.0.0'} - express-rate-limit@8.3.2: - resolution: {integrity: sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==} - engines: {node: '>= 16'} - peerDependencies: - express: '>= 4.11' - express@5.2.1: resolution: {integrity: sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==} engines: {node: '>= 18'} @@ -2990,10 +2772,6 @@ packages: resolution: {integrity: sha512-EQfezRg0NCZGNlhlDR3Evrw1FVL2G3LhU7EgPoxufQKruNBSYA8MiRPHeWbU+36o+Fhel0wMwM+sLEiBAlNLJA==} engines: {node: '>=10.0.0'} - hono@4.12.31: - resolution: {integrity: sha512-zJIHFrl6bq3RDd2YusFNCDlM8qUprxKswyi/OPzPyzKDdyBXDqWx8bZlZ7R+saTdSTatUmb3O7K4SspGPaEOQg==} - engines: {node: '>=16.9.0'} - http-errors@2.0.1: resolution: {integrity: sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==} engines: {node: '>= 0.8'} @@ -3049,10 +2827,6 @@ packages: resolution: {integrity: sha512-7PnF4oN3CvZF23ADhA5wRaYEQpJ8qygSkbtTXWBeXWXmEVRXK+1ITciHWwHhsjv1TmW0MgacIv6hEi5pX5NQdA==} engines: {node: '>=10'} - ip-address@10.4.0: - resolution: {integrity: sha512-oSK96Grm3aP6OrS263xVxbNDGVL7rzBtYdpGqlDG8iQdoenDoTs/nkki+DflYbAEE8Xl6o5YxhxlrKvI3nqKXQ==} - engines: {node: '>= 12'} - ipaddr.js@1.9.1: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} @@ -3105,16 +2879,10 @@ packages: resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} hasBin: true - jose@6.2.2: - resolution: {integrity: sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==} - joycon@3.1.1: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} - js-tiktoken@1.0.21: - resolution: {integrity: sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==} - js-yaml@3.15.0: resolution: {integrity: sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==} hasBin: true @@ -3126,19 +2894,12 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - json-schema-to-ts@3.1.1: - resolution: {integrity: sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==} - engines: {node: '>=16'} - json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-schema-traverse@1.0.0: resolution: {integrity: sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==} - json-schema-typed@8.0.2: - resolution: {integrity: sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==} - json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} @@ -3162,26 +2923,6 @@ packages: '@types/node': '>=18' typescript: '>=5.0.4 <7' - langsmith@0.7.1: - resolution: {integrity: sha512-Wjk90UjNoY5cBHMlNAC/eZx5clI8jnjBOBW8uJu8+MWBtx0QesNjsUiLtjI+I3UnrpxFFpDqGXcnhBjH654Mqg==} - peerDependencies: - '@opentelemetry/api': '*' - '@opentelemetry/exporter-trace-otlp-proto': '*' - '@opentelemetry/sdk-trace-base': '*' - openai: '*' - ws: '>=7' - peerDependenciesMeta: - '@opentelemetry/api': - optional: true - '@opentelemetry/exporter-trace-otlp-proto': - optional: true - '@opentelemetry/sdk-trace-base': - optional: true - openai: - optional: true - ws: - optional: true - levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} @@ -3502,18 +3243,6 @@ packages: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} - openai@5.11.0: - resolution: {integrity: sha512-+AuTc5pVjlnTuA9zvn8rA/k+1RluPIx9AD4eDcnutv6JNwHHZxIhkFy+tmMKCvmMFDQzfA/r1ujvPWB19DQkYg==} - hasBin: true - peerDependencies: - ws: ^8.18.0 - zod: ^3.23.8 - peerDependenciesMeta: - ws: - optional: true - zod: - optional: true - openai@6.25.0: resolution: {integrity: sha512-mEh6VZ2ds2AGGokWARo18aPISI1OhlgdEIC1ewhkZr8pSIT31dec0ecr9Nhxx0JlybyOgoAT1sWeKtwPZzJyww==} hasBin: true @@ -3526,18 +3255,6 @@ packages: zod: optional: true - openai@6.32.0: - resolution: {integrity: sha512-j3k+BjydAf8yQlcOI7WUQMQTbbF5GEIMAE2iZYCOzwwB3S2pCheaWYp+XZRNAch4jWVc52PMDGRRjutao3lLCg==} - hasBin: true - peerDependencies: - ws: ^8.18.0 - zod: ^3.25 || ^4.0 - peerDependenciesMeta: - ws: - optional: true - zod: - optional: true - optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} @@ -3555,10 +3272,6 @@ packages: resolution: {integrity: sha512-ZBxxZ5sL2HghephhpGAQdoskxplTwr7ICaehZwLIlfL6acuVgZPm8yBNuRAFBGEqtD/hmUeq9eqLg2ys9Xr/yw==} engines: {node: '>=8'} - p-finally@1.0.0: - resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} - engines: {node: '>=4'} - p-limit@2.3.0: resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==} engines: {node: '>=6'} @@ -3579,18 +3292,6 @@ packages: resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==} engines: {node: '>=6'} - p-queue@6.6.2: - resolution: {integrity: sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==} - engines: {node: '>=8'} - - p-retry@4.6.2: - resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} - engines: {node: '>=8'} - - p-timeout@3.2.0: - resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} - engines: {node: '>=8'} - p-try@2.2.0: resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==} engines: {node: '>=6'} @@ -3662,10 +3363,6 @@ packages: resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} engines: {node: '>= 6'} - pkce-challenge@5.0.0: - resolution: {integrity: sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==} - engines: {node: '>=16.20.0'} - pkg-types@1.3.1: resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} @@ -3743,15 +3440,6 @@ packages: resolution: {integrity: sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==} engines: {node: '>= 0.10'} - react-dom@19.2.4: - resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} - peerDependencies: - react: ^19.2.4 - - react@19.2.4: - resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} - engines: {node: '>=0.10.0'} - read-yaml-file@1.1.0: resolution: {integrity: sha512-VIMnQi/Z4HT2Fxuwg5KrY174U1VdUIASQVWXXyqtNRtxSr9IYkn1rsI6Tb6HsrHCmB7gVpNwX6JxPTHcH6IoTA==} engines: {node: '>=6'} @@ -3783,10 +3471,6 @@ packages: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} - retry@0.13.1: - resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} - engines: {node: '>= 4'} - rettime@0.11.8: resolution: {integrity: sha512-0fERGXktJTyJ+h8fBEiPxHPEFOu0h15JY7JtwrOVqR5K+vb99ho6IyOo7ekLS3h4sJCzIDy4VWKIbZUfe9njmg==} @@ -3823,9 +3507,6 @@ packages: safer-buffer@2.1.2: resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - scheduler@0.27.0: - resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - schema-utils@4.3.3: resolution: {integrity: sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==} engines: {node: '>= 10.13.0'} @@ -4135,9 +3816,6 @@ packages: resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} hasBin: true - ts-algebra@2.0.0: - resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} - ts-api-utils@2.1.0: resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} engines: {node: '>=18.12'} @@ -4497,9 +4175,6 @@ packages: peerDependencies: zod: ^3.25 || ^4 - zod@3.25.67: - resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} - zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} @@ -4528,12 +4203,6 @@ snapshots: '@anthropic-ai/sdk@0.60.0': {} - '@anthropic-ai/sdk@0.71.2(zod@3.25.76)': - dependencies: - json-schema-to-ts: 3.1.1 - optionalDependencies: - zod: 3.25.76 - '@babel/runtime@7.28.4': {} '@braintrust/bt-darwin-arm64@0.12.0': @@ -4557,8 +4226,6 @@ snapshots: '@braintrust/bt-win32-x64@0.12.0': optional: true - '@cfworker/json-schema@4.0.3': {} - '@changesets/apply-release-plan@7.1.0': dependencies: '@changesets/config': 3.1.3 @@ -4985,11 +4652,6 @@ snapshots: protobufjs: 7.6.5 yargs: 17.7.2 - '@hono/node-server@1.19.13(hono@4.12.31)': - dependencies: - hono: 4.12.31 - optional: true - '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -5006,7 +4668,8 @@ snapshots: '@humanwhocodes/retry@0.4.3': {} - '@inquirer/ansi@2.0.5': {} + '@inquirer/ansi@2.0.5': + optional: true '@inquirer/confirm@6.0.12(@types/node@20.19.40)': dependencies: @@ -5014,6 +4677,7 @@ snapshots: '@inquirer/type': 4.0.5(@types/node@20.19.40) optionalDependencies: '@types/node': 20.19.40 + optional: true '@inquirer/confirm@6.0.12(@types/node@22.19.1)': dependencies: @@ -5034,6 +4698,7 @@ snapshots: signal-exit: 4.1.0 optionalDependencies: '@types/node': 20.19.40 + optional: true '@inquirer/core@11.1.9(@types/node@22.19.1)': dependencies: @@ -5055,11 +4720,13 @@ snapshots: optionalDependencies: '@types/node': 24.12.4 - '@inquirer/figures@2.0.5': {} + '@inquirer/figures@2.0.5': + optional: true '@inquirer/type@4.0.5(@types/node@20.19.40)': optionalDependencies: '@types/node': 20.19.40 + optional: true '@inquirer/type@4.0.5(@types/node@22.19.1)': optionalDependencies: @@ -5141,68 +4808,6 @@ snapshots: '@jsonjoy.com/codegen': 1.0.0(tslib@2.8.1) tslib: 2.8.1 - '@langchain/anthropic@1.3.5(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1))': - dependencies: - '@anthropic-ai/sdk': 0.71.2(zod@3.25.76) - '@langchain/core': 1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1) - zod: 3.25.76 - - '@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1)': - dependencies: - '@cfworker/json-schema': 4.0.3 - ansi-styles: 5.2.0 - camelcase: 6.3.0 - decamelize: 1.2.0 - js-tiktoken: 1.0.21 - langsmith: 0.7.1(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1) - mustache: 4.2.0 - p-queue: 6.6.2 - uuid: 11.1.1 - zod: 3.25.76 - transitivePeerDependencies: - - '@opentelemetry/api' - - '@opentelemetry/exporter-trace-otlp-proto' - - '@opentelemetry/sdk-trace-base' - - openai - - ws - - '@langchain/langgraph-checkpoint@1.0.0(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1))': - dependencies: - '@langchain/core': 1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1) - uuid: 11.1.1 - - '@langchain/langgraph-sdk@1.3.1(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)': - dependencies: - p-queue: 6.6.2 - p-retry: 4.6.2 - uuid: 11.1.1 - optionalDependencies: - '@langchain/core': 1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1) - react: 19.2.4 - react-dom: 19.2.4(react@19.2.4) - - '@langchain/langgraph@1.0.7(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(zod-to-json-schema@3.25.1(zod@3.25.76))(zod@3.25.76)': - dependencies: - '@langchain/core': 1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1) - '@langchain/langgraph-checkpoint': 1.0.0(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1)) - '@langchain/langgraph-sdk': 1.3.1(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1))(react-dom@19.2.4(react@19.2.4))(react@19.2.4) - uuid: 11.1.1 - zod: 3.25.76 - optionalDependencies: - zod-to-json-schema: 3.25.1(zod@3.25.76) - transitivePeerDependencies: - - react - - react-dom - - '@langchain/openai@1.2.1(@langchain/core@1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1))(ws@8.20.1)': - dependencies: - '@langchain/core': 1.1.10(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1) - js-tiktoken: 1.0.21 - openai: 6.25.0(ws@8.20.1)(zod@3.25.76) - zod: 3.25.76 - transitivePeerDependencies: - - ws - '@manypkg/find-root@1.1.0': dependencies: '@babel/runtime': 7.28.4 @@ -5219,29 +4824,6 @@ snapshots: globby: 11.1.0 read-yaml-file: 1.1.0 - '@modelcontextprotocol/sdk@1.29.0(zod@3.25.76)': - dependencies: - '@hono/node-server': 1.19.13(hono@4.12.31) - ajv: 8.20.0 - ajv-formats: 3.0.1(ajv@8.20.0) - content-type: 1.0.5 - cors: 2.8.5 - cross-spawn: 7.0.6 - eventsource: 3.0.7 - eventsource-parser: 3.0.6 - express: 5.2.1 - express-rate-limit: 8.3.2(express@5.2.1) - hono: 4.12.31 - jose: 6.2.2 - json-schema-typed: 8.0.2 - pkce-challenge: 5.0.0 - raw-body: 3.0.2 - zod: 3.25.76 - zod-to-json-schema: 3.25.1(zod@3.25.76) - transitivePeerDependencies: - - supports-color - optional: true - '@mswjs/interceptors@0.41.6': dependencies: '@open-draft/deferred-promise': 2.2.0 @@ -5250,6 +4832,7 @@ snapshots: is-node-process: 1.2.0 outvariant: 1.4.3 strict-event-emitter: 0.5.1 + optional: true '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1)': dependencies: @@ -5272,70 +4855,20 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.16.0 - '@open-draft/deferred-promise@2.2.0': {} + '@open-draft/deferred-promise@2.2.0': + optional: true - '@open-draft/deferred-promise@3.0.0': {} + '@open-draft/deferred-promise@3.0.0': + optional: true '@open-draft/logger@0.3.0': dependencies: is-node-process: 1.2.0 outvariant: 1.4.3 + optional: true - '@open-draft/until@2.1.0': {} - - '@openai/agents-core@0.0.15(ws@8.20.1)(zod@3.25.76)': - dependencies: - '@openai/zod': zod@3.25.67 - debug: 4.4.3 - openai: 5.11.0(ws@8.20.1)(zod@3.25.76) - optionalDependencies: - '@modelcontextprotocol/sdk': 1.29.0(zod@3.25.76) - zod: 3.25.76 - transitivePeerDependencies: - - '@cfworker/json-schema' - - supports-color - - ws - - '@openai/agents-openai@0.0.15(ws@8.20.1)(zod@3.25.76)': - dependencies: - '@openai/agents-core': 0.0.15(ws@8.20.1)(zod@3.25.76) - '@openai/zod': zod@3.25.67 - debug: 4.4.3 - openai: 5.11.0(ws@8.20.1)(zod@3.25.76) - transitivePeerDependencies: - - '@cfworker/json-schema' - - supports-color - - ws - - zod - - '@openai/agents-realtime@0.0.15(zod@3.25.76)': - dependencies: - '@openai/agents-core': 0.0.15(ws@8.20.1)(zod@3.25.76) - '@openai/zod': zod@3.25.67 - '@types/ws': 8.18.1 - debug: 4.4.3 - ws: 8.20.1 - transitivePeerDependencies: - - '@cfworker/json-schema' - - bufferutil - - supports-color - - utf-8-validate - - zod - - '@openai/agents@0.0.15(ws@8.20.1)(zod@3.25.76)': - dependencies: - '@openai/agents-core': 0.0.15(ws@8.20.1)(zod@3.25.76) - '@openai/agents-openai': 0.0.15(ws@8.20.1)(zod@3.25.76) - '@openai/agents-realtime': 0.0.15(zod@3.25.76) - debug: 4.4.3 - openai: 5.11.0(ws@8.20.1)(zod@3.25.76) - transitivePeerDependencies: - - '@cfworker/json-schema' - - bufferutil - - supports-color - - utf-8-validate - - ws - - zod + '@open-draft/until@2.1.0': + optional: true '@opentelemetry/api-logs@0.208.0': dependencies: @@ -5909,8 +5442,6 @@ snapshots: '@types/range-parser@1.2.7': {} - '@types/retry@0.12.0': {} - '@types/send@0.17.4': dependencies: '@types/mime': 1.3.5 @@ -5925,12 +5456,10 @@ snapshots: '@types/set-cookie-parser@2.4.10': dependencies: '@types/node': 22.19.1 + optional: true - '@types/statuses@2.0.6': {} - - '@types/ws@8.18.1': - dependencies: - '@types/node': 22.19.1 + '@types/statuses@2.0.6': + optional: true '@typescript-eslint/eslint-plugin@8.50.0(@typescript-eslint/parser@8.50.0(eslint@9.39.4(jiti@2.6.1))(typescript@5.4.4))(eslint@9.39.4(jiti@2.6.1))(typescript@5.4.4)': dependencies: @@ -6054,15 +5583,6 @@ snapshots: msw: 2.13.6(@types/node@20.19.40)(typescript@5.5.4) vite: 8.1.5(@types/node@20.19.40)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0) - '@vitest/mocker@4.1.5(msw@2.13.6(@types/node@20.19.40)(typescript@5.9.3))(vite@8.1.5(@types/node@20.19.40)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0))': - dependencies: - '@vitest/spy': 4.1.5 - estree-walker: 3.0.3 - magic-string: 0.30.21 - optionalDependencies: - msw: 2.13.6(@types/node@20.19.40)(typescript@5.9.3) - vite: 8.1.5(@types/node@20.19.40)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0) - '@vitest/mocker@4.1.5(msw@2.13.6(@types/node@22.19.1)(typescript@5.5.4))(vite@8.1.5(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.5 @@ -6222,11 +5742,6 @@ snapshots: optionalDependencies: ajv: 8.20.0 - ajv-formats@3.0.1(ajv@8.20.0): - optionalDependencies: - ajv: 8.20.0 - optional: true - ajv-keywords@5.1.0(ajv@8.20.0): dependencies: ajv: 8.20.0 @@ -6264,8 +5779,6 @@ snapshots: dependencies: color-convert: 2.0.1 - ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} any-promise@1.3.0: {} @@ -6290,8 +5803,6 @@ snapshots: balanced-match@4.0.4: {} - base64-js@1.5.1: {} - baseline-browser-mapping@2.9.14: {} better-path-resolve@1.0.0: @@ -6360,8 +5871,6 @@ snapshots: callsites@3.1.0: {} - camelcase@6.3.0: {} - caniuse-lite@1.0.30001764: {} chai@6.2.2: {} @@ -6404,7 +5913,8 @@ snapshots: slice-ansi: 7.1.2 string-width: 8.2.0 - cli-width@4.1.0: {} + cli-width@4.1.0: + optional: true cliui@7.0.4: dependencies: @@ -6454,7 +5964,8 @@ snapshots: cookie@0.7.2: {} - cookie@1.1.1: {} + cookie@1.1.1: + optional: true cors@2.8.5: dependencies: @@ -6477,8 +5988,6 @@ snapshots: dependencies: ms: 2.1.3 - decamelize@1.2.0: {} - deep-is@0.1.4: {} depd@2.0.0: {} @@ -6705,8 +6214,6 @@ snapshots: event-target-shim@5.0.1: {} - eventemitter3@4.0.7: {} - eventemitter3@5.0.4: {} events@3.3.0: {} @@ -6715,19 +6222,8 @@ snapshots: eventsource-parser@3.0.6: {} - eventsource@3.0.7: - dependencies: - eventsource-parser: 3.0.6 - optional: true - expect-type@1.3.0: {} - express-rate-limit@8.3.2(express@5.2.1): - dependencies: - express: 5.2.1 - ip-address: 10.4.0 - optional: true - express@5.2.1: dependencies: accepts: 2.0.0 @@ -6954,7 +6450,8 @@ snapshots: graceful-fs@4.2.11: {} - graphql@16.13.2: {} + graphql@16.13.2: + optional: true handlebars@4.7.9: dependencies: @@ -6977,12 +6474,10 @@ snapshots: dependencies: '@types/set-cookie-parser': 2.4.10 set-cookie-parser: 3.1.0 + optional: true heap-js@2.7.1: {} - hono@4.12.31: - optional: true - http-errors@2.0.1: dependencies: depd: 2.0.0 @@ -7029,9 +6524,6 @@ snapshots: ini@2.0.0: {} - ip-address@10.4.0: - optional: true - ipaddr.js@1.9.1: {} is-extglob@2.1.1: {} @@ -7046,7 +6538,8 @@ snapshots: dependencies: is-extglob: 2.1.1 - is-node-process@1.2.0: {} + is-node-process@1.2.0: + optional: true is-number@7.0.0: {} @@ -7074,15 +6567,8 @@ snapshots: jiti@2.6.1: {} - jose@6.2.2: - optional: true - joycon@3.1.1: {} - js-tiktoken@1.0.21: - dependencies: - base64-js: 1.5.1 - js-yaml@3.15.0: dependencies: argparse: 1.0.10 @@ -7094,18 +6580,10 @@ snapshots: json-buffer@3.0.1: {} - json-schema-to-ts@3.1.1: - dependencies: - '@babel/runtime': 7.28.4 - ts-algebra: 2.0.0 - json-schema-traverse@0.4.1: {} json-schema-traverse@1.0.0: {} - json-schema-typed@8.0.2: - optional: true - json-schema@0.4.0: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -7140,15 +6618,6 @@ snapshots: - '@emnapi/core' - '@emnapi/runtime' - langsmith@0.7.1(@opentelemetry/api@1.9.0)(@opentelemetry/sdk-trace-base@2.6.1(@opentelemetry/api@1.9.0))(openai@6.32.0(ws@8.20.1)(zod@3.25.76))(ws@8.20.1): - dependencies: - p-queue: 6.6.2 - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/sdk-trace-base': 2.6.1(@opentelemetry/api@1.9.0) - openai: 6.32.0(ws@8.20.1)(zod@3.25.76) - ws: 8.20.1 - levn@0.4.1: dependencies: prelude-ls: 1.2.1 @@ -7385,31 +6854,6 @@ snapshots: - '@types/node' optional: true - msw@2.13.6(@types/node@20.19.40)(typescript@5.9.3): - dependencies: - '@inquirer/confirm': 6.0.12(@types/node@20.19.40) - '@mswjs/interceptors': 0.41.6 - '@open-draft/deferred-promise': 3.0.0 - '@types/statuses': 2.0.6 - cookie: 1.1.1 - graphql: 16.13.2 - headers-polyfill: 5.0.1 - is-node-process: 1.2.0 - outvariant: 1.4.3 - path-to-regexp: 6.3.0 - picocolors: 1.1.1 - rettime: 0.11.8 - statuses: 2.0.2 - strict-event-emitter: 0.5.1 - tough-cookie: 6.0.1 - type-fest: 5.6.0 - until-async: 3.0.2 - yargs: 17.7.2 - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - '@types/node' - msw@2.13.6(@types/node@22.19.1)(typescript@5.5.4): dependencies: '@inquirer/confirm': 6.0.12(@types/node@22.19.1) @@ -7464,7 +6908,8 @@ snapshots: mustache@4.2.0: {} - mute-stream@3.0.0: {} + mute-stream@3.0.0: + optional: true mz@2.7.0: dependencies: @@ -7529,22 +6974,11 @@ snapshots: dependencies: mimic-function: 5.0.1 - openai@5.11.0(ws@8.20.1)(zod@3.25.76): - optionalDependencies: - ws: 8.20.1 - zod: 3.25.76 - openai@6.25.0(ws@8.20.1)(zod@3.25.76): optionalDependencies: ws: 8.20.1 zod: 3.25.76 - openai@6.32.0(ws@8.20.1)(zod@3.25.76): - optionalDependencies: - ws: 8.20.1 - zod: 3.25.76 - optional: true - optionator@0.9.4: dependencies: deep-is: 0.1.4 @@ -7556,7 +6990,8 @@ snapshots: outdent@0.5.0: {} - outvariant@1.4.3: {} + outvariant@1.4.3: + optional: true oxc-resolver@11.19.1(@emnapi/core@1.11.1)(@emnapi/runtime@1.11.1): optionalDependencies: @@ -7588,8 +7023,6 @@ snapshots: dependencies: p-map: 2.1.0 - p-finally@1.0.0: {} - p-limit@2.3.0: dependencies: p-try: 2.2.0 @@ -7608,20 +7041,6 @@ snapshots: p-map@2.1.0: {} - p-queue@6.6.2: - dependencies: - eventemitter3: 4.0.7 - p-timeout: 3.2.0 - - p-retry@4.6.2: - dependencies: - '@types/retry': 0.12.0 - retry: 0.13.1 - - p-timeout@3.2.0: - dependencies: - p-finally: 1.0.0 - p-try@2.2.0: {} package-json-from-dist@1.0.1: {} @@ -7647,7 +7066,8 @@ snapshots: lru-cache: 10.4.3 minipass: 7.1.3 - path-to-regexp@6.3.0: {} + path-to-regexp@6.3.0: + optional: true path-to-regexp@8.4.0: {} @@ -7667,9 +7087,6 @@ snapshots: pirates@4.0.6: {} - pkce-challenge@5.0.0: - optional: true - pkg-types@1.3.1: dependencies: confbox: 0.1.8 @@ -7741,15 +7158,6 @@ snapshots: iconv-lite: 0.7.2 unpipe: 1.0.0 - react-dom@19.2.4(react@19.2.4): - dependencies: - react: 19.2.4 - scheduler: 0.27.0 - optional: true - - react@19.2.4: - optional: true - read-yaml-file@1.1.0: dependencies: graceful-fs: 4.2.11 @@ -7774,9 +7182,8 @@ snapshots: onetime: 7.0.0 signal-exit: 4.1.0 - retry@0.13.1: {} - - rettime@0.11.8: {} + rettime@0.11.8: + optional: true reusify@1.0.4: {} @@ -7856,9 +7263,6 @@ snapshots: safer-buffer@2.1.2: {} - scheduler@0.27.0: - optional: true - schema-utils@4.3.3: dependencies: '@types/json-schema': 7.0.15 @@ -7895,7 +7299,8 @@ snapshots: transitivePeerDependencies: - supports-color - set-cookie-parser@3.1.0: {} + set-cookie-parser@3.1.0: + optional: true setprototypeof@1.2.0: {} @@ -7985,7 +7390,8 @@ snapshots: std-env@4.1.0: {} - strict-event-emitter@0.5.1: {} + strict-event-emitter@0.5.1: + optional: true string-argv@0.3.2: {} @@ -8050,7 +7456,8 @@ snapshots: '@swc/counter': 0.1.3 webpack: 5.106.2(@swc/core@1.15.8)(esbuild@0.28.1)(postcss@8.5.19) - tagged-tag@1.0.0: {} + tagged-tag@1.0.0: + optional: true tapable@2.3.3: {} @@ -8112,11 +7519,13 @@ snapshots: tinyrainbow@3.1.0: {} - tldts-core@7.0.29: {} + tldts-core@7.0.29: + optional: true tldts@7.0.29: dependencies: tldts-core: 7.0.29 + optional: true to-regex-range@5.0.1: dependencies: @@ -8127,6 +7536,7 @@ snapshots: tough-cookie@6.0.1: dependencies: tldts: 7.0.29 + optional: true tr46@0.0.3: {} @@ -8136,8 +7546,6 @@ snapshots: tree-kill@1.2.2: {} - ts-algebra@2.0.0: {} - ts-api-utils@2.1.0(typescript@5.4.4): dependencies: typescript: 5.4.4 @@ -8265,6 +7673,7 @@ snapshots: type-fest@5.6.0: dependencies: tagged-tag: 1.0.0 + optional: true type-is@2.1.0: dependencies: @@ -8315,7 +7724,8 @@ snapshots: picomatch: 4.0.5 webpack-virtual-modules: 0.6.2 - until-async@3.0.2: {} + until-async@3.0.2: + optional: true update-browserslist-db@1.2.3(browserslist@4.28.1): dependencies: @@ -8430,34 +7840,6 @@ snapshots: transitivePeerDependencies: - msw - vitest@4.1.5(@opentelemetry/api@1.9.0)(@types/node@20.19.40)(msw@2.13.6(@types/node@20.19.40)(typescript@5.9.3))(vite@8.1.5(@types/node@20.19.40)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)): - dependencies: - '@vitest/expect': 4.1.5 - '@vitest/mocker': 4.1.5(msw@2.13.6(@types/node@20.19.40)(typescript@5.9.3))(vite@8.1.5(@types/node@20.19.40)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.5 - '@vitest/runner': 4.1.5 - '@vitest/snapshot': 4.1.5 - '@vitest/spy': 4.1.5 - '@vitest/utils': 4.1.5 - es-module-lexer: 2.1.0 - expect-type: 1.3.0 - magic-string: 0.30.21 - obug: 2.1.1 - pathe: 2.0.3 - picomatch: 4.0.5 - std-env: 4.1.0 - tinybench: 2.9.0 - tinyexec: 1.1.2 - tinyglobby: 0.2.17 - tinyrainbow: 3.1.0 - vite: 8.1.5(@types/node@20.19.40)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0) - why-is-node-running: 2.3.0 - optionalDependencies: - '@opentelemetry/api': 1.9.0 - '@types/node': 20.19.40 - transitivePeerDependencies: - - msw - vitest@4.1.5(@opentelemetry/api@1.9.0)(@types/node@22.19.1)(msw@2.13.6(@types/node@22.19.1)(typescript@5.5.4))(vite@8.1.5(@types/node@22.19.1)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.5 @@ -8609,7 +7991,8 @@ snapshots: wrappy@1.0.2: {} - ws@8.20.1: {} + ws@8.20.1: + optional: true y18n@5.0.8: {} @@ -8658,8 +8041,6 @@ snapshots: dependencies: zod: 3.25.76 - zod@3.25.67: {} - zod@3.25.76: {} zod@4.3.6: {} diff --git a/scripts/release/_shared.mjs b/scripts/release/_shared.mjs index 2af80e54c..a37f5d6d3 100644 --- a/scripts/release/_shared.mjs +++ b/scripts/release/_shared.mjs @@ -19,18 +19,12 @@ export const PUBLISHABLE_PACKAGES = [ dir: "integrations/deepseek-harness", name: "@braintrust/deepseek-harness", }, - { dir: "integrations/langchain-js", name: "@braintrust/langchain-js" }, - { dir: "integrations/openai-agents-js", name: "@braintrust/openai-agents" }, { dir: "integrations/otel-js", name: "@braintrust/otel" }, { dir: "integrations/templates-nunjucks", name: "@braintrust/templates-nunjucks-js", }, { dir: "integrations/temporal-js", name: "@braintrust/temporal" }, - { - dir: "integrations/vercel-ai-sdk", - name: "@braintrust/vercel-ai-sdk", - }, ]; export const PRIVATE_WORKSPACE_PACKAGES = [