@mastra/braintrust 1.0.0-beta.14 → 1.0.0-beta.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/dist/index.cjs +5 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/tracing.d.ts.map +1 -1
- package/package.json +5 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,46 @@
|
|
|
1
1
|
# @mastra/braintrust
|
|
2
2
|
|
|
3
|
+
## 1.0.0-beta.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed exporting tags for root observability spans in Braintrust exporter. ([#12057](https://github.com/mastra-ai/mastra/pull/12057))
|
|
8
|
+
|
|
9
|
+
This was broken in PR: 11870.
|
|
10
|
+
|
|
11
|
+
- Updated dependencies [[`ed3e3dd`](https://github.com/mastra-ai/mastra/commit/ed3e3ddec69d564fe2b125e083437f76331f1283), [`6833c69`](https://github.com/mastra-ai/mastra/commit/6833c69607418d257750bbcdd84638993d343539), [`47b1c16`](https://github.com/mastra-ai/mastra/commit/47b1c16a01c7ffb6765fe1e499b49092f8b7eba3), [`3a76a80`](https://github.com/mastra-ai/mastra/commit/3a76a80284cb71a0faa975abb3d4b2a9631e60cd), [`8538a0d`](https://github.com/mastra-ai/mastra/commit/8538a0d232619bf55dad7ddc2a8b0ca77c679a87), [`9312dcd`](https://github.com/mastra-ai/mastra/commit/9312dcd1c6f5b321929e7d382e763d95fdc030f5)]:
|
|
12
|
+
- @mastra/core@1.0.0-beta.25
|
|
13
|
+
|
|
14
|
+
## 1.0.0-beta.15
|
|
15
|
+
|
|
16
|
+
### Patch Changes
|
|
17
|
+
|
|
18
|
+
- Added `flush()` method to observability exporters and instances for serverless environments ([#12003](https://github.com/mastra-ai/mastra/pull/12003))
|
|
19
|
+
|
|
20
|
+
This feature allows flushing buffered spans without shutting down the exporter, which is useful in serverless environments like Vercel's fluid compute where runtime instances can be reused across multiple requests.
|
|
21
|
+
|
|
22
|
+
**New API:**
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
// Flush all exporters via the observability instance
|
|
26
|
+
const observability = mastra.getObservability();
|
|
27
|
+
await observability.flush();
|
|
28
|
+
|
|
29
|
+
// Or flush individual exporters
|
|
30
|
+
const exporters = observability.getExporters();
|
|
31
|
+
await exporters[0].flush();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**Why this matters:**
|
|
35
|
+
|
|
36
|
+
In serverless environments, you may need to ensure all spans are exported before the runtime instance is terminated, while keeping the exporter active for future requests. Unlike shutdown(), flush() does not release resources or prevent future exports.
|
|
37
|
+
|
|
38
|
+
Closes #11372
|
|
39
|
+
|
|
40
|
+
- Updated dependencies [[`1dbd8c7`](https://github.com/mastra-ai/mastra/commit/1dbd8c729fb6536ec52f00064d76b80253d346e9), [`1dbd8c7`](https://github.com/mastra-ai/mastra/commit/1dbd8c729fb6536ec52f00064d76b80253d346e9), [`c59e13c`](https://github.com/mastra-ai/mastra/commit/c59e13c7688284bd96b2baee3e314335003548de), [`f9a2509`](https://github.com/mastra-ai/mastra/commit/f9a25093ea72d210a5e52cfcb3bcc8b5e02dc25c), [`7a010c5`](https://github.com/mastra-ai/mastra/commit/7a010c56b846a313a49ae42fccd3d8de2b9f292d)]:
|
|
41
|
+
- @mastra/core@1.0.0-beta.24
|
|
42
|
+
- @mastra/observability@1.0.0-beta.13
|
|
43
|
+
|
|
3
44
|
## 1.0.0-beta.14
|
|
4
45
|
|
|
5
46
|
### Patch Changes
|
package/dist/index.cjs
CHANGED
|
@@ -280,9 +280,11 @@ var BraintrustExporter = class extends observability.TrackingExporter {
|
|
|
280
280
|
name: span.name,
|
|
281
281
|
type: mapSpanType(span.type),
|
|
282
282
|
startTime: span.startTime.getTime() / 1e3,
|
|
283
|
-
event: {
|
|
284
|
-
|
|
285
|
-
|
|
283
|
+
event: {
|
|
284
|
+
id: span.id,
|
|
285
|
+
// Use Mastra span ID as Braintrust row ID for logFeedback() compatibility
|
|
286
|
+
...payload
|
|
287
|
+
}
|
|
286
288
|
});
|
|
287
289
|
const isModelGeneration = span.type === observability$1.SpanType.MODEL_GENERATION;
|
|
288
290
|
return {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/formatter.ts","../src/metrics.ts","../src/thread-reconstruction.ts","../src/tracing.ts"],"names":["SpanType","TrackingExporter","initLogger","currentSpan","omitKeys"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Message format conversion utilities for Braintrust.\n *\n * Converts AI SDK message format (v4/v5) to OpenAI Chat Completion format,\n * which Braintrust requires for proper rendering of threads.\n */\n\n// ==============================================================================\n// Utility functions\n// ==============================================================================\n\n/**\n * Remove null and undefined values from an object (shallow)\n */\nexport function removeNullish<T extends Record<string, unknown>>(obj: T): Partial<T> {\n return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null)) as Partial<T>;\n}\n\n// ==============================================================================\n// Type definitions for AI SDK message format conversion to OpenAI format\n// ==============================================================================\n\n/**\n * AI SDK content part types (both v4 and v5)\n */\ninterface AISDKTextPart {\n type: 'text';\n text: string;\n}\n\ninterface AISDKImagePart {\n type: 'image';\n image?: string | Uint8Array | URL;\n mimeType?: string;\n}\n\ninterface AISDKFilePart {\n type: 'file';\n data?: string | Uint8Array | URL;\n filename?: string;\n name?: string;\n mimeType?: string;\n}\n\ninterface AISDKReasoningPart {\n type: 'reasoning';\n text?: string;\n}\n\ninterface AISDKToolCallPart {\n type: 'tool-call';\n toolCallId: string;\n toolName: string;\n args?: unknown; // AI SDK v4\n input?: unknown; // AI SDK v5\n}\n\ninterface AISDKToolResultPart {\n type: 'tool-result';\n toolCallId: string;\n result?: unknown; // AI SDK v4\n output?: unknown; // AI SDK v5\n}\n\ntype AISDKContentPart =\n | AISDKTextPart\n | AISDKImagePart\n | AISDKFilePart\n | AISDKReasoningPart\n | AISDKToolCallPart\n | AISDKToolResultPart\n | { type: string; [key: string]: unknown }; // Catch-all for unknown types\n\n/**\n * AI SDK message format (input format for conversion)\n */\ninterface AISDKMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string | AISDKContentPart[];\n [key: string]: unknown; // Allow additional properties\n}\n\n/**\n * OpenAI Chat Completion tool call format\n */\nexport interface OpenAIToolCall {\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n}\n\n/**\n * OpenAI Chat Completion message format (output format)\n */\nexport interface OpenAIMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n [key: string]: unknown; // Allow additional properties\n}\n\n// ==============================================================================\n// Message conversion functions\n// ==============================================================================\n\n/**\n * Converts a content part to a string representation.\n * Handles text, image, file, reasoning, and other content types.\n */\nfunction convertContentPart(part: AISDKContentPart | null | undefined): string | null {\n if (!part || typeof part !== 'object') {\n return null;\n }\n\n switch (part.type) {\n case 'text':\n return (part as AISDKTextPart).text || null;\n\n case 'image':\n // Represent image content with a placeholder\n return '[image]';\n\n case 'file': {\n // Represent file content with filename if available\n const filePart = part as AISDKFilePart;\n if (filePart.filename || filePart.name) {\n return `[file: ${filePart.filename || filePart.name}]`;\n }\n return '[file]';\n }\n\n case 'reasoning': {\n // Represent reasoning/thinking content\n const reasoningPart = part as AISDKReasoningPart;\n if (typeof reasoningPart.text === 'string' && reasoningPart.text.length > 0) {\n return `[reasoning: ${reasoningPart.text.substring(0, 100)}${reasoningPart.text.length > 100 ? '...' : ''}]`;\n }\n return '[reasoning]';\n }\n\n case 'tool-call':\n // Tool calls are handled separately in assistant messages\n return null;\n\n case 'tool-result':\n // Tool results are handled separately in tool messages\n return null;\n\n default: {\n // For unknown types, try to extract any text-like content\n const unknownPart = part as { type?: string; text?: string; content?: string };\n if (typeof unknownPart.text === 'string') {\n return unknownPart.text;\n }\n if (typeof unknownPart.content === 'string') {\n return unknownPart.content;\n }\n // Represent unknown content type\n return `[${unknownPart.type || 'unknown'}]`;\n }\n }\n}\n\n/**\n * Serializes tool result data to a string for OpenAI format.\n */\nfunction serializeToolResult(resultData: unknown): string {\n if (typeof resultData === 'string') {\n return resultData;\n }\n if (resultData && typeof resultData === 'object' && 'value' in resultData) {\n const valueData = (resultData as { value: unknown }).value;\n return typeof valueData === 'string' ? valueData : JSON.stringify(valueData);\n }\n if (resultData === undefined || resultData === null) {\n return '';\n }\n try {\n return JSON.stringify(resultData);\n } catch {\n return '[unserializable result]';\n }\n}\n\n/**\n * Converts AI SDK message format to OpenAI Chat Completion format for Braintrust.\n *\n * Supports both AI SDK v4 and v5 formats:\n * - v4 uses 'args' for tool calls and 'result' for tool results\n * - v5 uses 'input' for tool calls and 'output' for tool results\n *\n * AI SDK format:\n * { role: \"user\", content: [{ type: \"text\", text: \"hello\" }] }\n * { role: \"assistant\", content: [{ type: \"text\", text: \"...\" }, { type: \"tool-call\", toolCallId: \"...\", toolName: \"...\", args: {...} }] }\n * { role: \"tool\", content: [{ type: \"tool-result\", toolCallId: \"...\", result: {...} }] }\n *\n * OpenAI format (what Braintrust expects):\n * { role: \"user\", content: \"hello\" }\n * { role: \"assistant\", content: \"...\", tool_calls: [{ id: \"...\", type: \"function\", function: { name: \"...\", arguments: \"...\" } }] }\n * { role: \"tool\", content: \"result\", tool_call_id: \"...\" }\n */\nexport function convertAISDKMessage(message: AISDKMessage | OpenAIMessage | unknown): OpenAIMessage | unknown {\n if (!message || typeof message !== 'object') {\n return message;\n }\n\n const { role, content, ...rest } = message as AISDKMessage;\n\n // If content is already a string, return as-is (already in OpenAI format)\n if (typeof content === 'string') {\n return message;\n }\n\n // If content is an array (AI SDK format), convert based on role\n if (Array.isArray(content)) {\n // Handle empty content arrays\n if (content.length === 0) {\n return { role, content: '', ...rest };\n }\n\n // For user/system messages, extract text and represent non-text content\n if (role === 'user' || role === 'system') {\n const contentParts = content.map((part: AISDKContentPart) => convertContentPart(part)).filter(Boolean);\n\n return {\n role,\n content: contentParts.length > 0 ? contentParts.join('\\n') : '',\n ...rest,\n };\n }\n\n // For assistant messages, extract text, non-text content, AND tool calls\n if (role === 'assistant') {\n const contentParts = content\n .filter((part: AISDKContentPart) => part?.type !== 'tool-call')\n .map((part: AISDKContentPart) => convertContentPart(part))\n .filter(Boolean);\n\n const toolCallParts = content.filter((part: AISDKContentPart) => part?.type === 'tool-call');\n\n const result: OpenAIMessage = {\n role,\n content: contentParts.length > 0 ? (contentParts as string[]).join('\\n') : '',\n ...rest,\n };\n\n // Add tool_calls array if there are tool calls\n if (toolCallParts.length > 0) {\n result.tool_calls = toolCallParts.map((tc: AISDKContentPart) => {\n const toolCall = tc as AISDKToolCallPart;\n const toolCallId = toolCall.toolCallId;\n const toolName = toolCall.toolName;\n // Support both v4 'args' and v5 'input'\n const args = toolCall.args ?? toolCall.input;\n\n let argsString: string;\n if (typeof args === 'string') {\n argsString = args;\n } else if (args !== undefined && args !== null) {\n argsString = JSON.stringify(args);\n } else {\n argsString = '{}';\n }\n\n return {\n id: toolCallId,\n type: 'function' as const,\n function: {\n name: toolName,\n arguments: argsString,\n },\n };\n });\n }\n\n return result;\n }\n\n // For tool messages, convert to OpenAI tool message format\n if (role === 'tool') {\n const toolResult = content.find((part): part is AISDKToolResultPart => part?.type === 'tool-result');\n if (toolResult) {\n // Support both v4 'result' and v5 'output' fields\n const resultData = toolResult.output ?? toolResult.result;\n const resultContent = serializeToolResult(resultData);\n\n return {\n role: 'tool',\n content: resultContent,\n tool_call_id: toolResult.toolCallId,\n } as OpenAIMessage;\n }\n }\n }\n\n return message;\n}\n","import type { UsageStats } from '@mastra/core/observability';\n\n/**\n * BraintrustUsageMetrics\n *\n * Canonical metric keys expected by Braintrust for LLM usage accounting.\n */\nexport interface BraintrustUsageMetrics {\n prompt_tokens?: number;\n completion_tokens?: number;\n tokens?: number;\n completion_reasoning_tokens?: number;\n prompt_cached_tokens?: number;\n prompt_cache_creation_tokens?: number;\n}\n\n/**\n * Formats UsageStats to Braintrust's expected metric format.\n */\nexport function formatUsageMetrics(usage?: UsageStats): BraintrustUsageMetrics {\n const metrics: BraintrustUsageMetrics = {};\n\n if (usage?.inputTokens !== undefined) {\n metrics.prompt_tokens = usage.inputTokens;\n }\n\n if (usage?.outputTokens !== undefined) {\n metrics.completion_tokens = usage.outputTokens;\n }\n\n // Compute total if we have both\n if (metrics.prompt_tokens !== undefined && metrics.completion_tokens !== undefined) {\n metrics.tokens = metrics.prompt_tokens + metrics.completion_tokens;\n }\n\n if (usage?.outputDetails?.reasoning !== undefined) {\n metrics.completion_reasoning_tokens = usage.outputDetails.reasoning;\n }\n\n if (usage?.inputDetails?.cacheRead !== undefined) {\n metrics.prompt_cached_tokens = usage.inputDetails.cacheRead;\n }\n\n if (usage?.inputDetails?.cacheWrite !== undefined) {\n metrics.prompt_cache_creation_tokens = usage.inputDetails.cacheWrite;\n }\n\n return metrics;\n}\n","/**\n * Thread view reconstruction for Braintrust.\n *\n * Reconstructs LLM output in OpenAI Chat Completion format by examining\n * child MODEL_STEP and TOOL_CALL spans. This enables Braintrust's Thread\n * view to properly display the full conversation flow including tool calls.\n *\n * See THREAD_VIEW_RECONSTRUCTION.md for details.\n */\n\nimport { removeNullish } from './formatter';\nimport type { OpenAIMessage } from './formatter';\n\n// ==============================================================================\n// Thread view reconstruction types\n// ==============================================================================\n\n/**\n * Tool call data accumulated from MODEL_STEP and TOOL_CALL spans\n */\nexport interface ThreadToolCall {\n toolCallId: string;\n toolName: string;\n args: unknown;\n result?: unknown; // filled in when TOOL_CALL ends\n startTime?: Date; // from TOOL_CALL span, for ordering multiple tool calls within a step\n}\n\n/**\n * Step data accumulated from MODEL_STEP spans for Thread view reconstruction\n */\nexport interface ThreadStepData {\n stepSpanId: string;\n stepIndex: number; // for ordering steps correctly\n text?: string;\n toolCalls?: ThreadToolCall[];\n}\n\n/**\n * Accumulated data for reconstructing Braintrust Thread view.\n * Populated for MODEL_GENERATION spans as child MODEL_STEP and TOOL_CALL spans complete.\n */\nexport type ThreadData = ThreadStepData[];\n\n/**\n * Tool result data stored when TOOL_CALL spans end (before MODEL_STEP ends)\n */\nexport interface PendingToolResult {\n result: unknown;\n startTime: Date;\n}\n\n// ==============================================================================\n// Thread view reconstruction\n// ==============================================================================\n\n/**\n * Reconstruct the Thread view output from accumulated threadData.\n * Converts to OpenAI Chat Completion message format.\n */\nexport function reconstructThreadOutput(threadData: ThreadData, originalOutput: unknown): OpenAIMessage[] {\n const messages: OpenAIMessage[] = [];\n\n // Sort steps by stepIndex\n const sortedSteps = [...threadData].sort((a, b) => a.stepIndex - b.stepIndex);\n\n for (const step of sortedSteps) {\n // Sort tool calls by startTime within each step\n const sortedToolCalls = step.toolCalls\n ? [...step.toolCalls].sort((a, b) => {\n if (!a.startTime || !b.startTime) return 0;\n return a.startTime.getTime() - b.startTime.getTime();\n })\n : [];\n\n if (sortedToolCalls.length > 0) {\n // Add assistant message with tool_calls\n messages.push({\n role: 'assistant',\n content: step.text || '',\n tool_calls: sortedToolCalls.map(tc => {\n // Clean null/undefined values from args before stringifying\n const cleanArgs =\n tc.args && typeof tc.args === 'object' && !Array.isArray(tc.args)\n ? removeNullish(tc.args as Record<string, unknown>)\n : tc.args;\n return {\n id: tc.toolCallId,\n type: 'function' as const,\n function: {\n name: tc.toolName,\n arguments: typeof cleanArgs === 'string' ? cleanArgs : JSON.stringify(cleanArgs),\n },\n };\n }),\n });\n\n // Add tool messages for each result\n for (const tc of sortedToolCalls) {\n if (tc.result !== undefined) {\n messages.push({\n role: 'tool',\n content: typeof tc.result === 'string' ? tc.result : JSON.stringify(tc.result),\n tool_call_id: tc.toolCallId,\n });\n }\n }\n } else if (step.text) {\n // Step with text only (final response)\n messages.push({\n role: 'assistant',\n content: step.text,\n });\n }\n }\n\n // If we have messages and the last one is a tool message,\n // add the final assistant text from original output\n if (messages.length > 0) {\n const lastMessage = messages[messages.length - 1]!;\n const originalText = (originalOutput as { text?: string })?.text;\n\n // If the last message is a tool response and we have final text, add it\n if (originalText && lastMessage.role === 'tool') {\n messages.push({\n role: 'assistant',\n content: originalText,\n });\n }\n }\n\n return messages;\n}\n","/**\n * Braintrust Exporter for Mastra Observability\n *\n * This exporter sends observability data to Braintrust.\n * Root spans become top-level Braintrust spans (no trace wrapper).\n * Events are handled as zero-duration spans with matching start/end times.\n */\n\nimport type { AnyExportedSpan, ModelGenerationAttributes, SpanErrorInfo } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport { omitKeys } from '@mastra/core/utils';\nimport { TrackingExporter } from '@mastra/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport { initLogger, currentSpan } from 'braintrust';\nimport type { Span, Logger } from 'braintrust';\nimport { removeNullish, convertAISDKMessage } from './formatter';\nimport { formatUsageMetrics } from './metrics';\nimport { reconstructThreadOutput } from './thread-reconstruction';\nimport type { ThreadData, ThreadStepData, PendingToolResult } from './thread-reconstruction';\n\n/**\n * Extended Braintrust span data that includes span type and thread reconstruction data\n */\ninterface BraintrustSpanData {\n span: Span;\n spanType: SpanType;\n threadData?: ThreadData; // only populated for MODEL_GENERATION spans\n // Tool results stored when TOOL_CALL ends (may arrive before MODEL_STEP ends)\n pendingToolResults?: Map<string, PendingToolResult>; // keyed by toolCallId\n}\n\nexport interface BraintrustExporterConfig extends TrackingExporterConfig {\n /**\n * Optional Braintrust logger instance.\n * When provided, enables integration with Braintrust contexts such as:\n * - Evals: Agent traces nest inside eval task spans\n * - logger.traced(): Agent traces nest inside traced spans\n * - Parent spans: Auto-detects and attaches to external Braintrust spans\n */\n braintrustLogger?: Logger<true>;\n\n /** Braintrust API key. Required if logger is not provided. */\n apiKey?: string;\n /** Optional custom endpoint */\n endpoint?: string;\n /** Braintrust project name (default: 'mastra-tracing') */\n projectName?: string;\n /** Support tuning parameters */\n tuningParameters?: Record<string, any>;\n}\n\ntype BraintrustRoot = Logger<true> | Span;\ntype BraintrustSpan = BraintrustSpanData;\ntype BraintrustEvent = Span;\ntype BraintrustMetadata = unknown;\ntype BraintrustTraceData = TraceData<BraintrustRoot, BraintrustSpan, BraintrustEvent, BraintrustMetadata>;\n\n// Default span type for all spans\nconst DEFAULT_SPAN_TYPE = 'task';\n\n// Exceptions to the default mapping\nconst SPAN_TYPE_EXCEPTIONS: Partial<Record<SpanType, string>> = {\n [SpanType.MODEL_GENERATION]: 'llm',\n [SpanType.TOOL_CALL]: 'tool',\n [SpanType.MCP_TOOL_CALL]: 'tool',\n [SpanType.WORKFLOW_CONDITIONAL_EVAL]: 'function',\n [SpanType.WORKFLOW_WAIT_EVENT]: 'function',\n};\n\n// Mapping function - returns valid Braintrust span types\nfunction mapSpanType(spanType: SpanType): 'llm' | 'score' | 'function' | 'eval' | 'task' | 'tool' {\n return (SPAN_TYPE_EXCEPTIONS[spanType] as any) ?? DEFAULT_SPAN_TYPE;\n}\n\nexport class BraintrustExporter extends TrackingExporter<\n BraintrustRoot,\n BraintrustSpan,\n BraintrustEvent,\n BraintrustMetadata,\n BraintrustExporterConfig\n> {\n name = 'braintrust';\n\n // Flags and logger for context-aware mode\n #useProvidedLogger: boolean;\n #providedLogger?: Logger<true>;\n #localLogger?: Logger<true>;\n\n constructor(config: BraintrustExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const resolvedApiKey = config.apiKey ?? process.env.BRAINTRUST_API_KEY;\n const resolvedEndpoint = config.endpoint ?? process.env.BRAINTRUST_ENDPOINT;\n\n super({\n ...config,\n apiKey: resolvedApiKey,\n endpoint: resolvedEndpoint,\n });\n\n this.#useProvidedLogger = !!config.braintrustLogger;\n\n if (this.#useProvidedLogger) {\n // Use provided logger - enables Braintrust context integration\n this.#providedLogger = config.braintrustLogger;\n } else {\n // Validate apiKey for creating loggers per trace\n if (!this.config.apiKey) {\n this.setDisabled(\n `Missing required API key. Set BRAINTRUST_API_KEY environment variable or pass apiKey in config.`,\n );\n return;\n }\n // lazy create logger on first rootSpan\n this.#localLogger = undefined;\n }\n }\n\n private async getLocalLogger(): Promise<Logger<true> | undefined> {\n if (this.#localLogger) {\n return this.#localLogger;\n }\n try {\n const logger = await initLogger({\n projectName: this.config.projectName ?? 'mastra-tracing',\n apiKey: this.config.apiKey,\n appUrl: this.config.endpoint,\n ...this.config.tuningParameters,\n });\n this.#localLogger = logger;\n return logger;\n } catch (err) {\n this.logger.error('Braintrust exporter: Failed to initialize logger', { error: err });\n this.setDisabled('Failed to initialize Braintrust logger');\n }\n }\n\n private startSpan(args: { parent: Span | Logger<true>; span: AnyExportedSpan }): BraintrustSpanData {\n const { parent, span } = args;\n const payload = this.buildSpanPayload(span);\n const braintrustSpan = parent.startSpan({\n spanId: span.id,\n name: span.name,\n type: mapSpanType(span.type),\n startTime: span.startTime.getTime() / 1000,\n event: { id: span.id }, // Use Mastra span ID as Braintrust row ID for logFeedback() compatibility\n ...payload,\n });\n\n // Create BraintrustSpanData with span type for tree walking\n // Initialize threadData and pendingToolResults for MODEL_GENERATION spans (used for Thread view reconstruction)\n const isModelGeneration = span.type === SpanType.MODEL_GENERATION;\n return {\n span: braintrustSpan,\n spanType: span.type,\n threadData: isModelGeneration ? [] : undefined,\n pendingToolResults: isModelGeneration ? new Map() : undefined,\n };\n }\n\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<BraintrustRoot | undefined> {\n if (this.#useProvidedLogger) {\n // Try to find a Braintrust span to attach to:\n // 1. Auto-detect from Braintrust's current span (logger.traced(), Eval(), etc.)\n // 2. Fall back to the configured logger\n const externalSpan = currentSpan();\n\n // Check if it's a valid span (not the NOOP_SPAN)\n if (externalSpan && externalSpan.id) {\n // External span detected - attach Mastra traces to it\n return externalSpan;\n } else {\n // No external span - use provided logger\n return this.#providedLogger!;\n }\n } else {\n // Use the local logger\n return this.getLocalLogger();\n }\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<BraintrustSpanData | undefined> {\n const { span, traceData } = args;\n\n if (span.isRootSpan) {\n const root = traceData.getRoot();\n if (root) {\n return this.startSpan({ parent: root, span });\n }\n } else {\n const parent = traceData.getParent(args);\n if (parent) {\n // Parent could be BraintrustSpanData (has .span) or BraintrustRoot (Logger/Span, no .span)\n const parentSpan = 'span' in parent ? parent.span : parent;\n return this.startSpan({ parent: parentSpan, span });\n }\n }\n }\n\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<Span | undefined> {\n const spanData = await this._buildSpan(args);\n\n if (!spanData) {\n // parent doesn't exist and not creating rootSpan, return early data\n return;\n }\n\n spanData.span.end({ endTime: args.span.startTime.getTime() / 1000 });\n return spanData.span;\n }\n\n protected override async _updateSpan(args: { span: AnyExportedSpan; traceData: BraintrustTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n const spanData = traceData.getSpan({ spanId: span.id });\n if (!spanData) {\n return;\n }\n spanData.span.log(this.buildSpanPayload(span, false));\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: BraintrustTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n const spanData = traceData.getSpan({ spanId: span.id });\n if (!spanData) {\n return;\n }\n\n // Handle thread data accumulation for MODEL_STEP and TOOL_CALL spans\n if (span.type === SpanType.MODEL_STEP) {\n this.accumulateModelStepData(span, traceData);\n } else if (span.type === SpanType.TOOL_CALL) {\n this.accumulateToolCallResult(span, traceData);\n }\n\n // Build payload - for MODEL_GENERATION, may reconstruct output from threadData\n const payload =\n span.type === SpanType.MODEL_GENERATION\n ? this.buildModelGenerationPayload(span, spanData)\n : this.buildSpanPayload(span, false);\n\n spanData.span.log(payload);\n\n if (span.endTime) {\n spanData.span.end({ endTime: span.endTime.getTime() / 1000 });\n } else {\n spanData.span.end();\n }\n }\n\n protected override async _abortSpan(args: { span: BraintrustSpan; reason: SpanErrorInfo }): Promise<void> {\n const { span: spanData, reason } = args;\n spanData.span.log({\n error: reason.message,\n metadata: { errorDetails: reason },\n });\n spanData.span.end();\n }\n\n // ==============================================================================\n // Thread view reconstruction helpers\n // ==============================================================================\n\n /**\n * Walk up the tree to find the MODEL_GENERATION ancestor span.\n * Returns the BraintrustSpanData if found, undefined otherwise.\n */\n private findModelGenerationAncestor(spanId: string, traceData: BraintrustTraceData): BraintrustSpanData | undefined {\n let currentId: string | undefined = spanId;\n\n while (currentId) {\n const parentId = traceData.getParentId({ spanId: currentId });\n if (!parentId) return undefined;\n\n const parentSpanData = traceData.getSpan({ spanId: parentId });\n if (parentSpanData?.spanType === SpanType.MODEL_GENERATION) {\n return parentSpanData;\n }\n currentId = parentId;\n }\n\n return undefined;\n }\n\n /**\n * Accumulate MODEL_STEP data to the parent MODEL_GENERATION's threadData.\n * Called when a MODEL_STEP span ends.\n */\n private accumulateModelStepData(span: AnyExportedSpan, traceData: BraintrustTraceData): void {\n const modelGenSpanData = this.findModelGenerationAncestor(span.id, traceData);\n if (!modelGenSpanData?.threadData) {\n return;\n }\n\n // Extract step data from MODEL_STEP output and attributes\n const output = span.output as\n | { text?: string; toolCalls?: Array<{ toolCallId: string; toolName: string; args: unknown }> }\n | undefined;\n const attributes = span.attributes as { stepIndex?: number } | undefined;\n\n const stepData: ThreadStepData = {\n stepSpanId: span.id,\n stepIndex: attributes?.stepIndex ?? 0,\n text: output?.text,\n toolCalls: output?.toolCalls?.map(tc => ({\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: tc.args,\n })),\n };\n\n modelGenSpanData.threadData.push(stepData);\n }\n\n /**\n * Store TOOL_CALL result in parent MODEL_GENERATION's pendingToolResults.\n * Called when a TOOL_CALL span ends.\n * Results are merged into threadData when MODEL_GENERATION ends.\n */\n private accumulateToolCallResult(span: AnyExportedSpan, traceData: BraintrustTraceData): void {\n const modelGenSpanData = this.findModelGenerationAncestor(span.id, traceData);\n if (!modelGenSpanData?.pendingToolResults) {\n return;\n }\n\n // Extract tool call ID from TOOL_CALL span input\n const input = span.input as { toolCallId?: string } | undefined;\n const toolCallId = input?.toolCallId;\n if (!toolCallId) {\n return;\n }\n\n // Store the result for later merging\n modelGenSpanData.pendingToolResults.set(toolCallId, {\n result: span.output,\n startTime: span.startTime,\n });\n }\n\n /**\n * Build the payload for MODEL_GENERATION span, reconstructing output from threadData if available.\n */\n private buildModelGenerationPayload(span: AnyExportedSpan, spanData: BraintrustSpanData): Record<string, any> {\n const basePayload = this.buildSpanPayload(span, false);\n\n // Check if we have threadData with tool calls to reconstruct\n const threadData = spanData.threadData;\n if (!threadData || threadData.length === 0) {\n return basePayload;\n }\n\n // Merge pending tool results into threadData\n if (spanData.pendingToolResults && spanData.pendingToolResults.size > 0) {\n for (const step of threadData) {\n if (step.toolCalls) {\n for (const toolCall of step.toolCalls) {\n const pendingResult = spanData.pendingToolResults.get(toolCall.toolCallId);\n if (pendingResult) {\n toolCall.result = pendingResult.result;\n toolCall.startTime = pendingResult.startTime;\n }\n }\n }\n }\n }\n\n // Check if any step has tool calls\n const hasToolCalls = threadData.some(step => step.toolCalls && step.toolCalls.length > 0);\n if (!hasToolCalls) {\n return basePayload;\n }\n\n // Reconstruct output as OpenAI messages\n const reconstructedOutput = reconstructThreadOutput(threadData, span.output);\n return {\n ...basePayload,\n output: reconstructedOutput,\n };\n }\n\n /**\n * Transforms MODEL_GENERATION input to Braintrust Thread view format.\n * Converts AI SDK messages (v4/v5) to OpenAI Chat Completion format, which Braintrust requires\n * for proper rendering of threads (fixes #11023).\n */\n private transformInput(input: unknown, spanType: SpanType): unknown {\n if (spanType === SpanType.MODEL_GENERATION) {\n // If input is already an array of messages, convert AI SDK format to OpenAI format\n if (Array.isArray(input)) {\n return input.map((msg: unknown) => convertAISDKMessage(msg));\n }\n\n // If input has a messages array\n if (\n input &&\n typeof input === 'object' &&\n 'messages' in input &&\n Array.isArray((input as { messages: unknown[] }).messages)\n ) {\n return (input as { messages: unknown[] }).messages.map((msg: unknown) => convertAISDKMessage(msg));\n }\n }\n\n return input;\n }\n\n /**\n * Transforms MODEL_GENERATION output to Braintrust Thread view format.\n */\n private transformOutput(output: any, spanType: SpanType): any {\n if (spanType === SpanType.MODEL_GENERATION) {\n if (!output || typeof output !== 'object') {\n return output;\n }\n const { text, ...rest } = output;\n // Remove null/undefined values from rest to keep Thread view clean\n return { role: 'assistant', content: text, ...removeNullish(rest) };\n }\n\n return output;\n }\n\n private buildSpanPayload(span: AnyExportedSpan, isCreate = true): Record<string, any> {\n const payload: Record<string, any> = {};\n\n if (span.input !== undefined) {\n payload.input = this.transformInput(span.input, span.type);\n }\n\n if (span.output !== undefined) {\n payload.output = this.transformOutput(span.output, span.type);\n }\n\n if (isCreate && span.isRootSpan && span.tags?.length) {\n payload.tags = span.tags;\n }\n\n // Initialize metrics and metadata objects\n payload.metrics = {};\n // Spread span.metadata first, then set spanType to prevent accidental override\n payload.metadata = {\n ...span.metadata,\n spanType: span.type,\n };\n\n if (isCreate) {\n payload.metadata['mastra-trace-id'] = span.traceId;\n }\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = attributes as ModelGenerationAttributes;\n\n // Model goes to metadata\n if (modelAttr.model !== undefined) {\n payload.metadata.model = modelAttr.model;\n }\n\n // Provider goes to metadata (if provided by attributes)\n if (modelAttr.provider !== undefined) {\n payload.metadata.provider = modelAttr.provider;\n }\n\n // Usage/token info goes to metrics\n payload.metrics = formatUsageMetrics(modelAttr.usage);\n\n // Time to first token (TTFT) for streaming responses\n // Braintrust expects TTFT in seconds (not milliseconds)\n if (modelAttr.completionStartTime) {\n payload.metrics.time_to_first_token =\n (modelAttr.completionStartTime.getTime() - span.startTime.getTime()) / 1000;\n }\n\n // Model parameters go to metadata\n if (modelAttr.parameters !== undefined) {\n payload.metadata.modelParameters = modelAttr.parameters;\n }\n\n // Other LLM attributes go to metadata\n const otherAttributes = omitKeys(attributes, ['model', 'usage', 'parameters', 'completionStartTime']);\n payload.metadata = {\n ...payload.metadata,\n ...otherAttributes,\n };\n } else {\n // For non-LLM spans, put all attributes in metadata\n payload.metadata = {\n ...payload.metadata,\n ...attributes,\n };\n }\n\n // Handle errors\n if (span.errorInfo) {\n payload.error = span.errorInfo.message;\n payload.metadata.errorDetails = span.errorInfo;\n }\n\n // Clean up empty metrics object\n if (Object.keys(payload.metrics).length === 0) {\n delete payload.metrics;\n }\n\n // Remove null/undefined values from metadata to keep Braintrust UI clean\n payload.metadata = removeNullish(payload.metadata);\n\n return payload;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/formatter.ts","../src/metrics.ts","../src/thread-reconstruction.ts","../src/tracing.ts"],"names":["SpanType","TrackingExporter","initLogger","currentSpan","omitKeys"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Message format conversion utilities for Braintrust.\n *\n * Converts AI SDK message format (v4/v5) to OpenAI Chat Completion format,\n * which Braintrust requires for proper rendering of threads.\n */\n\n// ==============================================================================\n// Utility functions\n// ==============================================================================\n\n/**\n * Remove null and undefined values from an object (shallow)\n */\nexport function removeNullish<T extends Record<string, unknown>>(obj: T): Partial<T> {\n return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null)) as Partial<T>;\n}\n\n// ==============================================================================\n// Type definitions for AI SDK message format conversion to OpenAI format\n// ==============================================================================\n\n/**\n * AI SDK content part types (both v4 and v5)\n */\ninterface AISDKTextPart {\n type: 'text';\n text: string;\n}\n\ninterface AISDKImagePart {\n type: 'image';\n image?: string | Uint8Array | URL;\n mimeType?: string;\n}\n\ninterface AISDKFilePart {\n type: 'file';\n data?: string | Uint8Array | URL;\n filename?: string;\n name?: string;\n mimeType?: string;\n}\n\ninterface AISDKReasoningPart {\n type: 'reasoning';\n text?: string;\n}\n\ninterface AISDKToolCallPart {\n type: 'tool-call';\n toolCallId: string;\n toolName: string;\n args?: unknown; // AI SDK v4\n input?: unknown; // AI SDK v5\n}\n\ninterface AISDKToolResultPart {\n type: 'tool-result';\n toolCallId: string;\n result?: unknown; // AI SDK v4\n output?: unknown; // AI SDK v5\n}\n\ntype AISDKContentPart =\n | AISDKTextPart\n | AISDKImagePart\n | AISDKFilePart\n | AISDKReasoningPart\n | AISDKToolCallPart\n | AISDKToolResultPart\n | { type: string; [key: string]: unknown }; // Catch-all for unknown types\n\n/**\n * AI SDK message format (input format for conversion)\n */\ninterface AISDKMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string | AISDKContentPart[];\n [key: string]: unknown; // Allow additional properties\n}\n\n/**\n * OpenAI Chat Completion tool call format\n */\nexport interface OpenAIToolCall {\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n}\n\n/**\n * OpenAI Chat Completion message format (output format)\n */\nexport interface OpenAIMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n [key: string]: unknown; // Allow additional properties\n}\n\n// ==============================================================================\n// Message conversion functions\n// ==============================================================================\n\n/**\n * Converts a content part to a string representation.\n * Handles text, image, file, reasoning, and other content types.\n */\nfunction convertContentPart(part: AISDKContentPart | null | undefined): string | null {\n if (!part || typeof part !== 'object') {\n return null;\n }\n\n switch (part.type) {\n case 'text':\n return (part as AISDKTextPart).text || null;\n\n case 'image':\n // Represent image content with a placeholder\n return '[image]';\n\n case 'file': {\n // Represent file content with filename if available\n const filePart = part as AISDKFilePart;\n if (filePart.filename || filePart.name) {\n return `[file: ${filePart.filename || filePart.name}]`;\n }\n return '[file]';\n }\n\n case 'reasoning': {\n // Represent reasoning/thinking content\n const reasoningPart = part as AISDKReasoningPart;\n if (typeof reasoningPart.text === 'string' && reasoningPart.text.length > 0) {\n return `[reasoning: ${reasoningPart.text.substring(0, 100)}${reasoningPart.text.length > 100 ? '...' : ''}]`;\n }\n return '[reasoning]';\n }\n\n case 'tool-call':\n // Tool calls are handled separately in assistant messages\n return null;\n\n case 'tool-result':\n // Tool results are handled separately in tool messages\n return null;\n\n default: {\n // For unknown types, try to extract any text-like content\n const unknownPart = part as { type?: string; text?: string; content?: string };\n if (typeof unknownPart.text === 'string') {\n return unknownPart.text;\n }\n if (typeof unknownPart.content === 'string') {\n return unknownPart.content;\n }\n // Represent unknown content type\n return `[${unknownPart.type || 'unknown'}]`;\n }\n }\n}\n\n/**\n * Serializes tool result data to a string for OpenAI format.\n */\nfunction serializeToolResult(resultData: unknown): string {\n if (typeof resultData === 'string') {\n return resultData;\n }\n if (resultData && typeof resultData === 'object' && 'value' in resultData) {\n const valueData = (resultData as { value: unknown }).value;\n return typeof valueData === 'string' ? valueData : JSON.stringify(valueData);\n }\n if (resultData === undefined || resultData === null) {\n return '';\n }\n try {\n return JSON.stringify(resultData);\n } catch {\n return '[unserializable result]';\n }\n}\n\n/**\n * Converts AI SDK message format to OpenAI Chat Completion format for Braintrust.\n *\n * Supports both AI SDK v4 and v5 formats:\n * - v4 uses 'args' for tool calls and 'result' for tool results\n * - v5 uses 'input' for tool calls and 'output' for tool results\n *\n * AI SDK format:\n * { role: \"user\", content: [{ type: \"text\", text: \"hello\" }] }\n * { role: \"assistant\", content: [{ type: \"text\", text: \"...\" }, { type: \"tool-call\", toolCallId: \"...\", toolName: \"...\", args: {...} }] }\n * { role: \"tool\", content: [{ type: \"tool-result\", toolCallId: \"...\", result: {...} }] }\n *\n * OpenAI format (what Braintrust expects):\n * { role: \"user\", content: \"hello\" }\n * { role: \"assistant\", content: \"...\", tool_calls: [{ id: \"...\", type: \"function\", function: { name: \"...\", arguments: \"...\" } }] }\n * { role: \"tool\", content: \"result\", tool_call_id: \"...\" }\n */\nexport function convertAISDKMessage(message: AISDKMessage | OpenAIMessage | unknown): OpenAIMessage | unknown {\n if (!message || typeof message !== 'object') {\n return message;\n }\n\n const { role, content, ...rest } = message as AISDKMessage;\n\n // If content is already a string, return as-is (already in OpenAI format)\n if (typeof content === 'string') {\n return message;\n }\n\n // If content is an array (AI SDK format), convert based on role\n if (Array.isArray(content)) {\n // Handle empty content arrays\n if (content.length === 0) {\n return { role, content: '', ...rest };\n }\n\n // For user/system messages, extract text and represent non-text content\n if (role === 'user' || role === 'system') {\n const contentParts = content.map((part: AISDKContentPart) => convertContentPart(part)).filter(Boolean);\n\n return {\n role,\n content: contentParts.length > 0 ? contentParts.join('\\n') : '',\n ...rest,\n };\n }\n\n // For assistant messages, extract text, non-text content, AND tool calls\n if (role === 'assistant') {\n const contentParts = content\n .filter((part: AISDKContentPart) => part?.type !== 'tool-call')\n .map((part: AISDKContentPart) => convertContentPart(part))\n .filter(Boolean);\n\n const toolCallParts = content.filter((part: AISDKContentPart) => part?.type === 'tool-call');\n\n const result: OpenAIMessage = {\n role,\n content: contentParts.length > 0 ? (contentParts as string[]).join('\\n') : '',\n ...rest,\n };\n\n // Add tool_calls array if there are tool calls\n if (toolCallParts.length > 0) {\n result.tool_calls = toolCallParts.map((tc: AISDKContentPart) => {\n const toolCall = tc as AISDKToolCallPart;\n const toolCallId = toolCall.toolCallId;\n const toolName = toolCall.toolName;\n // Support both v4 'args' and v5 'input'\n const args = toolCall.args ?? toolCall.input;\n\n let argsString: string;\n if (typeof args === 'string') {\n argsString = args;\n } else if (args !== undefined && args !== null) {\n argsString = JSON.stringify(args);\n } else {\n argsString = '{}';\n }\n\n return {\n id: toolCallId,\n type: 'function' as const,\n function: {\n name: toolName,\n arguments: argsString,\n },\n };\n });\n }\n\n return result;\n }\n\n // For tool messages, convert to OpenAI tool message format\n if (role === 'tool') {\n const toolResult = content.find((part): part is AISDKToolResultPart => part?.type === 'tool-result');\n if (toolResult) {\n // Support both v4 'result' and v5 'output' fields\n const resultData = toolResult.output ?? toolResult.result;\n const resultContent = serializeToolResult(resultData);\n\n return {\n role: 'tool',\n content: resultContent,\n tool_call_id: toolResult.toolCallId,\n } as OpenAIMessage;\n }\n }\n }\n\n return message;\n}\n","import type { UsageStats } from '@mastra/core/observability';\n\n/**\n * BraintrustUsageMetrics\n *\n * Canonical metric keys expected by Braintrust for LLM usage accounting.\n */\nexport interface BraintrustUsageMetrics {\n prompt_tokens?: number;\n completion_tokens?: number;\n tokens?: number;\n completion_reasoning_tokens?: number;\n prompt_cached_tokens?: number;\n prompt_cache_creation_tokens?: number;\n}\n\n/**\n * Formats UsageStats to Braintrust's expected metric format.\n */\nexport function formatUsageMetrics(usage?: UsageStats): BraintrustUsageMetrics {\n const metrics: BraintrustUsageMetrics = {};\n\n if (usage?.inputTokens !== undefined) {\n metrics.prompt_tokens = usage.inputTokens;\n }\n\n if (usage?.outputTokens !== undefined) {\n metrics.completion_tokens = usage.outputTokens;\n }\n\n // Compute total if we have both\n if (metrics.prompt_tokens !== undefined && metrics.completion_tokens !== undefined) {\n metrics.tokens = metrics.prompt_tokens + metrics.completion_tokens;\n }\n\n if (usage?.outputDetails?.reasoning !== undefined) {\n metrics.completion_reasoning_tokens = usage.outputDetails.reasoning;\n }\n\n if (usage?.inputDetails?.cacheRead !== undefined) {\n metrics.prompt_cached_tokens = usage.inputDetails.cacheRead;\n }\n\n if (usage?.inputDetails?.cacheWrite !== undefined) {\n metrics.prompt_cache_creation_tokens = usage.inputDetails.cacheWrite;\n }\n\n return metrics;\n}\n","/**\n * Thread view reconstruction for Braintrust.\n *\n * Reconstructs LLM output in OpenAI Chat Completion format by examining\n * child MODEL_STEP and TOOL_CALL spans. This enables Braintrust's Thread\n * view to properly display the full conversation flow including tool calls.\n *\n * See THREAD_VIEW_RECONSTRUCTION.md for details.\n */\n\nimport { removeNullish } from './formatter';\nimport type { OpenAIMessage } from './formatter';\n\n// ==============================================================================\n// Thread view reconstruction types\n// ==============================================================================\n\n/**\n * Tool call data accumulated from MODEL_STEP and TOOL_CALL spans\n */\nexport interface ThreadToolCall {\n toolCallId: string;\n toolName: string;\n args: unknown;\n result?: unknown; // filled in when TOOL_CALL ends\n startTime?: Date; // from TOOL_CALL span, for ordering multiple tool calls within a step\n}\n\n/**\n * Step data accumulated from MODEL_STEP spans for Thread view reconstruction\n */\nexport interface ThreadStepData {\n stepSpanId: string;\n stepIndex: number; // for ordering steps correctly\n text?: string;\n toolCalls?: ThreadToolCall[];\n}\n\n/**\n * Accumulated data for reconstructing Braintrust Thread view.\n * Populated for MODEL_GENERATION spans as child MODEL_STEP and TOOL_CALL spans complete.\n */\nexport type ThreadData = ThreadStepData[];\n\n/**\n * Tool result data stored when TOOL_CALL spans end (before MODEL_STEP ends)\n */\nexport interface PendingToolResult {\n result: unknown;\n startTime: Date;\n}\n\n// ==============================================================================\n// Thread view reconstruction\n// ==============================================================================\n\n/**\n * Reconstruct the Thread view output from accumulated threadData.\n * Converts to OpenAI Chat Completion message format.\n */\nexport function reconstructThreadOutput(threadData: ThreadData, originalOutput: unknown): OpenAIMessage[] {\n const messages: OpenAIMessage[] = [];\n\n // Sort steps by stepIndex\n const sortedSteps = [...threadData].sort((a, b) => a.stepIndex - b.stepIndex);\n\n for (const step of sortedSteps) {\n // Sort tool calls by startTime within each step\n const sortedToolCalls = step.toolCalls\n ? [...step.toolCalls].sort((a, b) => {\n if (!a.startTime || !b.startTime) return 0;\n return a.startTime.getTime() - b.startTime.getTime();\n })\n : [];\n\n if (sortedToolCalls.length > 0) {\n // Add assistant message with tool_calls\n messages.push({\n role: 'assistant',\n content: step.text || '',\n tool_calls: sortedToolCalls.map(tc => {\n // Clean null/undefined values from args before stringifying\n const cleanArgs =\n tc.args && typeof tc.args === 'object' && !Array.isArray(tc.args)\n ? removeNullish(tc.args as Record<string, unknown>)\n : tc.args;\n return {\n id: tc.toolCallId,\n type: 'function' as const,\n function: {\n name: tc.toolName,\n arguments: typeof cleanArgs === 'string' ? cleanArgs : JSON.stringify(cleanArgs),\n },\n };\n }),\n });\n\n // Add tool messages for each result\n for (const tc of sortedToolCalls) {\n if (tc.result !== undefined) {\n messages.push({\n role: 'tool',\n content: typeof tc.result === 'string' ? tc.result : JSON.stringify(tc.result),\n tool_call_id: tc.toolCallId,\n });\n }\n }\n } else if (step.text) {\n // Step with text only (final response)\n messages.push({\n role: 'assistant',\n content: step.text,\n });\n }\n }\n\n // If we have messages and the last one is a tool message,\n // add the final assistant text from original output\n if (messages.length > 0) {\n const lastMessage = messages[messages.length - 1]!;\n const originalText = (originalOutput as { text?: string })?.text;\n\n // If the last message is a tool response and we have final text, add it\n if (originalText && lastMessage.role === 'tool') {\n messages.push({\n role: 'assistant',\n content: originalText,\n });\n }\n }\n\n return messages;\n}\n","/**\n * Braintrust Exporter for Mastra Observability\n *\n * This exporter sends observability data to Braintrust.\n * Root spans become top-level Braintrust spans (no trace wrapper).\n * Events are handled as zero-duration spans with matching start/end times.\n */\n\nimport type { AnyExportedSpan, ModelGenerationAttributes, SpanErrorInfo } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport { omitKeys } from '@mastra/core/utils';\nimport { TrackingExporter } from '@mastra/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport { initLogger, currentSpan } from 'braintrust';\nimport type { Span, Logger } from 'braintrust';\nimport { removeNullish, convertAISDKMessage } from './formatter';\nimport { formatUsageMetrics } from './metrics';\nimport { reconstructThreadOutput } from './thread-reconstruction';\nimport type { ThreadData, ThreadStepData, PendingToolResult } from './thread-reconstruction';\n\n/**\n * Extended Braintrust span data that includes span type and thread reconstruction data\n */\ninterface BraintrustSpanData {\n span: Span;\n spanType: SpanType;\n threadData?: ThreadData; // only populated for MODEL_GENERATION spans\n // Tool results stored when TOOL_CALL ends (may arrive before MODEL_STEP ends)\n pendingToolResults?: Map<string, PendingToolResult>; // keyed by toolCallId\n}\n\nexport interface BraintrustExporterConfig extends TrackingExporterConfig {\n /**\n * Optional Braintrust logger instance.\n * When provided, enables integration with Braintrust contexts such as:\n * - Evals: Agent traces nest inside eval task spans\n * - logger.traced(): Agent traces nest inside traced spans\n * - Parent spans: Auto-detects and attaches to external Braintrust spans\n */\n braintrustLogger?: Logger<true>;\n\n /** Braintrust API key. Required if logger is not provided. */\n apiKey?: string;\n /** Optional custom endpoint */\n endpoint?: string;\n /** Braintrust project name (default: 'mastra-tracing') */\n projectName?: string;\n /** Support tuning parameters */\n tuningParameters?: Record<string, any>;\n}\n\ntype BraintrustRoot = Logger<true> | Span;\ntype BraintrustSpan = BraintrustSpanData;\ntype BraintrustEvent = Span;\ntype BraintrustMetadata = unknown;\ntype BraintrustTraceData = TraceData<BraintrustRoot, BraintrustSpan, BraintrustEvent, BraintrustMetadata>;\n\n// Default span type for all spans\nconst DEFAULT_SPAN_TYPE = 'task';\n\n// Exceptions to the default mapping\nconst SPAN_TYPE_EXCEPTIONS: Partial<Record<SpanType, string>> = {\n [SpanType.MODEL_GENERATION]: 'llm',\n [SpanType.TOOL_CALL]: 'tool',\n [SpanType.MCP_TOOL_CALL]: 'tool',\n [SpanType.WORKFLOW_CONDITIONAL_EVAL]: 'function',\n [SpanType.WORKFLOW_WAIT_EVENT]: 'function',\n};\n\n// Mapping function - returns valid Braintrust span types\nfunction mapSpanType(spanType: SpanType): 'llm' | 'score' | 'function' | 'eval' | 'task' | 'tool' {\n return (SPAN_TYPE_EXCEPTIONS[spanType] as any) ?? DEFAULT_SPAN_TYPE;\n}\n\nexport class BraintrustExporter extends TrackingExporter<\n BraintrustRoot,\n BraintrustSpan,\n BraintrustEvent,\n BraintrustMetadata,\n BraintrustExporterConfig\n> {\n name = 'braintrust';\n\n // Flags and logger for context-aware mode\n #useProvidedLogger: boolean;\n #providedLogger?: Logger<true>;\n #localLogger?: Logger<true>;\n\n constructor(config: BraintrustExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const resolvedApiKey = config.apiKey ?? process.env.BRAINTRUST_API_KEY;\n const resolvedEndpoint = config.endpoint ?? process.env.BRAINTRUST_ENDPOINT;\n\n super({\n ...config,\n apiKey: resolvedApiKey,\n endpoint: resolvedEndpoint,\n });\n\n this.#useProvidedLogger = !!config.braintrustLogger;\n\n if (this.#useProvidedLogger) {\n // Use provided logger - enables Braintrust context integration\n this.#providedLogger = config.braintrustLogger;\n } else {\n // Validate apiKey for creating loggers per trace\n if (!this.config.apiKey) {\n this.setDisabled(\n `Missing required API key. Set BRAINTRUST_API_KEY environment variable or pass apiKey in config.`,\n );\n return;\n }\n // lazy create logger on first rootSpan\n this.#localLogger = undefined;\n }\n }\n\n private async getLocalLogger(): Promise<Logger<true> | undefined> {\n if (this.#localLogger) {\n return this.#localLogger;\n }\n try {\n const logger = await initLogger({\n projectName: this.config.projectName ?? 'mastra-tracing',\n apiKey: this.config.apiKey,\n appUrl: this.config.endpoint,\n ...this.config.tuningParameters,\n });\n this.#localLogger = logger;\n return logger;\n } catch (err) {\n this.logger.error('Braintrust exporter: Failed to initialize logger', { error: err });\n this.setDisabled('Failed to initialize Braintrust logger');\n }\n }\n\n private startSpan(args: { parent: Span | Logger<true>; span: AnyExportedSpan }): BraintrustSpanData {\n const { parent, span } = args;\n const payload = this.buildSpanPayload(span);\n\n // Braintrust's startSpan() accepts data properties via the `event` parameter\n // which maps to StartSpanEventArgs (ExperimentLogPartialArgs & Partial<IdField>)\n // This includes: input, output, metadata, metrics, tags, scores, error, etc.\n const braintrustSpan = parent.startSpan({\n spanId: span.id,\n name: span.name,\n type: mapSpanType(span.type),\n startTime: span.startTime.getTime() / 1000,\n event: {\n id: span.id, // Use Mastra span ID as Braintrust row ID for logFeedback() compatibility\n ...payload,\n },\n });\n\n // Create BraintrustSpanData with span type for tree walking\n // Initialize threadData and pendingToolResults for MODEL_GENERATION spans (used for Thread view reconstruction)\n const isModelGeneration = span.type === SpanType.MODEL_GENERATION;\n return {\n span: braintrustSpan,\n spanType: span.type,\n threadData: isModelGeneration ? [] : undefined,\n pendingToolResults: isModelGeneration ? new Map() : undefined,\n };\n }\n\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<BraintrustRoot | undefined> {\n if (this.#useProvidedLogger) {\n // Try to find a Braintrust span to attach to:\n // 1. Auto-detect from Braintrust's current span (logger.traced(), Eval(), etc.)\n // 2. Fall back to the configured logger\n const externalSpan = currentSpan();\n\n // Check if it's a valid span (not the NOOP_SPAN)\n if (externalSpan && externalSpan.id) {\n // External span detected - attach Mastra traces to it\n return externalSpan;\n } else {\n // No external span - use provided logger\n return this.#providedLogger!;\n }\n } else {\n // Use the local logger\n return this.getLocalLogger();\n }\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<BraintrustSpanData | undefined> {\n const { span, traceData } = args;\n\n if (span.isRootSpan) {\n const root = traceData.getRoot();\n if (root) {\n return this.startSpan({ parent: root, span });\n }\n } else {\n const parent = traceData.getParent(args);\n if (parent) {\n // Parent could be BraintrustSpanData (has .span) or BraintrustRoot (Logger/Span, no .span)\n const parentSpan = 'span' in parent ? parent.span : parent;\n return this.startSpan({ parent: parentSpan, span });\n }\n }\n }\n\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<Span | undefined> {\n const spanData = await this._buildSpan(args);\n\n if (!spanData) {\n // parent doesn't exist and not creating rootSpan, return early data\n return;\n }\n\n spanData.span.end({ endTime: args.span.startTime.getTime() / 1000 });\n return spanData.span;\n }\n\n protected override async _updateSpan(args: { span: AnyExportedSpan; traceData: BraintrustTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n const spanData = traceData.getSpan({ spanId: span.id });\n if (!spanData) {\n return;\n }\n spanData.span.log(this.buildSpanPayload(span, false));\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: BraintrustTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n const spanData = traceData.getSpan({ spanId: span.id });\n if (!spanData) {\n return;\n }\n\n // Handle thread data accumulation for MODEL_STEP and TOOL_CALL spans\n if (span.type === SpanType.MODEL_STEP) {\n this.accumulateModelStepData(span, traceData);\n } else if (span.type === SpanType.TOOL_CALL) {\n this.accumulateToolCallResult(span, traceData);\n }\n\n // Build payload - for MODEL_GENERATION, may reconstruct output from threadData\n const payload =\n span.type === SpanType.MODEL_GENERATION\n ? this.buildModelGenerationPayload(span, spanData)\n : this.buildSpanPayload(span, false);\n\n spanData.span.log(payload);\n\n if (span.endTime) {\n spanData.span.end({ endTime: span.endTime.getTime() / 1000 });\n } else {\n spanData.span.end();\n }\n }\n\n protected override async _abortSpan(args: { span: BraintrustSpan; reason: SpanErrorInfo }): Promise<void> {\n const { span: spanData, reason } = args;\n spanData.span.log({\n error: reason.message,\n metadata: { errorDetails: reason },\n });\n spanData.span.end();\n }\n\n // ==============================================================================\n // Thread view reconstruction helpers\n // ==============================================================================\n\n /**\n * Walk up the tree to find the MODEL_GENERATION ancestor span.\n * Returns the BraintrustSpanData if found, undefined otherwise.\n */\n private findModelGenerationAncestor(spanId: string, traceData: BraintrustTraceData): BraintrustSpanData | undefined {\n let currentId: string | undefined = spanId;\n\n while (currentId) {\n const parentId = traceData.getParentId({ spanId: currentId });\n if (!parentId) return undefined;\n\n const parentSpanData = traceData.getSpan({ spanId: parentId });\n if (parentSpanData?.spanType === SpanType.MODEL_GENERATION) {\n return parentSpanData;\n }\n currentId = parentId;\n }\n\n return undefined;\n }\n\n /**\n * Accumulate MODEL_STEP data to the parent MODEL_GENERATION's threadData.\n * Called when a MODEL_STEP span ends.\n */\n private accumulateModelStepData(span: AnyExportedSpan, traceData: BraintrustTraceData): void {\n const modelGenSpanData = this.findModelGenerationAncestor(span.id, traceData);\n if (!modelGenSpanData?.threadData) {\n return;\n }\n\n // Extract step data from MODEL_STEP output and attributes\n const output = span.output as\n | { text?: string; toolCalls?: Array<{ toolCallId: string; toolName: string; args: unknown }> }\n | undefined;\n const attributes = span.attributes as { stepIndex?: number } | undefined;\n\n const stepData: ThreadStepData = {\n stepSpanId: span.id,\n stepIndex: attributes?.stepIndex ?? 0,\n text: output?.text,\n toolCalls: output?.toolCalls?.map(tc => ({\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: tc.args,\n })),\n };\n\n modelGenSpanData.threadData.push(stepData);\n }\n\n /**\n * Store TOOL_CALL result in parent MODEL_GENERATION's pendingToolResults.\n * Called when a TOOL_CALL span ends.\n * Results are merged into threadData when MODEL_GENERATION ends.\n */\n private accumulateToolCallResult(span: AnyExportedSpan, traceData: BraintrustTraceData): void {\n const modelGenSpanData = this.findModelGenerationAncestor(span.id, traceData);\n if (!modelGenSpanData?.pendingToolResults) {\n return;\n }\n\n // Extract tool call ID from TOOL_CALL span input\n const input = span.input as { toolCallId?: string } | undefined;\n const toolCallId = input?.toolCallId;\n if (!toolCallId) {\n return;\n }\n\n // Store the result for later merging\n modelGenSpanData.pendingToolResults.set(toolCallId, {\n result: span.output,\n startTime: span.startTime,\n });\n }\n\n /**\n * Build the payload for MODEL_GENERATION span, reconstructing output from threadData if available.\n */\n private buildModelGenerationPayload(span: AnyExportedSpan, spanData: BraintrustSpanData): Record<string, any> {\n const basePayload = this.buildSpanPayload(span, false);\n\n // Check if we have threadData with tool calls to reconstruct\n const threadData = spanData.threadData;\n if (!threadData || threadData.length === 0) {\n return basePayload;\n }\n\n // Merge pending tool results into threadData\n if (spanData.pendingToolResults && spanData.pendingToolResults.size > 0) {\n for (const step of threadData) {\n if (step.toolCalls) {\n for (const toolCall of step.toolCalls) {\n const pendingResult = spanData.pendingToolResults.get(toolCall.toolCallId);\n if (pendingResult) {\n toolCall.result = pendingResult.result;\n toolCall.startTime = pendingResult.startTime;\n }\n }\n }\n }\n }\n\n // Check if any step has tool calls\n const hasToolCalls = threadData.some(step => step.toolCalls && step.toolCalls.length > 0);\n if (!hasToolCalls) {\n return basePayload;\n }\n\n // Reconstruct output as OpenAI messages\n const reconstructedOutput = reconstructThreadOutput(threadData, span.output);\n return {\n ...basePayload,\n output: reconstructedOutput,\n };\n }\n\n /**\n * Transforms MODEL_GENERATION input to Braintrust Thread view format.\n * Converts AI SDK messages (v4/v5) to OpenAI Chat Completion format, which Braintrust requires\n * for proper rendering of threads (fixes #11023).\n */\n private transformInput(input: unknown, spanType: SpanType): unknown {\n if (spanType === SpanType.MODEL_GENERATION) {\n // If input is already an array of messages, convert AI SDK format to OpenAI format\n if (Array.isArray(input)) {\n return input.map((msg: unknown) => convertAISDKMessage(msg));\n }\n\n // If input has a messages array\n if (\n input &&\n typeof input === 'object' &&\n 'messages' in input &&\n Array.isArray((input as { messages: unknown[] }).messages)\n ) {\n return (input as { messages: unknown[] }).messages.map((msg: unknown) => convertAISDKMessage(msg));\n }\n }\n\n return input;\n }\n\n /**\n * Transforms MODEL_GENERATION output to Braintrust Thread view format.\n */\n private transformOutput(output: any, spanType: SpanType): any {\n if (spanType === SpanType.MODEL_GENERATION) {\n if (!output || typeof output !== 'object') {\n return output;\n }\n const { text, ...rest } = output;\n // Remove null/undefined values from rest to keep Thread view clean\n return { role: 'assistant', content: text, ...removeNullish(rest) };\n }\n\n return output;\n }\n\n private buildSpanPayload(span: AnyExportedSpan, isCreate = true): Record<string, any> {\n const payload: Record<string, any> = {};\n\n if (span.input !== undefined) {\n payload.input = this.transformInput(span.input, span.type);\n }\n\n if (span.output !== undefined) {\n payload.output = this.transformOutput(span.output, span.type);\n }\n\n if (isCreate && span.isRootSpan && span.tags?.length) {\n payload.tags = span.tags;\n }\n\n // Initialize metrics and metadata objects\n payload.metrics = {};\n // Spread span.metadata first, then set spanType to prevent accidental override\n payload.metadata = {\n ...span.metadata,\n spanType: span.type,\n };\n\n if (isCreate) {\n payload.metadata['mastra-trace-id'] = span.traceId;\n }\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = attributes as ModelGenerationAttributes;\n\n // Model goes to metadata\n if (modelAttr.model !== undefined) {\n payload.metadata.model = modelAttr.model;\n }\n\n // Provider goes to metadata (if provided by attributes)\n if (modelAttr.provider !== undefined) {\n payload.metadata.provider = modelAttr.provider;\n }\n\n // Usage/token info goes to metrics\n payload.metrics = formatUsageMetrics(modelAttr.usage);\n\n // Time to first token (TTFT) for streaming responses\n // Braintrust expects TTFT in seconds (not milliseconds)\n if (modelAttr.completionStartTime) {\n payload.metrics.time_to_first_token =\n (modelAttr.completionStartTime.getTime() - span.startTime.getTime()) / 1000;\n }\n\n // Model parameters go to metadata\n if (modelAttr.parameters !== undefined) {\n payload.metadata.modelParameters = modelAttr.parameters;\n }\n\n // Other LLM attributes go to metadata\n const otherAttributes = omitKeys(attributes, ['model', 'usage', 'parameters', 'completionStartTime']);\n payload.metadata = {\n ...payload.metadata,\n ...otherAttributes,\n };\n } else {\n // For non-LLM spans, put all attributes in metadata\n payload.metadata = {\n ...payload.metadata,\n ...attributes,\n };\n }\n\n // Handle errors\n if (span.errorInfo) {\n payload.error = span.errorInfo.message;\n payload.metadata.errorDetails = span.errorInfo;\n }\n\n // Clean up empty metrics object\n if (Object.keys(payload.metrics).length === 0) {\n delete payload.metrics;\n }\n\n // Remove null/undefined values from metadata to keep Braintrust UI clean\n payload.metadata = removeNullish(payload.metadata);\n\n return payload;\n }\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -278,9 +278,11 @@ var BraintrustExporter = class extends TrackingExporter {
|
|
|
278
278
|
name: span.name,
|
|
279
279
|
type: mapSpanType(span.type),
|
|
280
280
|
startTime: span.startTime.getTime() / 1e3,
|
|
281
|
-
event: {
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
event: {
|
|
282
|
+
id: span.id,
|
|
283
|
+
// Use Mastra span ID as Braintrust row ID for logFeedback() compatibility
|
|
284
|
+
...payload
|
|
285
|
+
}
|
|
284
286
|
});
|
|
285
287
|
const isModelGeneration = span.type === SpanType.MODEL_GENERATION;
|
|
286
288
|
return {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/formatter.ts","../src/metrics.ts","../src/thread-reconstruction.ts","../src/tracing.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Message format conversion utilities for Braintrust.\n *\n * Converts AI SDK message format (v4/v5) to OpenAI Chat Completion format,\n * which Braintrust requires for proper rendering of threads.\n */\n\n// ==============================================================================\n// Utility functions\n// ==============================================================================\n\n/**\n * Remove null and undefined values from an object (shallow)\n */\nexport function removeNullish<T extends Record<string, unknown>>(obj: T): Partial<T> {\n return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null)) as Partial<T>;\n}\n\n// ==============================================================================\n// Type definitions for AI SDK message format conversion to OpenAI format\n// ==============================================================================\n\n/**\n * AI SDK content part types (both v4 and v5)\n */\ninterface AISDKTextPart {\n type: 'text';\n text: string;\n}\n\ninterface AISDKImagePart {\n type: 'image';\n image?: string | Uint8Array | URL;\n mimeType?: string;\n}\n\ninterface AISDKFilePart {\n type: 'file';\n data?: string | Uint8Array | URL;\n filename?: string;\n name?: string;\n mimeType?: string;\n}\n\ninterface AISDKReasoningPart {\n type: 'reasoning';\n text?: string;\n}\n\ninterface AISDKToolCallPart {\n type: 'tool-call';\n toolCallId: string;\n toolName: string;\n args?: unknown; // AI SDK v4\n input?: unknown; // AI SDK v5\n}\n\ninterface AISDKToolResultPart {\n type: 'tool-result';\n toolCallId: string;\n result?: unknown; // AI SDK v4\n output?: unknown; // AI SDK v5\n}\n\ntype AISDKContentPart =\n | AISDKTextPart\n | AISDKImagePart\n | AISDKFilePart\n | AISDKReasoningPart\n | AISDKToolCallPart\n | AISDKToolResultPart\n | { type: string; [key: string]: unknown }; // Catch-all for unknown types\n\n/**\n * AI SDK message format (input format for conversion)\n */\ninterface AISDKMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string | AISDKContentPart[];\n [key: string]: unknown; // Allow additional properties\n}\n\n/**\n * OpenAI Chat Completion tool call format\n */\nexport interface OpenAIToolCall {\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n}\n\n/**\n * OpenAI Chat Completion message format (output format)\n */\nexport interface OpenAIMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n [key: string]: unknown; // Allow additional properties\n}\n\n// ==============================================================================\n// Message conversion functions\n// ==============================================================================\n\n/**\n * Converts a content part to a string representation.\n * Handles text, image, file, reasoning, and other content types.\n */\nfunction convertContentPart(part: AISDKContentPart | null | undefined): string | null {\n if (!part || typeof part !== 'object') {\n return null;\n }\n\n switch (part.type) {\n case 'text':\n return (part as AISDKTextPart).text || null;\n\n case 'image':\n // Represent image content with a placeholder\n return '[image]';\n\n case 'file': {\n // Represent file content with filename if available\n const filePart = part as AISDKFilePart;\n if (filePart.filename || filePart.name) {\n return `[file: ${filePart.filename || filePart.name}]`;\n }\n return '[file]';\n }\n\n case 'reasoning': {\n // Represent reasoning/thinking content\n const reasoningPart = part as AISDKReasoningPart;\n if (typeof reasoningPart.text === 'string' && reasoningPart.text.length > 0) {\n return `[reasoning: ${reasoningPart.text.substring(0, 100)}${reasoningPart.text.length > 100 ? '...' : ''}]`;\n }\n return '[reasoning]';\n }\n\n case 'tool-call':\n // Tool calls are handled separately in assistant messages\n return null;\n\n case 'tool-result':\n // Tool results are handled separately in tool messages\n return null;\n\n default: {\n // For unknown types, try to extract any text-like content\n const unknownPart = part as { type?: string; text?: string; content?: string };\n if (typeof unknownPart.text === 'string') {\n return unknownPart.text;\n }\n if (typeof unknownPart.content === 'string') {\n return unknownPart.content;\n }\n // Represent unknown content type\n return `[${unknownPart.type || 'unknown'}]`;\n }\n }\n}\n\n/**\n * Serializes tool result data to a string for OpenAI format.\n */\nfunction serializeToolResult(resultData: unknown): string {\n if (typeof resultData === 'string') {\n return resultData;\n }\n if (resultData && typeof resultData === 'object' && 'value' in resultData) {\n const valueData = (resultData as { value: unknown }).value;\n return typeof valueData === 'string' ? valueData : JSON.stringify(valueData);\n }\n if (resultData === undefined || resultData === null) {\n return '';\n }\n try {\n return JSON.stringify(resultData);\n } catch {\n return '[unserializable result]';\n }\n}\n\n/**\n * Converts AI SDK message format to OpenAI Chat Completion format for Braintrust.\n *\n * Supports both AI SDK v4 and v5 formats:\n * - v4 uses 'args' for tool calls and 'result' for tool results\n * - v5 uses 'input' for tool calls and 'output' for tool results\n *\n * AI SDK format:\n * { role: \"user\", content: [{ type: \"text\", text: \"hello\" }] }\n * { role: \"assistant\", content: [{ type: \"text\", text: \"...\" }, { type: \"tool-call\", toolCallId: \"...\", toolName: \"...\", args: {...} }] }\n * { role: \"tool\", content: [{ type: \"tool-result\", toolCallId: \"...\", result: {...} }] }\n *\n * OpenAI format (what Braintrust expects):\n * { role: \"user\", content: \"hello\" }\n * { role: \"assistant\", content: \"...\", tool_calls: [{ id: \"...\", type: \"function\", function: { name: \"...\", arguments: \"...\" } }] }\n * { role: \"tool\", content: \"result\", tool_call_id: \"...\" }\n */\nexport function convertAISDKMessage(message: AISDKMessage | OpenAIMessage | unknown): OpenAIMessage | unknown {\n if (!message || typeof message !== 'object') {\n return message;\n }\n\n const { role, content, ...rest } = message as AISDKMessage;\n\n // If content is already a string, return as-is (already in OpenAI format)\n if (typeof content === 'string') {\n return message;\n }\n\n // If content is an array (AI SDK format), convert based on role\n if (Array.isArray(content)) {\n // Handle empty content arrays\n if (content.length === 0) {\n return { role, content: '', ...rest };\n }\n\n // For user/system messages, extract text and represent non-text content\n if (role === 'user' || role === 'system') {\n const contentParts = content.map((part: AISDKContentPart) => convertContentPart(part)).filter(Boolean);\n\n return {\n role,\n content: contentParts.length > 0 ? contentParts.join('\\n') : '',\n ...rest,\n };\n }\n\n // For assistant messages, extract text, non-text content, AND tool calls\n if (role === 'assistant') {\n const contentParts = content\n .filter((part: AISDKContentPart) => part?.type !== 'tool-call')\n .map((part: AISDKContentPart) => convertContentPart(part))\n .filter(Boolean);\n\n const toolCallParts = content.filter((part: AISDKContentPart) => part?.type === 'tool-call');\n\n const result: OpenAIMessage = {\n role,\n content: contentParts.length > 0 ? (contentParts as string[]).join('\\n') : '',\n ...rest,\n };\n\n // Add tool_calls array if there are tool calls\n if (toolCallParts.length > 0) {\n result.tool_calls = toolCallParts.map((tc: AISDKContentPart) => {\n const toolCall = tc as AISDKToolCallPart;\n const toolCallId = toolCall.toolCallId;\n const toolName = toolCall.toolName;\n // Support both v4 'args' and v5 'input'\n const args = toolCall.args ?? toolCall.input;\n\n let argsString: string;\n if (typeof args === 'string') {\n argsString = args;\n } else if (args !== undefined && args !== null) {\n argsString = JSON.stringify(args);\n } else {\n argsString = '{}';\n }\n\n return {\n id: toolCallId,\n type: 'function' as const,\n function: {\n name: toolName,\n arguments: argsString,\n },\n };\n });\n }\n\n return result;\n }\n\n // For tool messages, convert to OpenAI tool message format\n if (role === 'tool') {\n const toolResult = content.find((part): part is AISDKToolResultPart => part?.type === 'tool-result');\n if (toolResult) {\n // Support both v4 'result' and v5 'output' fields\n const resultData = toolResult.output ?? toolResult.result;\n const resultContent = serializeToolResult(resultData);\n\n return {\n role: 'tool',\n content: resultContent,\n tool_call_id: toolResult.toolCallId,\n } as OpenAIMessage;\n }\n }\n }\n\n return message;\n}\n","import type { UsageStats } from '@mastra/core/observability';\n\n/**\n * BraintrustUsageMetrics\n *\n * Canonical metric keys expected by Braintrust for LLM usage accounting.\n */\nexport interface BraintrustUsageMetrics {\n prompt_tokens?: number;\n completion_tokens?: number;\n tokens?: number;\n completion_reasoning_tokens?: number;\n prompt_cached_tokens?: number;\n prompt_cache_creation_tokens?: number;\n}\n\n/**\n * Formats UsageStats to Braintrust's expected metric format.\n */\nexport function formatUsageMetrics(usage?: UsageStats): BraintrustUsageMetrics {\n const metrics: BraintrustUsageMetrics = {};\n\n if (usage?.inputTokens !== undefined) {\n metrics.prompt_tokens = usage.inputTokens;\n }\n\n if (usage?.outputTokens !== undefined) {\n metrics.completion_tokens = usage.outputTokens;\n }\n\n // Compute total if we have both\n if (metrics.prompt_tokens !== undefined && metrics.completion_tokens !== undefined) {\n metrics.tokens = metrics.prompt_tokens + metrics.completion_tokens;\n }\n\n if (usage?.outputDetails?.reasoning !== undefined) {\n metrics.completion_reasoning_tokens = usage.outputDetails.reasoning;\n }\n\n if (usage?.inputDetails?.cacheRead !== undefined) {\n metrics.prompt_cached_tokens = usage.inputDetails.cacheRead;\n }\n\n if (usage?.inputDetails?.cacheWrite !== undefined) {\n metrics.prompt_cache_creation_tokens = usage.inputDetails.cacheWrite;\n }\n\n return metrics;\n}\n","/**\n * Thread view reconstruction for Braintrust.\n *\n * Reconstructs LLM output in OpenAI Chat Completion format by examining\n * child MODEL_STEP and TOOL_CALL spans. This enables Braintrust's Thread\n * view to properly display the full conversation flow including tool calls.\n *\n * See THREAD_VIEW_RECONSTRUCTION.md for details.\n */\n\nimport { removeNullish } from './formatter';\nimport type { OpenAIMessage } from './formatter';\n\n// ==============================================================================\n// Thread view reconstruction types\n// ==============================================================================\n\n/**\n * Tool call data accumulated from MODEL_STEP and TOOL_CALL spans\n */\nexport interface ThreadToolCall {\n toolCallId: string;\n toolName: string;\n args: unknown;\n result?: unknown; // filled in when TOOL_CALL ends\n startTime?: Date; // from TOOL_CALL span, for ordering multiple tool calls within a step\n}\n\n/**\n * Step data accumulated from MODEL_STEP spans for Thread view reconstruction\n */\nexport interface ThreadStepData {\n stepSpanId: string;\n stepIndex: number; // for ordering steps correctly\n text?: string;\n toolCalls?: ThreadToolCall[];\n}\n\n/**\n * Accumulated data for reconstructing Braintrust Thread view.\n * Populated for MODEL_GENERATION spans as child MODEL_STEP and TOOL_CALL spans complete.\n */\nexport type ThreadData = ThreadStepData[];\n\n/**\n * Tool result data stored when TOOL_CALL spans end (before MODEL_STEP ends)\n */\nexport interface PendingToolResult {\n result: unknown;\n startTime: Date;\n}\n\n// ==============================================================================\n// Thread view reconstruction\n// ==============================================================================\n\n/**\n * Reconstruct the Thread view output from accumulated threadData.\n * Converts to OpenAI Chat Completion message format.\n */\nexport function reconstructThreadOutput(threadData: ThreadData, originalOutput: unknown): OpenAIMessage[] {\n const messages: OpenAIMessage[] = [];\n\n // Sort steps by stepIndex\n const sortedSteps = [...threadData].sort((a, b) => a.stepIndex - b.stepIndex);\n\n for (const step of sortedSteps) {\n // Sort tool calls by startTime within each step\n const sortedToolCalls = step.toolCalls\n ? [...step.toolCalls].sort((a, b) => {\n if (!a.startTime || !b.startTime) return 0;\n return a.startTime.getTime() - b.startTime.getTime();\n })\n : [];\n\n if (sortedToolCalls.length > 0) {\n // Add assistant message with tool_calls\n messages.push({\n role: 'assistant',\n content: step.text || '',\n tool_calls: sortedToolCalls.map(tc => {\n // Clean null/undefined values from args before stringifying\n const cleanArgs =\n tc.args && typeof tc.args === 'object' && !Array.isArray(tc.args)\n ? removeNullish(tc.args as Record<string, unknown>)\n : tc.args;\n return {\n id: tc.toolCallId,\n type: 'function' as const,\n function: {\n name: tc.toolName,\n arguments: typeof cleanArgs === 'string' ? cleanArgs : JSON.stringify(cleanArgs),\n },\n };\n }),\n });\n\n // Add tool messages for each result\n for (const tc of sortedToolCalls) {\n if (tc.result !== undefined) {\n messages.push({\n role: 'tool',\n content: typeof tc.result === 'string' ? tc.result : JSON.stringify(tc.result),\n tool_call_id: tc.toolCallId,\n });\n }\n }\n } else if (step.text) {\n // Step with text only (final response)\n messages.push({\n role: 'assistant',\n content: step.text,\n });\n }\n }\n\n // If we have messages and the last one is a tool message,\n // add the final assistant text from original output\n if (messages.length > 0) {\n const lastMessage = messages[messages.length - 1]!;\n const originalText = (originalOutput as { text?: string })?.text;\n\n // If the last message is a tool response and we have final text, add it\n if (originalText && lastMessage.role === 'tool') {\n messages.push({\n role: 'assistant',\n content: originalText,\n });\n }\n }\n\n return messages;\n}\n","/**\n * Braintrust Exporter for Mastra Observability\n *\n * This exporter sends observability data to Braintrust.\n * Root spans become top-level Braintrust spans (no trace wrapper).\n * Events are handled as zero-duration spans with matching start/end times.\n */\n\nimport type { AnyExportedSpan, ModelGenerationAttributes, SpanErrorInfo } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport { omitKeys } from '@mastra/core/utils';\nimport { TrackingExporter } from '@mastra/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport { initLogger, currentSpan } from 'braintrust';\nimport type { Span, Logger } from 'braintrust';\nimport { removeNullish, convertAISDKMessage } from './formatter';\nimport { formatUsageMetrics } from './metrics';\nimport { reconstructThreadOutput } from './thread-reconstruction';\nimport type { ThreadData, ThreadStepData, PendingToolResult } from './thread-reconstruction';\n\n/**\n * Extended Braintrust span data that includes span type and thread reconstruction data\n */\ninterface BraintrustSpanData {\n span: Span;\n spanType: SpanType;\n threadData?: ThreadData; // only populated for MODEL_GENERATION spans\n // Tool results stored when TOOL_CALL ends (may arrive before MODEL_STEP ends)\n pendingToolResults?: Map<string, PendingToolResult>; // keyed by toolCallId\n}\n\nexport interface BraintrustExporterConfig extends TrackingExporterConfig {\n /**\n * Optional Braintrust logger instance.\n * When provided, enables integration with Braintrust contexts such as:\n * - Evals: Agent traces nest inside eval task spans\n * - logger.traced(): Agent traces nest inside traced spans\n * - Parent spans: Auto-detects and attaches to external Braintrust spans\n */\n braintrustLogger?: Logger<true>;\n\n /** Braintrust API key. Required if logger is not provided. */\n apiKey?: string;\n /** Optional custom endpoint */\n endpoint?: string;\n /** Braintrust project name (default: 'mastra-tracing') */\n projectName?: string;\n /** Support tuning parameters */\n tuningParameters?: Record<string, any>;\n}\n\ntype BraintrustRoot = Logger<true> | Span;\ntype BraintrustSpan = BraintrustSpanData;\ntype BraintrustEvent = Span;\ntype BraintrustMetadata = unknown;\ntype BraintrustTraceData = TraceData<BraintrustRoot, BraintrustSpan, BraintrustEvent, BraintrustMetadata>;\n\n// Default span type for all spans\nconst DEFAULT_SPAN_TYPE = 'task';\n\n// Exceptions to the default mapping\nconst SPAN_TYPE_EXCEPTIONS: Partial<Record<SpanType, string>> = {\n [SpanType.MODEL_GENERATION]: 'llm',\n [SpanType.TOOL_CALL]: 'tool',\n [SpanType.MCP_TOOL_CALL]: 'tool',\n [SpanType.WORKFLOW_CONDITIONAL_EVAL]: 'function',\n [SpanType.WORKFLOW_WAIT_EVENT]: 'function',\n};\n\n// Mapping function - returns valid Braintrust span types\nfunction mapSpanType(spanType: SpanType): 'llm' | 'score' | 'function' | 'eval' | 'task' | 'tool' {\n return (SPAN_TYPE_EXCEPTIONS[spanType] as any) ?? DEFAULT_SPAN_TYPE;\n}\n\nexport class BraintrustExporter extends TrackingExporter<\n BraintrustRoot,\n BraintrustSpan,\n BraintrustEvent,\n BraintrustMetadata,\n BraintrustExporterConfig\n> {\n name = 'braintrust';\n\n // Flags and logger for context-aware mode\n #useProvidedLogger: boolean;\n #providedLogger?: Logger<true>;\n #localLogger?: Logger<true>;\n\n constructor(config: BraintrustExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const resolvedApiKey = config.apiKey ?? process.env.BRAINTRUST_API_KEY;\n const resolvedEndpoint = config.endpoint ?? process.env.BRAINTRUST_ENDPOINT;\n\n super({\n ...config,\n apiKey: resolvedApiKey,\n endpoint: resolvedEndpoint,\n });\n\n this.#useProvidedLogger = !!config.braintrustLogger;\n\n if (this.#useProvidedLogger) {\n // Use provided logger - enables Braintrust context integration\n this.#providedLogger = config.braintrustLogger;\n } else {\n // Validate apiKey for creating loggers per trace\n if (!this.config.apiKey) {\n this.setDisabled(\n `Missing required API key. Set BRAINTRUST_API_KEY environment variable or pass apiKey in config.`,\n );\n return;\n }\n // lazy create logger on first rootSpan\n this.#localLogger = undefined;\n }\n }\n\n private async getLocalLogger(): Promise<Logger<true> | undefined> {\n if (this.#localLogger) {\n return this.#localLogger;\n }\n try {\n const logger = await initLogger({\n projectName: this.config.projectName ?? 'mastra-tracing',\n apiKey: this.config.apiKey,\n appUrl: this.config.endpoint,\n ...this.config.tuningParameters,\n });\n this.#localLogger = logger;\n return logger;\n } catch (err) {\n this.logger.error('Braintrust exporter: Failed to initialize logger', { error: err });\n this.setDisabled('Failed to initialize Braintrust logger');\n }\n }\n\n private startSpan(args: { parent: Span | Logger<true>; span: AnyExportedSpan }): BraintrustSpanData {\n const { parent, span } = args;\n const payload = this.buildSpanPayload(span);\n const braintrustSpan = parent.startSpan({\n spanId: span.id,\n name: span.name,\n type: mapSpanType(span.type),\n startTime: span.startTime.getTime() / 1000,\n event: { id: span.id }, // Use Mastra span ID as Braintrust row ID for logFeedback() compatibility\n ...payload,\n });\n\n // Create BraintrustSpanData with span type for tree walking\n // Initialize threadData and pendingToolResults for MODEL_GENERATION spans (used for Thread view reconstruction)\n const isModelGeneration = span.type === SpanType.MODEL_GENERATION;\n return {\n span: braintrustSpan,\n spanType: span.type,\n threadData: isModelGeneration ? [] : undefined,\n pendingToolResults: isModelGeneration ? new Map() : undefined,\n };\n }\n\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<BraintrustRoot | undefined> {\n if (this.#useProvidedLogger) {\n // Try to find a Braintrust span to attach to:\n // 1. Auto-detect from Braintrust's current span (logger.traced(), Eval(), etc.)\n // 2. Fall back to the configured logger\n const externalSpan = currentSpan();\n\n // Check if it's a valid span (not the NOOP_SPAN)\n if (externalSpan && externalSpan.id) {\n // External span detected - attach Mastra traces to it\n return externalSpan;\n } else {\n // No external span - use provided logger\n return this.#providedLogger!;\n }\n } else {\n // Use the local logger\n return this.getLocalLogger();\n }\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<BraintrustSpanData | undefined> {\n const { span, traceData } = args;\n\n if (span.isRootSpan) {\n const root = traceData.getRoot();\n if (root) {\n return this.startSpan({ parent: root, span });\n }\n } else {\n const parent = traceData.getParent(args);\n if (parent) {\n // Parent could be BraintrustSpanData (has .span) or BraintrustRoot (Logger/Span, no .span)\n const parentSpan = 'span' in parent ? parent.span : parent;\n return this.startSpan({ parent: parentSpan, span });\n }\n }\n }\n\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<Span | undefined> {\n const spanData = await this._buildSpan(args);\n\n if (!spanData) {\n // parent doesn't exist and not creating rootSpan, return early data\n return;\n }\n\n spanData.span.end({ endTime: args.span.startTime.getTime() / 1000 });\n return spanData.span;\n }\n\n protected override async _updateSpan(args: { span: AnyExportedSpan; traceData: BraintrustTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n const spanData = traceData.getSpan({ spanId: span.id });\n if (!spanData) {\n return;\n }\n spanData.span.log(this.buildSpanPayload(span, false));\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: BraintrustTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n const spanData = traceData.getSpan({ spanId: span.id });\n if (!spanData) {\n return;\n }\n\n // Handle thread data accumulation for MODEL_STEP and TOOL_CALL spans\n if (span.type === SpanType.MODEL_STEP) {\n this.accumulateModelStepData(span, traceData);\n } else if (span.type === SpanType.TOOL_CALL) {\n this.accumulateToolCallResult(span, traceData);\n }\n\n // Build payload - for MODEL_GENERATION, may reconstruct output from threadData\n const payload =\n span.type === SpanType.MODEL_GENERATION\n ? this.buildModelGenerationPayload(span, spanData)\n : this.buildSpanPayload(span, false);\n\n spanData.span.log(payload);\n\n if (span.endTime) {\n spanData.span.end({ endTime: span.endTime.getTime() / 1000 });\n } else {\n spanData.span.end();\n }\n }\n\n protected override async _abortSpan(args: { span: BraintrustSpan; reason: SpanErrorInfo }): Promise<void> {\n const { span: spanData, reason } = args;\n spanData.span.log({\n error: reason.message,\n metadata: { errorDetails: reason },\n });\n spanData.span.end();\n }\n\n // ==============================================================================\n // Thread view reconstruction helpers\n // ==============================================================================\n\n /**\n * Walk up the tree to find the MODEL_GENERATION ancestor span.\n * Returns the BraintrustSpanData if found, undefined otherwise.\n */\n private findModelGenerationAncestor(spanId: string, traceData: BraintrustTraceData): BraintrustSpanData | undefined {\n let currentId: string | undefined = spanId;\n\n while (currentId) {\n const parentId = traceData.getParentId({ spanId: currentId });\n if (!parentId) return undefined;\n\n const parentSpanData = traceData.getSpan({ spanId: parentId });\n if (parentSpanData?.spanType === SpanType.MODEL_GENERATION) {\n return parentSpanData;\n }\n currentId = parentId;\n }\n\n return undefined;\n }\n\n /**\n * Accumulate MODEL_STEP data to the parent MODEL_GENERATION's threadData.\n * Called when a MODEL_STEP span ends.\n */\n private accumulateModelStepData(span: AnyExportedSpan, traceData: BraintrustTraceData): void {\n const modelGenSpanData = this.findModelGenerationAncestor(span.id, traceData);\n if (!modelGenSpanData?.threadData) {\n return;\n }\n\n // Extract step data from MODEL_STEP output and attributes\n const output = span.output as\n | { text?: string; toolCalls?: Array<{ toolCallId: string; toolName: string; args: unknown }> }\n | undefined;\n const attributes = span.attributes as { stepIndex?: number } | undefined;\n\n const stepData: ThreadStepData = {\n stepSpanId: span.id,\n stepIndex: attributes?.stepIndex ?? 0,\n text: output?.text,\n toolCalls: output?.toolCalls?.map(tc => ({\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: tc.args,\n })),\n };\n\n modelGenSpanData.threadData.push(stepData);\n }\n\n /**\n * Store TOOL_CALL result in parent MODEL_GENERATION's pendingToolResults.\n * Called when a TOOL_CALL span ends.\n * Results are merged into threadData when MODEL_GENERATION ends.\n */\n private accumulateToolCallResult(span: AnyExportedSpan, traceData: BraintrustTraceData): void {\n const modelGenSpanData = this.findModelGenerationAncestor(span.id, traceData);\n if (!modelGenSpanData?.pendingToolResults) {\n return;\n }\n\n // Extract tool call ID from TOOL_CALL span input\n const input = span.input as { toolCallId?: string } | undefined;\n const toolCallId = input?.toolCallId;\n if (!toolCallId) {\n return;\n }\n\n // Store the result for later merging\n modelGenSpanData.pendingToolResults.set(toolCallId, {\n result: span.output,\n startTime: span.startTime,\n });\n }\n\n /**\n * Build the payload for MODEL_GENERATION span, reconstructing output from threadData if available.\n */\n private buildModelGenerationPayload(span: AnyExportedSpan, spanData: BraintrustSpanData): Record<string, any> {\n const basePayload = this.buildSpanPayload(span, false);\n\n // Check if we have threadData with tool calls to reconstruct\n const threadData = spanData.threadData;\n if (!threadData || threadData.length === 0) {\n return basePayload;\n }\n\n // Merge pending tool results into threadData\n if (spanData.pendingToolResults && spanData.pendingToolResults.size > 0) {\n for (const step of threadData) {\n if (step.toolCalls) {\n for (const toolCall of step.toolCalls) {\n const pendingResult = spanData.pendingToolResults.get(toolCall.toolCallId);\n if (pendingResult) {\n toolCall.result = pendingResult.result;\n toolCall.startTime = pendingResult.startTime;\n }\n }\n }\n }\n }\n\n // Check if any step has tool calls\n const hasToolCalls = threadData.some(step => step.toolCalls && step.toolCalls.length > 0);\n if (!hasToolCalls) {\n return basePayload;\n }\n\n // Reconstruct output as OpenAI messages\n const reconstructedOutput = reconstructThreadOutput(threadData, span.output);\n return {\n ...basePayload,\n output: reconstructedOutput,\n };\n }\n\n /**\n * Transforms MODEL_GENERATION input to Braintrust Thread view format.\n * Converts AI SDK messages (v4/v5) to OpenAI Chat Completion format, which Braintrust requires\n * for proper rendering of threads (fixes #11023).\n */\n private transformInput(input: unknown, spanType: SpanType): unknown {\n if (spanType === SpanType.MODEL_GENERATION) {\n // If input is already an array of messages, convert AI SDK format to OpenAI format\n if (Array.isArray(input)) {\n return input.map((msg: unknown) => convertAISDKMessage(msg));\n }\n\n // If input has a messages array\n if (\n input &&\n typeof input === 'object' &&\n 'messages' in input &&\n Array.isArray((input as { messages: unknown[] }).messages)\n ) {\n return (input as { messages: unknown[] }).messages.map((msg: unknown) => convertAISDKMessage(msg));\n }\n }\n\n return input;\n }\n\n /**\n * Transforms MODEL_GENERATION output to Braintrust Thread view format.\n */\n private transformOutput(output: any, spanType: SpanType): any {\n if (spanType === SpanType.MODEL_GENERATION) {\n if (!output || typeof output !== 'object') {\n return output;\n }\n const { text, ...rest } = output;\n // Remove null/undefined values from rest to keep Thread view clean\n return { role: 'assistant', content: text, ...removeNullish(rest) };\n }\n\n return output;\n }\n\n private buildSpanPayload(span: AnyExportedSpan, isCreate = true): Record<string, any> {\n const payload: Record<string, any> = {};\n\n if (span.input !== undefined) {\n payload.input = this.transformInput(span.input, span.type);\n }\n\n if (span.output !== undefined) {\n payload.output = this.transformOutput(span.output, span.type);\n }\n\n if (isCreate && span.isRootSpan && span.tags?.length) {\n payload.tags = span.tags;\n }\n\n // Initialize metrics and metadata objects\n payload.metrics = {};\n // Spread span.metadata first, then set spanType to prevent accidental override\n payload.metadata = {\n ...span.metadata,\n spanType: span.type,\n };\n\n if (isCreate) {\n payload.metadata['mastra-trace-id'] = span.traceId;\n }\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = attributes as ModelGenerationAttributes;\n\n // Model goes to metadata\n if (modelAttr.model !== undefined) {\n payload.metadata.model = modelAttr.model;\n }\n\n // Provider goes to metadata (if provided by attributes)\n if (modelAttr.provider !== undefined) {\n payload.metadata.provider = modelAttr.provider;\n }\n\n // Usage/token info goes to metrics\n payload.metrics = formatUsageMetrics(modelAttr.usage);\n\n // Time to first token (TTFT) for streaming responses\n // Braintrust expects TTFT in seconds (not milliseconds)\n if (modelAttr.completionStartTime) {\n payload.metrics.time_to_first_token =\n (modelAttr.completionStartTime.getTime() - span.startTime.getTime()) / 1000;\n }\n\n // Model parameters go to metadata\n if (modelAttr.parameters !== undefined) {\n payload.metadata.modelParameters = modelAttr.parameters;\n }\n\n // Other LLM attributes go to metadata\n const otherAttributes = omitKeys(attributes, ['model', 'usage', 'parameters', 'completionStartTime']);\n payload.metadata = {\n ...payload.metadata,\n ...otherAttributes,\n };\n } else {\n // For non-LLM spans, put all attributes in metadata\n payload.metadata = {\n ...payload.metadata,\n ...attributes,\n };\n }\n\n // Handle errors\n if (span.errorInfo) {\n payload.error = span.errorInfo.message;\n payload.metadata.errorDetails = span.errorInfo;\n }\n\n // Clean up empty metrics object\n if (Object.keys(payload.metrics).length === 0) {\n delete payload.metrics;\n }\n\n // Remove null/undefined values from metadata to keep Braintrust UI clean\n payload.metadata = removeNullish(payload.metadata);\n\n return payload;\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/formatter.ts","../src/metrics.ts","../src/thread-reconstruction.ts","../src/tracing.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Message format conversion utilities for Braintrust.\n *\n * Converts AI SDK message format (v4/v5) to OpenAI Chat Completion format,\n * which Braintrust requires for proper rendering of threads.\n */\n\n// ==============================================================================\n// Utility functions\n// ==============================================================================\n\n/**\n * Remove null and undefined values from an object (shallow)\n */\nexport function removeNullish<T extends Record<string, unknown>>(obj: T): Partial<T> {\n return Object.fromEntries(Object.entries(obj).filter(([_, v]) => v != null)) as Partial<T>;\n}\n\n// ==============================================================================\n// Type definitions for AI SDK message format conversion to OpenAI format\n// ==============================================================================\n\n/**\n * AI SDK content part types (both v4 and v5)\n */\ninterface AISDKTextPart {\n type: 'text';\n text: string;\n}\n\ninterface AISDKImagePart {\n type: 'image';\n image?: string | Uint8Array | URL;\n mimeType?: string;\n}\n\ninterface AISDKFilePart {\n type: 'file';\n data?: string | Uint8Array | URL;\n filename?: string;\n name?: string;\n mimeType?: string;\n}\n\ninterface AISDKReasoningPart {\n type: 'reasoning';\n text?: string;\n}\n\ninterface AISDKToolCallPart {\n type: 'tool-call';\n toolCallId: string;\n toolName: string;\n args?: unknown; // AI SDK v4\n input?: unknown; // AI SDK v5\n}\n\ninterface AISDKToolResultPart {\n type: 'tool-result';\n toolCallId: string;\n result?: unknown; // AI SDK v4\n output?: unknown; // AI SDK v5\n}\n\ntype AISDKContentPart =\n | AISDKTextPart\n | AISDKImagePart\n | AISDKFilePart\n | AISDKReasoningPart\n | AISDKToolCallPart\n | AISDKToolResultPart\n | { type: string; [key: string]: unknown }; // Catch-all for unknown types\n\n/**\n * AI SDK message format (input format for conversion)\n */\ninterface AISDKMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string | AISDKContentPart[];\n [key: string]: unknown; // Allow additional properties\n}\n\n/**\n * OpenAI Chat Completion tool call format\n */\nexport interface OpenAIToolCall {\n id: string;\n type: 'function';\n function: {\n name: string;\n arguments: string;\n };\n}\n\n/**\n * OpenAI Chat Completion message format (output format)\n */\nexport interface OpenAIMessage {\n role: 'user' | 'assistant' | 'system' | 'tool';\n content: string;\n tool_calls?: OpenAIToolCall[];\n tool_call_id?: string;\n [key: string]: unknown; // Allow additional properties\n}\n\n// ==============================================================================\n// Message conversion functions\n// ==============================================================================\n\n/**\n * Converts a content part to a string representation.\n * Handles text, image, file, reasoning, and other content types.\n */\nfunction convertContentPart(part: AISDKContentPart | null | undefined): string | null {\n if (!part || typeof part !== 'object') {\n return null;\n }\n\n switch (part.type) {\n case 'text':\n return (part as AISDKTextPart).text || null;\n\n case 'image':\n // Represent image content with a placeholder\n return '[image]';\n\n case 'file': {\n // Represent file content with filename if available\n const filePart = part as AISDKFilePart;\n if (filePart.filename || filePart.name) {\n return `[file: ${filePart.filename || filePart.name}]`;\n }\n return '[file]';\n }\n\n case 'reasoning': {\n // Represent reasoning/thinking content\n const reasoningPart = part as AISDKReasoningPart;\n if (typeof reasoningPart.text === 'string' && reasoningPart.text.length > 0) {\n return `[reasoning: ${reasoningPart.text.substring(0, 100)}${reasoningPart.text.length > 100 ? '...' : ''}]`;\n }\n return '[reasoning]';\n }\n\n case 'tool-call':\n // Tool calls are handled separately in assistant messages\n return null;\n\n case 'tool-result':\n // Tool results are handled separately in tool messages\n return null;\n\n default: {\n // For unknown types, try to extract any text-like content\n const unknownPart = part as { type?: string; text?: string; content?: string };\n if (typeof unknownPart.text === 'string') {\n return unknownPart.text;\n }\n if (typeof unknownPart.content === 'string') {\n return unknownPart.content;\n }\n // Represent unknown content type\n return `[${unknownPart.type || 'unknown'}]`;\n }\n }\n}\n\n/**\n * Serializes tool result data to a string for OpenAI format.\n */\nfunction serializeToolResult(resultData: unknown): string {\n if (typeof resultData === 'string') {\n return resultData;\n }\n if (resultData && typeof resultData === 'object' && 'value' in resultData) {\n const valueData = (resultData as { value: unknown }).value;\n return typeof valueData === 'string' ? valueData : JSON.stringify(valueData);\n }\n if (resultData === undefined || resultData === null) {\n return '';\n }\n try {\n return JSON.stringify(resultData);\n } catch {\n return '[unserializable result]';\n }\n}\n\n/**\n * Converts AI SDK message format to OpenAI Chat Completion format for Braintrust.\n *\n * Supports both AI SDK v4 and v5 formats:\n * - v4 uses 'args' for tool calls and 'result' for tool results\n * - v5 uses 'input' for tool calls and 'output' for tool results\n *\n * AI SDK format:\n * { role: \"user\", content: [{ type: \"text\", text: \"hello\" }] }\n * { role: \"assistant\", content: [{ type: \"text\", text: \"...\" }, { type: \"tool-call\", toolCallId: \"...\", toolName: \"...\", args: {...} }] }\n * { role: \"tool\", content: [{ type: \"tool-result\", toolCallId: \"...\", result: {...} }] }\n *\n * OpenAI format (what Braintrust expects):\n * { role: \"user\", content: \"hello\" }\n * { role: \"assistant\", content: \"...\", tool_calls: [{ id: \"...\", type: \"function\", function: { name: \"...\", arguments: \"...\" } }] }\n * { role: \"tool\", content: \"result\", tool_call_id: \"...\" }\n */\nexport function convertAISDKMessage(message: AISDKMessage | OpenAIMessage | unknown): OpenAIMessage | unknown {\n if (!message || typeof message !== 'object') {\n return message;\n }\n\n const { role, content, ...rest } = message as AISDKMessage;\n\n // If content is already a string, return as-is (already in OpenAI format)\n if (typeof content === 'string') {\n return message;\n }\n\n // If content is an array (AI SDK format), convert based on role\n if (Array.isArray(content)) {\n // Handle empty content arrays\n if (content.length === 0) {\n return { role, content: '', ...rest };\n }\n\n // For user/system messages, extract text and represent non-text content\n if (role === 'user' || role === 'system') {\n const contentParts = content.map((part: AISDKContentPart) => convertContentPart(part)).filter(Boolean);\n\n return {\n role,\n content: contentParts.length > 0 ? contentParts.join('\\n') : '',\n ...rest,\n };\n }\n\n // For assistant messages, extract text, non-text content, AND tool calls\n if (role === 'assistant') {\n const contentParts = content\n .filter((part: AISDKContentPart) => part?.type !== 'tool-call')\n .map((part: AISDKContentPart) => convertContentPart(part))\n .filter(Boolean);\n\n const toolCallParts = content.filter((part: AISDKContentPart) => part?.type === 'tool-call');\n\n const result: OpenAIMessage = {\n role,\n content: contentParts.length > 0 ? (contentParts as string[]).join('\\n') : '',\n ...rest,\n };\n\n // Add tool_calls array if there are tool calls\n if (toolCallParts.length > 0) {\n result.tool_calls = toolCallParts.map((tc: AISDKContentPart) => {\n const toolCall = tc as AISDKToolCallPart;\n const toolCallId = toolCall.toolCallId;\n const toolName = toolCall.toolName;\n // Support both v4 'args' and v5 'input'\n const args = toolCall.args ?? toolCall.input;\n\n let argsString: string;\n if (typeof args === 'string') {\n argsString = args;\n } else if (args !== undefined && args !== null) {\n argsString = JSON.stringify(args);\n } else {\n argsString = '{}';\n }\n\n return {\n id: toolCallId,\n type: 'function' as const,\n function: {\n name: toolName,\n arguments: argsString,\n },\n };\n });\n }\n\n return result;\n }\n\n // For tool messages, convert to OpenAI tool message format\n if (role === 'tool') {\n const toolResult = content.find((part): part is AISDKToolResultPart => part?.type === 'tool-result');\n if (toolResult) {\n // Support both v4 'result' and v5 'output' fields\n const resultData = toolResult.output ?? toolResult.result;\n const resultContent = serializeToolResult(resultData);\n\n return {\n role: 'tool',\n content: resultContent,\n tool_call_id: toolResult.toolCallId,\n } as OpenAIMessage;\n }\n }\n }\n\n return message;\n}\n","import type { UsageStats } from '@mastra/core/observability';\n\n/**\n * BraintrustUsageMetrics\n *\n * Canonical metric keys expected by Braintrust for LLM usage accounting.\n */\nexport interface BraintrustUsageMetrics {\n prompt_tokens?: number;\n completion_tokens?: number;\n tokens?: number;\n completion_reasoning_tokens?: number;\n prompt_cached_tokens?: number;\n prompt_cache_creation_tokens?: number;\n}\n\n/**\n * Formats UsageStats to Braintrust's expected metric format.\n */\nexport function formatUsageMetrics(usage?: UsageStats): BraintrustUsageMetrics {\n const metrics: BraintrustUsageMetrics = {};\n\n if (usage?.inputTokens !== undefined) {\n metrics.prompt_tokens = usage.inputTokens;\n }\n\n if (usage?.outputTokens !== undefined) {\n metrics.completion_tokens = usage.outputTokens;\n }\n\n // Compute total if we have both\n if (metrics.prompt_tokens !== undefined && metrics.completion_tokens !== undefined) {\n metrics.tokens = metrics.prompt_tokens + metrics.completion_tokens;\n }\n\n if (usage?.outputDetails?.reasoning !== undefined) {\n metrics.completion_reasoning_tokens = usage.outputDetails.reasoning;\n }\n\n if (usage?.inputDetails?.cacheRead !== undefined) {\n metrics.prompt_cached_tokens = usage.inputDetails.cacheRead;\n }\n\n if (usage?.inputDetails?.cacheWrite !== undefined) {\n metrics.prompt_cache_creation_tokens = usage.inputDetails.cacheWrite;\n }\n\n return metrics;\n}\n","/**\n * Thread view reconstruction for Braintrust.\n *\n * Reconstructs LLM output in OpenAI Chat Completion format by examining\n * child MODEL_STEP and TOOL_CALL spans. This enables Braintrust's Thread\n * view to properly display the full conversation flow including tool calls.\n *\n * See THREAD_VIEW_RECONSTRUCTION.md for details.\n */\n\nimport { removeNullish } from './formatter';\nimport type { OpenAIMessage } from './formatter';\n\n// ==============================================================================\n// Thread view reconstruction types\n// ==============================================================================\n\n/**\n * Tool call data accumulated from MODEL_STEP and TOOL_CALL spans\n */\nexport interface ThreadToolCall {\n toolCallId: string;\n toolName: string;\n args: unknown;\n result?: unknown; // filled in when TOOL_CALL ends\n startTime?: Date; // from TOOL_CALL span, for ordering multiple tool calls within a step\n}\n\n/**\n * Step data accumulated from MODEL_STEP spans for Thread view reconstruction\n */\nexport interface ThreadStepData {\n stepSpanId: string;\n stepIndex: number; // for ordering steps correctly\n text?: string;\n toolCalls?: ThreadToolCall[];\n}\n\n/**\n * Accumulated data for reconstructing Braintrust Thread view.\n * Populated for MODEL_GENERATION spans as child MODEL_STEP and TOOL_CALL spans complete.\n */\nexport type ThreadData = ThreadStepData[];\n\n/**\n * Tool result data stored when TOOL_CALL spans end (before MODEL_STEP ends)\n */\nexport interface PendingToolResult {\n result: unknown;\n startTime: Date;\n}\n\n// ==============================================================================\n// Thread view reconstruction\n// ==============================================================================\n\n/**\n * Reconstruct the Thread view output from accumulated threadData.\n * Converts to OpenAI Chat Completion message format.\n */\nexport function reconstructThreadOutput(threadData: ThreadData, originalOutput: unknown): OpenAIMessage[] {\n const messages: OpenAIMessage[] = [];\n\n // Sort steps by stepIndex\n const sortedSteps = [...threadData].sort((a, b) => a.stepIndex - b.stepIndex);\n\n for (const step of sortedSteps) {\n // Sort tool calls by startTime within each step\n const sortedToolCalls = step.toolCalls\n ? [...step.toolCalls].sort((a, b) => {\n if (!a.startTime || !b.startTime) return 0;\n return a.startTime.getTime() - b.startTime.getTime();\n })\n : [];\n\n if (sortedToolCalls.length > 0) {\n // Add assistant message with tool_calls\n messages.push({\n role: 'assistant',\n content: step.text || '',\n tool_calls: sortedToolCalls.map(tc => {\n // Clean null/undefined values from args before stringifying\n const cleanArgs =\n tc.args && typeof tc.args === 'object' && !Array.isArray(tc.args)\n ? removeNullish(tc.args as Record<string, unknown>)\n : tc.args;\n return {\n id: tc.toolCallId,\n type: 'function' as const,\n function: {\n name: tc.toolName,\n arguments: typeof cleanArgs === 'string' ? cleanArgs : JSON.stringify(cleanArgs),\n },\n };\n }),\n });\n\n // Add tool messages for each result\n for (const tc of sortedToolCalls) {\n if (tc.result !== undefined) {\n messages.push({\n role: 'tool',\n content: typeof tc.result === 'string' ? tc.result : JSON.stringify(tc.result),\n tool_call_id: tc.toolCallId,\n });\n }\n }\n } else if (step.text) {\n // Step with text only (final response)\n messages.push({\n role: 'assistant',\n content: step.text,\n });\n }\n }\n\n // If we have messages and the last one is a tool message,\n // add the final assistant text from original output\n if (messages.length > 0) {\n const lastMessage = messages[messages.length - 1]!;\n const originalText = (originalOutput as { text?: string })?.text;\n\n // If the last message is a tool response and we have final text, add it\n if (originalText && lastMessage.role === 'tool') {\n messages.push({\n role: 'assistant',\n content: originalText,\n });\n }\n }\n\n return messages;\n}\n","/**\n * Braintrust Exporter for Mastra Observability\n *\n * This exporter sends observability data to Braintrust.\n * Root spans become top-level Braintrust spans (no trace wrapper).\n * Events are handled as zero-duration spans with matching start/end times.\n */\n\nimport type { AnyExportedSpan, ModelGenerationAttributes, SpanErrorInfo } from '@mastra/core/observability';\nimport { SpanType } from '@mastra/core/observability';\nimport { omitKeys } from '@mastra/core/utils';\nimport { TrackingExporter } from '@mastra/observability';\nimport type { TraceData, TrackingExporterConfig } from '@mastra/observability';\nimport { initLogger, currentSpan } from 'braintrust';\nimport type { Span, Logger } from 'braintrust';\nimport { removeNullish, convertAISDKMessage } from './formatter';\nimport { formatUsageMetrics } from './metrics';\nimport { reconstructThreadOutput } from './thread-reconstruction';\nimport type { ThreadData, ThreadStepData, PendingToolResult } from './thread-reconstruction';\n\n/**\n * Extended Braintrust span data that includes span type and thread reconstruction data\n */\ninterface BraintrustSpanData {\n span: Span;\n spanType: SpanType;\n threadData?: ThreadData; // only populated for MODEL_GENERATION spans\n // Tool results stored when TOOL_CALL ends (may arrive before MODEL_STEP ends)\n pendingToolResults?: Map<string, PendingToolResult>; // keyed by toolCallId\n}\n\nexport interface BraintrustExporterConfig extends TrackingExporterConfig {\n /**\n * Optional Braintrust logger instance.\n * When provided, enables integration with Braintrust contexts such as:\n * - Evals: Agent traces nest inside eval task spans\n * - logger.traced(): Agent traces nest inside traced spans\n * - Parent spans: Auto-detects and attaches to external Braintrust spans\n */\n braintrustLogger?: Logger<true>;\n\n /** Braintrust API key. Required if logger is not provided. */\n apiKey?: string;\n /** Optional custom endpoint */\n endpoint?: string;\n /** Braintrust project name (default: 'mastra-tracing') */\n projectName?: string;\n /** Support tuning parameters */\n tuningParameters?: Record<string, any>;\n}\n\ntype BraintrustRoot = Logger<true> | Span;\ntype BraintrustSpan = BraintrustSpanData;\ntype BraintrustEvent = Span;\ntype BraintrustMetadata = unknown;\ntype BraintrustTraceData = TraceData<BraintrustRoot, BraintrustSpan, BraintrustEvent, BraintrustMetadata>;\n\n// Default span type for all spans\nconst DEFAULT_SPAN_TYPE = 'task';\n\n// Exceptions to the default mapping\nconst SPAN_TYPE_EXCEPTIONS: Partial<Record<SpanType, string>> = {\n [SpanType.MODEL_GENERATION]: 'llm',\n [SpanType.TOOL_CALL]: 'tool',\n [SpanType.MCP_TOOL_CALL]: 'tool',\n [SpanType.WORKFLOW_CONDITIONAL_EVAL]: 'function',\n [SpanType.WORKFLOW_WAIT_EVENT]: 'function',\n};\n\n// Mapping function - returns valid Braintrust span types\nfunction mapSpanType(spanType: SpanType): 'llm' | 'score' | 'function' | 'eval' | 'task' | 'tool' {\n return (SPAN_TYPE_EXCEPTIONS[spanType] as any) ?? DEFAULT_SPAN_TYPE;\n}\n\nexport class BraintrustExporter extends TrackingExporter<\n BraintrustRoot,\n BraintrustSpan,\n BraintrustEvent,\n BraintrustMetadata,\n BraintrustExporterConfig\n> {\n name = 'braintrust';\n\n // Flags and logger for context-aware mode\n #useProvidedLogger: boolean;\n #providedLogger?: Logger<true>;\n #localLogger?: Logger<true>;\n\n constructor(config: BraintrustExporterConfig = {}) {\n // Resolve env vars BEFORE calling super (config is readonly in base class)\n const resolvedApiKey = config.apiKey ?? process.env.BRAINTRUST_API_KEY;\n const resolvedEndpoint = config.endpoint ?? process.env.BRAINTRUST_ENDPOINT;\n\n super({\n ...config,\n apiKey: resolvedApiKey,\n endpoint: resolvedEndpoint,\n });\n\n this.#useProvidedLogger = !!config.braintrustLogger;\n\n if (this.#useProvidedLogger) {\n // Use provided logger - enables Braintrust context integration\n this.#providedLogger = config.braintrustLogger;\n } else {\n // Validate apiKey for creating loggers per trace\n if (!this.config.apiKey) {\n this.setDisabled(\n `Missing required API key. Set BRAINTRUST_API_KEY environment variable or pass apiKey in config.`,\n );\n return;\n }\n // lazy create logger on first rootSpan\n this.#localLogger = undefined;\n }\n }\n\n private async getLocalLogger(): Promise<Logger<true> | undefined> {\n if (this.#localLogger) {\n return this.#localLogger;\n }\n try {\n const logger = await initLogger({\n projectName: this.config.projectName ?? 'mastra-tracing',\n apiKey: this.config.apiKey,\n appUrl: this.config.endpoint,\n ...this.config.tuningParameters,\n });\n this.#localLogger = logger;\n return logger;\n } catch (err) {\n this.logger.error('Braintrust exporter: Failed to initialize logger', { error: err });\n this.setDisabled('Failed to initialize Braintrust logger');\n }\n }\n\n private startSpan(args: { parent: Span | Logger<true>; span: AnyExportedSpan }): BraintrustSpanData {\n const { parent, span } = args;\n const payload = this.buildSpanPayload(span);\n\n // Braintrust's startSpan() accepts data properties via the `event` parameter\n // which maps to StartSpanEventArgs (ExperimentLogPartialArgs & Partial<IdField>)\n // This includes: input, output, metadata, metrics, tags, scores, error, etc.\n const braintrustSpan = parent.startSpan({\n spanId: span.id,\n name: span.name,\n type: mapSpanType(span.type),\n startTime: span.startTime.getTime() / 1000,\n event: {\n id: span.id, // Use Mastra span ID as Braintrust row ID for logFeedback() compatibility\n ...payload,\n },\n });\n\n // Create BraintrustSpanData with span type for tree walking\n // Initialize threadData and pendingToolResults for MODEL_GENERATION spans (used for Thread view reconstruction)\n const isModelGeneration = span.type === SpanType.MODEL_GENERATION;\n return {\n span: braintrustSpan,\n spanType: span.type,\n threadData: isModelGeneration ? [] : undefined,\n pendingToolResults: isModelGeneration ? new Map() : undefined,\n };\n }\n\n protected override async _buildRoot(_args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<BraintrustRoot | undefined> {\n if (this.#useProvidedLogger) {\n // Try to find a Braintrust span to attach to:\n // 1. Auto-detect from Braintrust's current span (logger.traced(), Eval(), etc.)\n // 2. Fall back to the configured logger\n const externalSpan = currentSpan();\n\n // Check if it's a valid span (not the NOOP_SPAN)\n if (externalSpan && externalSpan.id) {\n // External span detected - attach Mastra traces to it\n return externalSpan;\n } else {\n // No external span - use provided logger\n return this.#providedLogger!;\n }\n } else {\n // Use the local logger\n return this.getLocalLogger();\n }\n }\n\n protected override async _buildSpan(args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<BraintrustSpanData | undefined> {\n const { span, traceData } = args;\n\n if (span.isRootSpan) {\n const root = traceData.getRoot();\n if (root) {\n return this.startSpan({ parent: root, span });\n }\n } else {\n const parent = traceData.getParent(args);\n if (parent) {\n // Parent could be BraintrustSpanData (has .span) or BraintrustRoot (Logger/Span, no .span)\n const parentSpan = 'span' in parent ? parent.span : parent;\n return this.startSpan({ parent: parentSpan, span });\n }\n }\n }\n\n protected override async _buildEvent(args: {\n span: AnyExportedSpan;\n traceData: BraintrustTraceData;\n }): Promise<Span | undefined> {\n const spanData = await this._buildSpan(args);\n\n if (!spanData) {\n // parent doesn't exist and not creating rootSpan, return early data\n return;\n }\n\n spanData.span.end({ endTime: args.span.startTime.getTime() / 1000 });\n return spanData.span;\n }\n\n protected override async _updateSpan(args: { span: AnyExportedSpan; traceData: BraintrustTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n const spanData = traceData.getSpan({ spanId: span.id });\n if (!spanData) {\n return;\n }\n spanData.span.log(this.buildSpanPayload(span, false));\n }\n\n protected override async _finishSpan(args: { span: AnyExportedSpan; traceData: BraintrustTraceData }): Promise<void> {\n const { span, traceData } = args;\n\n const spanData = traceData.getSpan({ spanId: span.id });\n if (!spanData) {\n return;\n }\n\n // Handle thread data accumulation for MODEL_STEP and TOOL_CALL spans\n if (span.type === SpanType.MODEL_STEP) {\n this.accumulateModelStepData(span, traceData);\n } else if (span.type === SpanType.TOOL_CALL) {\n this.accumulateToolCallResult(span, traceData);\n }\n\n // Build payload - for MODEL_GENERATION, may reconstruct output from threadData\n const payload =\n span.type === SpanType.MODEL_GENERATION\n ? this.buildModelGenerationPayload(span, spanData)\n : this.buildSpanPayload(span, false);\n\n spanData.span.log(payload);\n\n if (span.endTime) {\n spanData.span.end({ endTime: span.endTime.getTime() / 1000 });\n } else {\n spanData.span.end();\n }\n }\n\n protected override async _abortSpan(args: { span: BraintrustSpan; reason: SpanErrorInfo }): Promise<void> {\n const { span: spanData, reason } = args;\n spanData.span.log({\n error: reason.message,\n metadata: { errorDetails: reason },\n });\n spanData.span.end();\n }\n\n // ==============================================================================\n // Thread view reconstruction helpers\n // ==============================================================================\n\n /**\n * Walk up the tree to find the MODEL_GENERATION ancestor span.\n * Returns the BraintrustSpanData if found, undefined otherwise.\n */\n private findModelGenerationAncestor(spanId: string, traceData: BraintrustTraceData): BraintrustSpanData | undefined {\n let currentId: string | undefined = spanId;\n\n while (currentId) {\n const parentId = traceData.getParentId({ spanId: currentId });\n if (!parentId) return undefined;\n\n const parentSpanData = traceData.getSpan({ spanId: parentId });\n if (parentSpanData?.spanType === SpanType.MODEL_GENERATION) {\n return parentSpanData;\n }\n currentId = parentId;\n }\n\n return undefined;\n }\n\n /**\n * Accumulate MODEL_STEP data to the parent MODEL_GENERATION's threadData.\n * Called when a MODEL_STEP span ends.\n */\n private accumulateModelStepData(span: AnyExportedSpan, traceData: BraintrustTraceData): void {\n const modelGenSpanData = this.findModelGenerationAncestor(span.id, traceData);\n if (!modelGenSpanData?.threadData) {\n return;\n }\n\n // Extract step data from MODEL_STEP output and attributes\n const output = span.output as\n | { text?: string; toolCalls?: Array<{ toolCallId: string; toolName: string; args: unknown }> }\n | undefined;\n const attributes = span.attributes as { stepIndex?: number } | undefined;\n\n const stepData: ThreadStepData = {\n stepSpanId: span.id,\n stepIndex: attributes?.stepIndex ?? 0,\n text: output?.text,\n toolCalls: output?.toolCalls?.map(tc => ({\n toolCallId: tc.toolCallId,\n toolName: tc.toolName,\n args: tc.args,\n })),\n };\n\n modelGenSpanData.threadData.push(stepData);\n }\n\n /**\n * Store TOOL_CALL result in parent MODEL_GENERATION's pendingToolResults.\n * Called when a TOOL_CALL span ends.\n * Results are merged into threadData when MODEL_GENERATION ends.\n */\n private accumulateToolCallResult(span: AnyExportedSpan, traceData: BraintrustTraceData): void {\n const modelGenSpanData = this.findModelGenerationAncestor(span.id, traceData);\n if (!modelGenSpanData?.pendingToolResults) {\n return;\n }\n\n // Extract tool call ID from TOOL_CALL span input\n const input = span.input as { toolCallId?: string } | undefined;\n const toolCallId = input?.toolCallId;\n if (!toolCallId) {\n return;\n }\n\n // Store the result for later merging\n modelGenSpanData.pendingToolResults.set(toolCallId, {\n result: span.output,\n startTime: span.startTime,\n });\n }\n\n /**\n * Build the payload for MODEL_GENERATION span, reconstructing output from threadData if available.\n */\n private buildModelGenerationPayload(span: AnyExportedSpan, spanData: BraintrustSpanData): Record<string, any> {\n const basePayload = this.buildSpanPayload(span, false);\n\n // Check if we have threadData with tool calls to reconstruct\n const threadData = spanData.threadData;\n if (!threadData || threadData.length === 0) {\n return basePayload;\n }\n\n // Merge pending tool results into threadData\n if (spanData.pendingToolResults && spanData.pendingToolResults.size > 0) {\n for (const step of threadData) {\n if (step.toolCalls) {\n for (const toolCall of step.toolCalls) {\n const pendingResult = spanData.pendingToolResults.get(toolCall.toolCallId);\n if (pendingResult) {\n toolCall.result = pendingResult.result;\n toolCall.startTime = pendingResult.startTime;\n }\n }\n }\n }\n }\n\n // Check if any step has tool calls\n const hasToolCalls = threadData.some(step => step.toolCalls && step.toolCalls.length > 0);\n if (!hasToolCalls) {\n return basePayload;\n }\n\n // Reconstruct output as OpenAI messages\n const reconstructedOutput = reconstructThreadOutput(threadData, span.output);\n return {\n ...basePayload,\n output: reconstructedOutput,\n };\n }\n\n /**\n * Transforms MODEL_GENERATION input to Braintrust Thread view format.\n * Converts AI SDK messages (v4/v5) to OpenAI Chat Completion format, which Braintrust requires\n * for proper rendering of threads (fixes #11023).\n */\n private transformInput(input: unknown, spanType: SpanType): unknown {\n if (spanType === SpanType.MODEL_GENERATION) {\n // If input is already an array of messages, convert AI SDK format to OpenAI format\n if (Array.isArray(input)) {\n return input.map((msg: unknown) => convertAISDKMessage(msg));\n }\n\n // If input has a messages array\n if (\n input &&\n typeof input === 'object' &&\n 'messages' in input &&\n Array.isArray((input as { messages: unknown[] }).messages)\n ) {\n return (input as { messages: unknown[] }).messages.map((msg: unknown) => convertAISDKMessage(msg));\n }\n }\n\n return input;\n }\n\n /**\n * Transforms MODEL_GENERATION output to Braintrust Thread view format.\n */\n private transformOutput(output: any, spanType: SpanType): any {\n if (spanType === SpanType.MODEL_GENERATION) {\n if (!output || typeof output !== 'object') {\n return output;\n }\n const { text, ...rest } = output;\n // Remove null/undefined values from rest to keep Thread view clean\n return { role: 'assistant', content: text, ...removeNullish(rest) };\n }\n\n return output;\n }\n\n private buildSpanPayload(span: AnyExportedSpan, isCreate = true): Record<string, any> {\n const payload: Record<string, any> = {};\n\n if (span.input !== undefined) {\n payload.input = this.transformInput(span.input, span.type);\n }\n\n if (span.output !== undefined) {\n payload.output = this.transformOutput(span.output, span.type);\n }\n\n if (isCreate && span.isRootSpan && span.tags?.length) {\n payload.tags = span.tags;\n }\n\n // Initialize metrics and metadata objects\n payload.metrics = {};\n // Spread span.metadata first, then set spanType to prevent accidental override\n payload.metadata = {\n ...span.metadata,\n spanType: span.type,\n };\n\n if (isCreate) {\n payload.metadata['mastra-trace-id'] = span.traceId;\n }\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n if (span.type === SpanType.MODEL_GENERATION) {\n const modelAttr = attributes as ModelGenerationAttributes;\n\n // Model goes to metadata\n if (modelAttr.model !== undefined) {\n payload.metadata.model = modelAttr.model;\n }\n\n // Provider goes to metadata (if provided by attributes)\n if (modelAttr.provider !== undefined) {\n payload.metadata.provider = modelAttr.provider;\n }\n\n // Usage/token info goes to metrics\n payload.metrics = formatUsageMetrics(modelAttr.usage);\n\n // Time to first token (TTFT) for streaming responses\n // Braintrust expects TTFT in seconds (not milliseconds)\n if (modelAttr.completionStartTime) {\n payload.metrics.time_to_first_token =\n (modelAttr.completionStartTime.getTime() - span.startTime.getTime()) / 1000;\n }\n\n // Model parameters go to metadata\n if (modelAttr.parameters !== undefined) {\n payload.metadata.modelParameters = modelAttr.parameters;\n }\n\n // Other LLM attributes go to metadata\n const otherAttributes = omitKeys(attributes, ['model', 'usage', 'parameters', 'completionStartTime']);\n payload.metadata = {\n ...payload.metadata,\n ...otherAttributes,\n };\n } else {\n // For non-LLM spans, put all attributes in metadata\n payload.metadata = {\n ...payload.metadata,\n ...attributes,\n };\n }\n\n // Handle errors\n if (span.errorInfo) {\n payload.error = span.errorInfo.message;\n payload.metadata.errorDetails = span.errorInfo;\n }\n\n // Clean up empty metrics object\n if (Object.keys(payload.metrics).length === 0) {\n delete payload.metrics;\n }\n\n // Remove null/undefined values from metadata to keep Braintrust UI clean\n payload.metadata = removeNullish(payload.metadata);\n\n return payload;\n }\n}\n"]}
|
package/dist/tracing.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,eAAe,EAA6B,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC5G,OAAO,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAC;AAEtD,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,KAAK,EAAE,SAAS,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAE/E,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAI/C,OAAO,KAAK,EAAE,UAAU,EAAkB,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE7F;;GAEG;AACH,UAAU,kBAAkB;IAC1B,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,UAAU,CAAC,EAAE,UAAU,CAAC;IAExB,kBAAkB,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC;CACrD;AAED,MAAM,WAAW,wBAAyB,SAAQ,sBAAsB;IACtE;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC;IAEhC,8DAA8D;IAC9D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,0DAA0D;IAC1D,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACxC;AAED,KAAK,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;AAC1C,KAAK,cAAc,GAAG,kBAAkB,CAAC;AACzC,KAAK,eAAe,GAAG,IAAI,CAAC;AAC5B,KAAK,kBAAkB,GAAG,OAAO,CAAC;AAClC,KAAK,mBAAmB,GAAG,SAAS,CAAC,cAAc,EAAE,cAAc,EAAE,eAAe,EAAE,kBAAkB,CAAC,CAAC;AAmB1G,qBAAa,kBAAmB,SAAQ,gBAAgB,CACtD,cAAc,EACd,cAAc,EACd,eAAe,EACf,kBAAkB,EAClB,wBAAwB,CACzB;;IACC,IAAI,SAAgB;gBAOR,MAAM,GAAE,wBAA6B;YA6BnC,cAAc;IAmB5B,OAAO,CAAC,SAAS;
|
|
1
|
+
{"version":3,"file":"tracing.d.ts","sourceRoot":"","sources":["../src/tracing.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mastra/braintrust",
|
|
3
|
-
"version": "1.0.0-beta.
|
|
3
|
+
"version": "1.0.0-beta.16",
|
|
4
4
|
"description": "Braintrust observability provider for Mastra - includes tracing and future observability features",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"braintrust": "^1.1.0",
|
|
28
|
-
"@mastra/observability": "1.0.0-beta.
|
|
28
|
+
"@mastra/observability": "1.0.0-beta.13"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@types/node": "22.13.17",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"typescript": "^5.9.3",
|
|
37
37
|
"vitest": "4.0.16",
|
|
38
38
|
"@internal/lint": "0.0.53",
|
|
39
|
-
"@
|
|
40
|
-
"@
|
|
41
|
-
"@
|
|
39
|
+
"@mastra/core": "1.0.0-beta.25",
|
|
40
|
+
"@observability/test-utils": "0.0.1",
|
|
41
|
+
"@internal/types-builder": "0.0.28"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"@mastra/core": ">=1.0.0-0 <2.0.0-0"
|