From c1330165a4c75955a98ff432ff6eca453d608915 Mon Sep 17 00:00:00 2001 From: miguel Date: Fri, 21 Aug 2026 10:58:45 -0700 Subject: [PATCH] evals: braintrust tracedSpan OTEL branch (LangSmith attrs; native byte-identical) --- packages/evals/framework/braintrust.ts | 49 ++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/packages/evals/framework/braintrust.ts b/packages/evals/framework/braintrust.ts index 54092cb8c7..6afd098f46 100644 --- a/packages/evals/framework/braintrust.ts +++ b/packages/evals/framework/braintrust.ts @@ -7,7 +7,9 @@ * the callback's value unchanged, so this is safe to call from offline tools * (e.g., `bench verify`). */ +import { SpanStatusCode } from "@opentelemetry/api"; import type { Span, StartSpanArgs } from "braintrust"; +import { resolveTraceTransport } from "./langsmith.js"; let braintrustPromise: Promise | undefined; @@ -37,6 +39,53 @@ const NOOP_SPAN: SpanLike = { }; export async function tracedSpan(fn: TracedFn, options: TracedSpanOptions): Promise { + if (resolveTraceTransport() === "otel") { + // cappedJsonAttr keeps oversized attrs under the exporter limit; shared with + // otel.ts so there's a single serialization/cap path. + const { getTracer, cappedJsonAttr } = await import("./otel.js"); + const input = options.event?.input; + return getTracer().startActiveSpan( + options.name, + { + attributes: { + "langsmith.span.kind": options.type === "llm" ? "llm" : "chain", + ...(input === undefined + ? {} + : { + "input.value": cappedJsonAttr(input), + "input.mime_type": "application/json", + }), + }, + }, + async (otelSpan) => { + const span: SpanLike = { + log: (event) => { + if (event.output !== undefined) { + otelSpan.setAttributes({ + "output.value": cappedJsonAttr(event.output), + "output.mime_type": "application/json", + }); + } + if (event.metadata !== undefined) { + otelSpan.setAttribute("langsmith.metadata", cappedJsonAttr(event.metadata)); + } + }, + }; + try { + return await fn(span); + } catch (error) { + otelSpan.recordException(error instanceof Error ? error : String(error)); + otelSpan.setStatus({ + code: SpanStatusCode.ERROR, + message: error instanceof Error ? error.message : String(error), + }); + throw error; + } finally { + otelSpan.end(); + } + }, + ); + } if (!hasBraintrustApiKey()) { return fn(NOOP_SPAN); }