@mastra/braintrust 0.0.0-issue-7498-20250905233741
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 +62 -0
- package/LICENSE.md +15 -0
- package/README.md +47 -0
- package/dist/ai-tracing.d.ts +35 -0
- package/dist/ai-tracing.d.ts.map +1 -0
- package/dist/index.cjs +244 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +242 -0
- package/dist/index.js.map +1 -0
- package/package.json +59 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @mastra/braintrust
|
|
2
|
+
|
|
3
|
+
## 0.0.0-issue-7498-20250905233741
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Updated dependencies [0662d02]
|
|
8
|
+
- Updated dependencies [6189844]
|
|
9
|
+
- Updated dependencies [d7a8f59]
|
|
10
|
+
- Updated dependencies [4dda259]
|
|
11
|
+
- Updated dependencies [94edeff]
|
|
12
|
+
- Updated dependencies [defed1c]
|
|
13
|
+
- Updated dependencies [6991ced]
|
|
14
|
+
- Updated dependencies [9cb9c42]
|
|
15
|
+
- Updated dependencies [8334859]
|
|
16
|
+
- @mastra/core@0.0.0-issue-7498-20250905233741
|
|
17
|
+
|
|
18
|
+
## 0.1.0
|
|
19
|
+
|
|
20
|
+
### Minor Changes
|
|
21
|
+
|
|
22
|
+
- 3fc9261: "Initial release of BraintrustExporter for ai-observability"
|
|
23
|
+
|
|
24
|
+
### Patch Changes
|
|
25
|
+
|
|
26
|
+
- 376913a: Update peerdeps
|
|
27
|
+
- Updated dependencies [8fbf79e]
|
|
28
|
+
- Updated dependencies [fd83526]
|
|
29
|
+
- Updated dependencies [d0b90ab]
|
|
30
|
+
- Updated dependencies [6f5eb7a]
|
|
31
|
+
- Updated dependencies [a01cf14]
|
|
32
|
+
- Updated dependencies [a9e50ee]
|
|
33
|
+
- Updated dependencies [5397eb4]
|
|
34
|
+
- Updated dependencies [c9f4e4a]
|
|
35
|
+
- Updated dependencies [0acbc80]
|
|
36
|
+
- @mastra/core@0.16.0
|
|
37
|
+
|
|
38
|
+
## 0.1.0-alpha.1
|
|
39
|
+
|
|
40
|
+
### Patch Changes
|
|
41
|
+
|
|
42
|
+
- 376913a: Update peerdeps
|
|
43
|
+
- Updated dependencies [8fbf79e]
|
|
44
|
+
- @mastra/core@0.16.0-alpha.1
|
|
45
|
+
|
|
46
|
+
## 0.1.0-alpha.0
|
|
47
|
+
|
|
48
|
+
### Minor Changes
|
|
49
|
+
|
|
50
|
+
- 3fc9261: "Initial release of BraintrustExporter for ai-observability"
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- Updated dependencies [fd83526]
|
|
55
|
+
- Updated dependencies [d0b90ab]
|
|
56
|
+
- Updated dependencies [6f5eb7a]
|
|
57
|
+
- Updated dependencies [a01cf14]
|
|
58
|
+
- Updated dependencies [a9e50ee]
|
|
59
|
+
- Updated dependencies [5397eb4]
|
|
60
|
+
- Updated dependencies [c9f4e4a]
|
|
61
|
+
- Updated dependencies [0acbc80]
|
|
62
|
+
- @mastra/core@0.16.0-alpha.0
|
package/LICENSE.md
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Apache License 2.0
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Kepler Software, Inc.
|
|
4
|
+
|
|
5
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
6
|
+
you may not use this file except in compliance with the License.
|
|
7
|
+
You may obtain a copy of the License at
|
|
8
|
+
|
|
9
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
10
|
+
|
|
11
|
+
Unless required by applicable law or agreed to in writing, software
|
|
12
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
13
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
14
|
+
See the License for the specific language governing permissions and
|
|
15
|
+
limitations under the License.
|
package/README.md
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# @mastra/braintrust
|
|
2
|
+
|
|
3
|
+
Braintrust AI Observability exporter for Mastra applications.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @mastra/braintrust
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { BraintrustExporter } from '@mastra/braintrust';
|
|
15
|
+
|
|
16
|
+
// Use with Mastra
|
|
17
|
+
const mastra = new Mastra({
|
|
18
|
+
...,
|
|
19
|
+
observability: {
|
|
20
|
+
instances: {
|
|
21
|
+
braintrust: {
|
|
22
|
+
serviceName: 'service',
|
|
23
|
+
exporters: [
|
|
24
|
+
new BraintrustExporter({
|
|
25
|
+
apiKey: process.env.BRAINTRUST_API_KEY,
|
|
26
|
+
endpoint: process.env.BRAINTRUST_ENDPOINT, // optional
|
|
27
|
+
}),
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
},
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
### AI Tracing
|
|
38
|
+
|
|
39
|
+
- **Automatic span mapping**: Root spans become Braintrust traces
|
|
40
|
+
- **Type-specific metadata**: Extracts relevant metadata for each span type (agents, tools, workflows)
|
|
41
|
+
- **Error tracking**: Automatic error status and message tracking
|
|
42
|
+
- **Hierarchical traces**: Maintains parent-child relationships
|
|
43
|
+
- **Event span support**: Zero-duration spans for event-type traces
|
|
44
|
+
|
|
45
|
+
## License
|
|
46
|
+
|
|
47
|
+
Apache 2.0
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Braintrust Exporter for Mastra AI Tracing
|
|
3
|
+
*
|
|
4
|
+
* This exporter sends tracing data to Braintrust for AI observability.
|
|
5
|
+
* Root spans become top-level Braintrust spans (no trace wrapper).
|
|
6
|
+
* Events are handled as zero-duration spans with matching start/end times.
|
|
7
|
+
*/
|
|
8
|
+
import type { AITracingExporter, AITracingEvent } from '@mastra/core/ai-tracing';
|
|
9
|
+
export interface BraintrustExporterConfig {
|
|
10
|
+
/** Braintrust API key */
|
|
11
|
+
apiKey?: string;
|
|
12
|
+
/** Optional custom endpoint */
|
|
13
|
+
endpoint?: string;
|
|
14
|
+
/** Logger level for diagnostic messages (default: 'warn') */
|
|
15
|
+
logLevel?: 'debug' | 'info' | 'warn' | 'error';
|
|
16
|
+
/** Support tuning parameters */
|
|
17
|
+
tuningParameters?: Record<string, any>;
|
|
18
|
+
}
|
|
19
|
+
export declare class BraintrustExporter implements AITracingExporter {
|
|
20
|
+
name: string;
|
|
21
|
+
private traceMap;
|
|
22
|
+
private logger;
|
|
23
|
+
private config;
|
|
24
|
+
constructor(config: BraintrustExporterConfig);
|
|
25
|
+
exportEvent(event: AITracingEvent): Promise<void>;
|
|
26
|
+
private handleSpanStarted;
|
|
27
|
+
private handleSpanUpdateOrEnd;
|
|
28
|
+
private handleEventSpan;
|
|
29
|
+
private initLogger;
|
|
30
|
+
private getSpanData;
|
|
31
|
+
private getBraintrustParent;
|
|
32
|
+
private buildSpanPayload;
|
|
33
|
+
shutdown(): Promise<void>;
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=ai-tracing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ai-tracing.d.ts","sourceRoot":"","sources":["../src/ai-tracing.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAsC,MAAM,yBAAyB,CAAC;AAMrH,MAAM,WAAW,wBAAwB;IACvC,yBAAyB;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6DAA6D;IAC7D,QAAQ,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;IAC/C,gCAAgC;IAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CACxC;AAyBD,qBAAa,kBAAmB,YAAW,iBAAiB;IAC1D,IAAI,SAAgB;IACpB,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,MAAM,CAAgB;IAC9B,OAAO,CAAC,MAAM,CAA2B;gBAE7B,MAAM,EAAE,wBAAwB;IActC,WAAW,CAAC,KAAK,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;YAuBzC,iBAAiB;YA2BjB,qBAAqB;YAsCrB,eAAe;YAoCf,UAAU;IAWxB,OAAO,CAAC,WAAW;IAiBnB,OAAO,CAAC,mBAAmB;IA2B3B,OAAO,CAAC,gBAAgB;IAsElB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAchC"}
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var aiTracing = require('@mastra/core/ai-tracing');
|
|
4
|
+
var logger = require('@mastra/core/logger');
|
|
5
|
+
var braintrust = require('braintrust');
|
|
6
|
+
|
|
7
|
+
// src/ai-tracing.ts
|
|
8
|
+
var DEFAULT_SPAN_TYPE = "task";
|
|
9
|
+
var SPAN_TYPE_EXCEPTIONS = {
|
|
10
|
+
[aiTracing.AISpanType.LLM_GENERATION]: "llm",
|
|
11
|
+
[aiTracing.AISpanType.LLM_CHUNK]: "llm",
|
|
12
|
+
[aiTracing.AISpanType.TOOL_CALL]: "tool",
|
|
13
|
+
[aiTracing.AISpanType.MCP_TOOL_CALL]: "tool",
|
|
14
|
+
[aiTracing.AISpanType.WORKFLOW_CONDITIONAL_EVAL]: "function",
|
|
15
|
+
[aiTracing.AISpanType.WORKFLOW_WAIT_EVENT]: "function"
|
|
16
|
+
};
|
|
17
|
+
function mapSpanType(spanType) {
|
|
18
|
+
return SPAN_TYPE_EXCEPTIONS[spanType] ?? DEFAULT_SPAN_TYPE;
|
|
19
|
+
}
|
|
20
|
+
var BraintrustExporter = class {
|
|
21
|
+
name = "braintrust";
|
|
22
|
+
traceMap = /* @__PURE__ */ new Map();
|
|
23
|
+
logger;
|
|
24
|
+
config;
|
|
25
|
+
constructor(config) {
|
|
26
|
+
this.logger = new logger.ConsoleLogger({ level: config.logLevel ?? "warn" });
|
|
27
|
+
if (!config.apiKey) {
|
|
28
|
+
this.logger.error("BraintrustExporter: Missing required credentials, exporter will be disabled", {
|
|
29
|
+
hasApiKey: !!config.apiKey
|
|
30
|
+
});
|
|
31
|
+
this.config = null;
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
this.config = config;
|
|
35
|
+
}
|
|
36
|
+
async exportEvent(event) {
|
|
37
|
+
if (!this.config) {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (event.span.isEvent) {
|
|
41
|
+
await this.handleEventSpan(event.span);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
switch (event.type) {
|
|
45
|
+
case "span_started":
|
|
46
|
+
await this.handleSpanStarted(event.span);
|
|
47
|
+
break;
|
|
48
|
+
case "span_updated":
|
|
49
|
+
await this.handleSpanUpdateOrEnd(event.span, false);
|
|
50
|
+
break;
|
|
51
|
+
case "span_ended":
|
|
52
|
+
await this.handleSpanUpdateOrEnd(event.span, true);
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
async handleSpanStarted(span) {
|
|
57
|
+
if (span.isRootSpan) {
|
|
58
|
+
await this.initLogger(span);
|
|
59
|
+
}
|
|
60
|
+
const method = "handleSpanStarted";
|
|
61
|
+
const spanData = this.getSpanData({ span, method });
|
|
62
|
+
if (!spanData) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
const braintrustParent = this.getBraintrustParent({ spanData, span, method });
|
|
66
|
+
if (!braintrustParent) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
const payload = this.buildSpanPayload(span);
|
|
70
|
+
const braintrustSpan = braintrustParent.startSpan({
|
|
71
|
+
name: span.name,
|
|
72
|
+
type: mapSpanType(span.type),
|
|
73
|
+
...payload
|
|
74
|
+
});
|
|
75
|
+
spanData.spans.set(span.id, braintrustSpan);
|
|
76
|
+
}
|
|
77
|
+
async handleSpanUpdateOrEnd(span, isEnd) {
|
|
78
|
+
const method = isEnd ? "handleSpanEnd" : "handleSpanUpdate";
|
|
79
|
+
const spanData = this.getSpanData({ span, method });
|
|
80
|
+
if (!spanData) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const braintrustSpan = spanData.spans.get(span.id);
|
|
84
|
+
if (!braintrustSpan) {
|
|
85
|
+
this.logger.warn("Braintrust exporter: No Braintrust span found for span update/end", {
|
|
86
|
+
traceId: span.traceId,
|
|
87
|
+
spanId: span.id,
|
|
88
|
+
spanName: span.name,
|
|
89
|
+
spanType: span.type,
|
|
90
|
+
isRootSpan: span.isRootSpan,
|
|
91
|
+
parentSpanId: span.parent?.id,
|
|
92
|
+
method
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
braintrustSpan.log(this.buildSpanPayload(span));
|
|
97
|
+
if (isEnd) {
|
|
98
|
+
if (span.endTime) {
|
|
99
|
+
braintrustSpan.end({ endTime: span.endTime.getTime() / 1e3 });
|
|
100
|
+
} else {
|
|
101
|
+
braintrustSpan.end();
|
|
102
|
+
}
|
|
103
|
+
if (span.isRootSpan) {
|
|
104
|
+
this.traceMap.delete(span.traceId);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async handleEventSpan(span) {
|
|
109
|
+
if (span.isRootSpan) {
|
|
110
|
+
this.logger.debug("Braintrust exporter: Creating logger for event", {
|
|
111
|
+
traceId: span.traceId,
|
|
112
|
+
spanId: span.id,
|
|
113
|
+
spanName: span.name,
|
|
114
|
+
method: "handleEventSpan"
|
|
115
|
+
});
|
|
116
|
+
await this.initLogger(span);
|
|
117
|
+
}
|
|
118
|
+
const method = "handleEventSpan";
|
|
119
|
+
const spanData = this.getSpanData({ span, method });
|
|
120
|
+
if (!spanData) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
const braintrustParent = this.getBraintrustParent({ spanData, span, method });
|
|
124
|
+
if (!braintrustParent) {
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const payload = this.buildSpanPayload(span);
|
|
128
|
+
const braintrustSpan = braintrustParent.startSpan({
|
|
129
|
+
name: span.name,
|
|
130
|
+
type: mapSpanType(span.type),
|
|
131
|
+
startTime: span.startTime.getTime() / 1e3,
|
|
132
|
+
...payload
|
|
133
|
+
});
|
|
134
|
+
braintrustSpan.end({ endTime: span.startTime.getTime() / 1e3 });
|
|
135
|
+
spanData.spans.set(span.id, braintrustSpan);
|
|
136
|
+
}
|
|
137
|
+
async initLogger(span) {
|
|
138
|
+
const logger = await braintrust.initLogger({
|
|
139
|
+
projectName: "mastra-tracing",
|
|
140
|
+
// TODO: Make this configurable
|
|
141
|
+
apiKey: this.config.apiKey,
|
|
142
|
+
appUrl: this.config.endpoint,
|
|
143
|
+
...this.config.tuningParameters
|
|
144
|
+
});
|
|
145
|
+
this.traceMap.set(span.traceId, { logger, spans: /* @__PURE__ */ new Map() });
|
|
146
|
+
}
|
|
147
|
+
getSpanData(options) {
|
|
148
|
+
const { span, method } = options;
|
|
149
|
+
if (this.traceMap.has(span.traceId)) {
|
|
150
|
+
return this.traceMap.get(span.traceId);
|
|
151
|
+
}
|
|
152
|
+
this.logger.warn("Braintrust exporter: No span data found for span", {
|
|
153
|
+
traceId: span.traceId,
|
|
154
|
+
spanId: span.id,
|
|
155
|
+
spanName: span.name,
|
|
156
|
+
spanType: span.type,
|
|
157
|
+
isRootSpan: span.isRootSpan,
|
|
158
|
+
parentSpanId: span.parent?.id,
|
|
159
|
+
method
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
getBraintrustParent(options) {
|
|
163
|
+
const { spanData, span, method } = options;
|
|
164
|
+
const parentId = span.parent?.id;
|
|
165
|
+
if (!parentId) {
|
|
166
|
+
return spanData.logger;
|
|
167
|
+
}
|
|
168
|
+
if (spanData.spans.has(parentId)) {
|
|
169
|
+
return spanData.spans.get(parentId);
|
|
170
|
+
}
|
|
171
|
+
this.logger.warn("Braintrust exporter: No parent data found for span", {
|
|
172
|
+
traceId: span.traceId,
|
|
173
|
+
spanId: span.id,
|
|
174
|
+
spanName: span.name,
|
|
175
|
+
spanType: span.type,
|
|
176
|
+
isRootSpan: span.isRootSpan,
|
|
177
|
+
parentSpanId: span.parent?.id,
|
|
178
|
+
method
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
buildSpanPayload(span) {
|
|
182
|
+
const payload = {};
|
|
183
|
+
if (span.input !== void 0) {
|
|
184
|
+
payload.input = span.input;
|
|
185
|
+
}
|
|
186
|
+
if (span.output !== void 0) {
|
|
187
|
+
payload.output = span.output;
|
|
188
|
+
}
|
|
189
|
+
payload.metrics = {};
|
|
190
|
+
payload.metadata = {
|
|
191
|
+
spanType: span.type,
|
|
192
|
+
...span.metadata
|
|
193
|
+
};
|
|
194
|
+
const attributes = span.attributes ?? {};
|
|
195
|
+
if (span.type === aiTracing.AISpanType.LLM_GENERATION) {
|
|
196
|
+
const llmAttr = attributes;
|
|
197
|
+
if (llmAttr.model !== void 0) {
|
|
198
|
+
payload.metadata.model = llmAttr.model;
|
|
199
|
+
}
|
|
200
|
+
if (llmAttr.usage !== void 0) {
|
|
201
|
+
payload.metrics = {
|
|
202
|
+
...payload.metrics,
|
|
203
|
+
...llmAttr.usage
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
if (llmAttr.parameters !== void 0) {
|
|
207
|
+
payload.metadata.modelParameters = llmAttr.parameters;
|
|
208
|
+
}
|
|
209
|
+
const otherAttributes = aiTracing.omitKeys(attributes, ["model", "usage", "parameters"]);
|
|
210
|
+
payload.metadata = {
|
|
211
|
+
...payload.metadata,
|
|
212
|
+
...otherAttributes
|
|
213
|
+
};
|
|
214
|
+
} else {
|
|
215
|
+
payload.metadata = {
|
|
216
|
+
...payload.metadata,
|
|
217
|
+
...attributes
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (span.errorInfo) {
|
|
221
|
+
payload.error = span.errorInfo.message;
|
|
222
|
+
payload.metadata.errorDetails = span.errorInfo;
|
|
223
|
+
}
|
|
224
|
+
if (Object.keys(payload.metrics).length === 0) {
|
|
225
|
+
delete payload.metrics;
|
|
226
|
+
}
|
|
227
|
+
return payload;
|
|
228
|
+
}
|
|
229
|
+
async shutdown() {
|
|
230
|
+
if (!this.config) {
|
|
231
|
+
return;
|
|
232
|
+
}
|
|
233
|
+
for (const [_traceId, spanData] of this.traceMap) {
|
|
234
|
+
for (const [_spanId, span] of spanData.spans) {
|
|
235
|
+
span.end();
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
this.traceMap.clear();
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
exports.BraintrustExporter = BraintrustExporter;
|
|
243
|
+
//# sourceMappingURL=index.cjs.map
|
|
244
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai-tracing.ts"],"names":["AISpanType","ConsoleLogger","initLogger","omitKeys"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Braintrust Exporter for Mastra AI Tracing\n *\n * This exporter sends tracing data to Braintrust for AI observability.\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 { AITracingExporter, AITracingEvent, AnyAISpan, LLMGenerationAttributes } from '@mastra/core/ai-tracing';\nimport { AISpanType, omitKeys } from '@mastra/core/ai-tracing';\nimport { ConsoleLogger } from '@mastra/core/logger';\nimport { initLogger } from 'braintrust';\nimport type { Span, Logger } from 'braintrust';\n\nexport interface BraintrustExporterConfig {\n /** Braintrust API key */\n apiKey?: string;\n /** Optional custom endpoint */\n endpoint?: string;\n /** Logger level for diagnostic messages (default: 'warn') */\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n /** Support tuning parameters */\n tuningParameters?: Record<string, any>;\n}\n\ntype SpanData = {\n logger: Logger<true>; // Braintrust logger (for root spans)\n spans: Map<string, Span>; // Maps span.id to Braintrust span\n};\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<AISpanType, string>> = {\n [AISpanType.LLM_GENERATION]: 'llm',\n [AISpanType.LLM_CHUNK]: 'llm',\n [AISpanType.TOOL_CALL]: 'tool',\n [AISpanType.MCP_TOOL_CALL]: 'tool',\n [AISpanType.WORKFLOW_CONDITIONAL_EVAL]: 'function',\n [AISpanType.WORKFLOW_WAIT_EVENT]: 'function',\n};\n\n// Mapping function - returns valid Braintrust span types\nfunction mapSpanType(spanType: AISpanType): 'llm' | 'score' | 'function' | 'eval' | 'task' | 'tool' {\n return (SPAN_TYPE_EXCEPTIONS[spanType] as any) ?? DEFAULT_SPAN_TYPE;\n}\n\nexport class BraintrustExporter implements AITracingExporter {\n name = 'braintrust';\n private traceMap = new Map<string, SpanData>();\n private logger: ConsoleLogger;\n private config: BraintrustExporterConfig;\n\n constructor(config: BraintrustExporterConfig) {\n this.logger = new ConsoleLogger({ level: config.logLevel ?? 'warn' });\n\n if (!config.apiKey) {\n this.logger.error('BraintrustExporter: Missing required credentials, exporter will be disabled', {\n hasApiKey: !!config.apiKey,\n });\n this.config = null as any;\n return;\n }\n\n this.config = config;\n }\n\n async exportEvent(event: AITracingEvent): Promise<void> {\n if (!this.config) {\n return;\n }\n\n if (event.span.isEvent) {\n await this.handleEventSpan(event.span);\n return;\n }\n\n switch (event.type) {\n case 'span_started':\n await this.handleSpanStarted(event.span);\n break;\n case 'span_updated':\n await this.handleSpanUpdateOrEnd(event.span, false);\n break;\n case 'span_ended':\n await this.handleSpanUpdateOrEnd(event.span, true);\n break;\n }\n }\n\n private async handleSpanStarted(span: AnyAISpan): Promise<void> {\n if (span.isRootSpan) {\n await this.initLogger(span);\n }\n\n const method = 'handleSpanStarted';\n const spanData = this.getSpanData({ span, method });\n if (!spanData) {\n return;\n }\n\n const braintrustParent = this.getBraintrustParent({ spanData, span, method });\n if (!braintrustParent) {\n return;\n }\n\n const payload = this.buildSpanPayload(span);\n\n const braintrustSpan = braintrustParent.startSpan({\n name: span.name,\n type: mapSpanType(span.type),\n ...payload,\n });\n\n spanData.spans.set(span.id, braintrustSpan);\n }\n\n private async handleSpanUpdateOrEnd(span: AnyAISpan, isEnd: boolean): Promise<void> {\n const method = isEnd ? 'handleSpanEnd' : 'handleSpanUpdate';\n\n const spanData = this.getSpanData({ span, method });\n if (!spanData) {\n return;\n }\n\n const braintrustSpan = spanData.spans.get(span.id);\n if (!braintrustSpan) {\n this.logger.warn('Braintrust exporter: No Braintrust span found for span update/end', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parent?.id,\n method,\n });\n return;\n }\n\n braintrustSpan.log(this.buildSpanPayload(span));\n\n if (isEnd) {\n // End the span with the correct endTime (convert milliseconds to seconds)\n if (span.endTime) {\n braintrustSpan.end({ endTime: span.endTime.getTime() / 1000 });\n } else {\n braintrustSpan.end();\n }\n\n if (span.isRootSpan) {\n this.traceMap.delete(span.traceId);\n }\n }\n }\n\n private async handleEventSpan(span: AnyAISpan): Promise<void> {\n if (span.isRootSpan) {\n this.logger.debug('Braintrust exporter: Creating logger for event', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n method: 'handleEventSpan',\n });\n await this.initLogger(span);\n }\n\n const method = 'handleEventSpan';\n const spanData = this.getSpanData({ span, method });\n if (!spanData) {\n return;\n }\n\n const braintrustParent = this.getBraintrustParent({ spanData, span, method });\n if (!braintrustParent) {\n return;\n }\n\n const payload = this.buildSpanPayload(span);\n\n // Create zero-duration span for event (convert milliseconds to seconds)\n const braintrustSpan = braintrustParent.startSpan({\n name: span.name,\n type: mapSpanType(span.type),\n startTime: span.startTime.getTime() / 1000,\n ...payload,\n });\n\n braintrustSpan.end({ endTime: span.startTime.getTime() / 1000 });\n spanData.spans.set(span.id, braintrustSpan);\n }\n\n private async initLogger(span: AnyAISpan): Promise<void> {\n const logger = await initLogger({\n projectName: 'mastra-tracing', // TODO: Make this configurable\n apiKey: this.config.apiKey,\n appUrl: this.config.endpoint,\n ...this.config.tuningParameters,\n });\n\n this.traceMap.set(span.traceId, { logger, spans: new Map() });\n }\n\n private getSpanData(options: { span: AnyAISpan; method: string }): SpanData | undefined {\n const { span, method } = options;\n if (this.traceMap.has(span.traceId)) {\n return this.traceMap.get(span.traceId);\n }\n\n this.logger.warn('Braintrust exporter: No span data found for span', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parent?.id,\n method,\n });\n }\n\n private getBraintrustParent(options: {\n spanData: SpanData;\n span: AnyAISpan;\n method: string;\n }): Logger<true> | Span | undefined {\n const { spanData, span, method } = options;\n\n const parentId = span.parent?.id;\n if (!parentId) {\n return spanData.logger;\n }\n\n if (spanData.spans.has(parentId)) {\n return spanData.spans.get(parentId);\n }\n\n this.logger.warn('Braintrust exporter: No parent data found for span', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parent?.id,\n method,\n });\n }\n\n private buildSpanPayload(span: AnyAISpan): Record<string, any> {\n const payload: Record<string, any> = {};\n\n // Core span data\n if (span.input !== undefined) {\n payload.input = span.input;\n }\n\n if (span.output !== undefined) {\n payload.output = span.output;\n }\n\n // Initialize metrics and metadata objects\n payload.metrics = {};\n payload.metadata = {\n spanType: span.type,\n ...span.metadata,\n };\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n if (span.type === AISpanType.LLM_GENERATION) {\n const llmAttr = attributes as LLMGenerationAttributes;\n\n // Model goes to metadata\n if (llmAttr.model !== undefined) {\n payload.metadata.model = llmAttr.model;\n }\n\n // Usage/token info goes to metrics\n if (llmAttr.usage !== undefined) {\n payload.metrics = {\n ...payload.metrics,\n ...llmAttr.usage,\n };\n }\n\n // Model parameters go to metadata\n if (llmAttr.parameters !== undefined) {\n payload.metadata.modelParameters = llmAttr.parameters;\n }\n\n // Other LLM attributes go to metadata\n const otherAttributes = omitKeys(attributes, ['model', 'usage', 'parameters']);\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 return payload;\n }\n\n async shutdown(): Promise<void> {\n if (!this.config) {\n return;\n }\n\n // End all active spans\n for (const [_traceId, spanData] of this.traceMap) {\n for (const [_spanId, span] of spanData.spans) {\n span.end();\n }\n // Loggers don't have an explicit shutdown method\n }\n this.traceMap.clear();\n }\n}\n"]}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Braintrust Observability Provider for Mastra
|
|
3
|
+
*
|
|
4
|
+
* This package provides Braintrust-specific observability features for Mastra applications.
|
|
5
|
+
* Currently includes AI tracing support with plans for additional observability features.
|
|
6
|
+
*/
|
|
7
|
+
export * from './ai-tracing.js';
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,cAAc,cAAc,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import { AISpanType, omitKeys } from '@mastra/core/ai-tracing';
|
|
2
|
+
import { ConsoleLogger } from '@mastra/core/logger';
|
|
3
|
+
import { initLogger } from 'braintrust';
|
|
4
|
+
|
|
5
|
+
// src/ai-tracing.ts
|
|
6
|
+
var DEFAULT_SPAN_TYPE = "task";
|
|
7
|
+
var SPAN_TYPE_EXCEPTIONS = {
|
|
8
|
+
[AISpanType.LLM_GENERATION]: "llm",
|
|
9
|
+
[AISpanType.LLM_CHUNK]: "llm",
|
|
10
|
+
[AISpanType.TOOL_CALL]: "tool",
|
|
11
|
+
[AISpanType.MCP_TOOL_CALL]: "tool",
|
|
12
|
+
[AISpanType.WORKFLOW_CONDITIONAL_EVAL]: "function",
|
|
13
|
+
[AISpanType.WORKFLOW_WAIT_EVENT]: "function"
|
|
14
|
+
};
|
|
15
|
+
function mapSpanType(spanType) {
|
|
16
|
+
return SPAN_TYPE_EXCEPTIONS[spanType] ?? DEFAULT_SPAN_TYPE;
|
|
17
|
+
}
|
|
18
|
+
var BraintrustExporter = class {
|
|
19
|
+
name = "braintrust";
|
|
20
|
+
traceMap = /* @__PURE__ */ new Map();
|
|
21
|
+
logger;
|
|
22
|
+
config;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.logger = new ConsoleLogger({ level: config.logLevel ?? "warn" });
|
|
25
|
+
if (!config.apiKey) {
|
|
26
|
+
this.logger.error("BraintrustExporter: Missing required credentials, exporter will be disabled", {
|
|
27
|
+
hasApiKey: !!config.apiKey
|
|
28
|
+
});
|
|
29
|
+
this.config = null;
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
this.config = config;
|
|
33
|
+
}
|
|
34
|
+
async exportEvent(event) {
|
|
35
|
+
if (!this.config) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (event.span.isEvent) {
|
|
39
|
+
await this.handleEventSpan(event.span);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
switch (event.type) {
|
|
43
|
+
case "span_started":
|
|
44
|
+
await this.handleSpanStarted(event.span);
|
|
45
|
+
break;
|
|
46
|
+
case "span_updated":
|
|
47
|
+
await this.handleSpanUpdateOrEnd(event.span, false);
|
|
48
|
+
break;
|
|
49
|
+
case "span_ended":
|
|
50
|
+
await this.handleSpanUpdateOrEnd(event.span, true);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
async handleSpanStarted(span) {
|
|
55
|
+
if (span.isRootSpan) {
|
|
56
|
+
await this.initLogger(span);
|
|
57
|
+
}
|
|
58
|
+
const method = "handleSpanStarted";
|
|
59
|
+
const spanData = this.getSpanData({ span, method });
|
|
60
|
+
if (!spanData) {
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const braintrustParent = this.getBraintrustParent({ spanData, span, method });
|
|
64
|
+
if (!braintrustParent) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
const payload = this.buildSpanPayload(span);
|
|
68
|
+
const braintrustSpan = braintrustParent.startSpan({
|
|
69
|
+
name: span.name,
|
|
70
|
+
type: mapSpanType(span.type),
|
|
71
|
+
...payload
|
|
72
|
+
});
|
|
73
|
+
spanData.spans.set(span.id, braintrustSpan);
|
|
74
|
+
}
|
|
75
|
+
async handleSpanUpdateOrEnd(span, isEnd) {
|
|
76
|
+
const method = isEnd ? "handleSpanEnd" : "handleSpanUpdate";
|
|
77
|
+
const spanData = this.getSpanData({ span, method });
|
|
78
|
+
if (!spanData) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
const braintrustSpan = spanData.spans.get(span.id);
|
|
82
|
+
if (!braintrustSpan) {
|
|
83
|
+
this.logger.warn("Braintrust exporter: No Braintrust span found for span update/end", {
|
|
84
|
+
traceId: span.traceId,
|
|
85
|
+
spanId: span.id,
|
|
86
|
+
spanName: span.name,
|
|
87
|
+
spanType: span.type,
|
|
88
|
+
isRootSpan: span.isRootSpan,
|
|
89
|
+
parentSpanId: span.parent?.id,
|
|
90
|
+
method
|
|
91
|
+
});
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
braintrustSpan.log(this.buildSpanPayload(span));
|
|
95
|
+
if (isEnd) {
|
|
96
|
+
if (span.endTime) {
|
|
97
|
+
braintrustSpan.end({ endTime: span.endTime.getTime() / 1e3 });
|
|
98
|
+
} else {
|
|
99
|
+
braintrustSpan.end();
|
|
100
|
+
}
|
|
101
|
+
if (span.isRootSpan) {
|
|
102
|
+
this.traceMap.delete(span.traceId);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
async handleEventSpan(span) {
|
|
107
|
+
if (span.isRootSpan) {
|
|
108
|
+
this.logger.debug("Braintrust exporter: Creating logger for event", {
|
|
109
|
+
traceId: span.traceId,
|
|
110
|
+
spanId: span.id,
|
|
111
|
+
spanName: span.name,
|
|
112
|
+
method: "handleEventSpan"
|
|
113
|
+
});
|
|
114
|
+
await this.initLogger(span);
|
|
115
|
+
}
|
|
116
|
+
const method = "handleEventSpan";
|
|
117
|
+
const spanData = this.getSpanData({ span, method });
|
|
118
|
+
if (!spanData) {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const braintrustParent = this.getBraintrustParent({ spanData, span, method });
|
|
122
|
+
if (!braintrustParent) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
const payload = this.buildSpanPayload(span);
|
|
126
|
+
const braintrustSpan = braintrustParent.startSpan({
|
|
127
|
+
name: span.name,
|
|
128
|
+
type: mapSpanType(span.type),
|
|
129
|
+
startTime: span.startTime.getTime() / 1e3,
|
|
130
|
+
...payload
|
|
131
|
+
});
|
|
132
|
+
braintrustSpan.end({ endTime: span.startTime.getTime() / 1e3 });
|
|
133
|
+
spanData.spans.set(span.id, braintrustSpan);
|
|
134
|
+
}
|
|
135
|
+
async initLogger(span) {
|
|
136
|
+
const logger = await initLogger({
|
|
137
|
+
projectName: "mastra-tracing",
|
|
138
|
+
// TODO: Make this configurable
|
|
139
|
+
apiKey: this.config.apiKey,
|
|
140
|
+
appUrl: this.config.endpoint,
|
|
141
|
+
...this.config.tuningParameters
|
|
142
|
+
});
|
|
143
|
+
this.traceMap.set(span.traceId, { logger, spans: /* @__PURE__ */ new Map() });
|
|
144
|
+
}
|
|
145
|
+
getSpanData(options) {
|
|
146
|
+
const { span, method } = options;
|
|
147
|
+
if (this.traceMap.has(span.traceId)) {
|
|
148
|
+
return this.traceMap.get(span.traceId);
|
|
149
|
+
}
|
|
150
|
+
this.logger.warn("Braintrust exporter: No span data found for span", {
|
|
151
|
+
traceId: span.traceId,
|
|
152
|
+
spanId: span.id,
|
|
153
|
+
spanName: span.name,
|
|
154
|
+
spanType: span.type,
|
|
155
|
+
isRootSpan: span.isRootSpan,
|
|
156
|
+
parentSpanId: span.parent?.id,
|
|
157
|
+
method
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
getBraintrustParent(options) {
|
|
161
|
+
const { spanData, span, method } = options;
|
|
162
|
+
const parentId = span.parent?.id;
|
|
163
|
+
if (!parentId) {
|
|
164
|
+
return spanData.logger;
|
|
165
|
+
}
|
|
166
|
+
if (spanData.spans.has(parentId)) {
|
|
167
|
+
return spanData.spans.get(parentId);
|
|
168
|
+
}
|
|
169
|
+
this.logger.warn("Braintrust exporter: No parent data found for span", {
|
|
170
|
+
traceId: span.traceId,
|
|
171
|
+
spanId: span.id,
|
|
172
|
+
spanName: span.name,
|
|
173
|
+
spanType: span.type,
|
|
174
|
+
isRootSpan: span.isRootSpan,
|
|
175
|
+
parentSpanId: span.parent?.id,
|
|
176
|
+
method
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
buildSpanPayload(span) {
|
|
180
|
+
const payload = {};
|
|
181
|
+
if (span.input !== void 0) {
|
|
182
|
+
payload.input = span.input;
|
|
183
|
+
}
|
|
184
|
+
if (span.output !== void 0) {
|
|
185
|
+
payload.output = span.output;
|
|
186
|
+
}
|
|
187
|
+
payload.metrics = {};
|
|
188
|
+
payload.metadata = {
|
|
189
|
+
spanType: span.type,
|
|
190
|
+
...span.metadata
|
|
191
|
+
};
|
|
192
|
+
const attributes = span.attributes ?? {};
|
|
193
|
+
if (span.type === AISpanType.LLM_GENERATION) {
|
|
194
|
+
const llmAttr = attributes;
|
|
195
|
+
if (llmAttr.model !== void 0) {
|
|
196
|
+
payload.metadata.model = llmAttr.model;
|
|
197
|
+
}
|
|
198
|
+
if (llmAttr.usage !== void 0) {
|
|
199
|
+
payload.metrics = {
|
|
200
|
+
...payload.metrics,
|
|
201
|
+
...llmAttr.usage
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (llmAttr.parameters !== void 0) {
|
|
205
|
+
payload.metadata.modelParameters = llmAttr.parameters;
|
|
206
|
+
}
|
|
207
|
+
const otherAttributes = omitKeys(attributes, ["model", "usage", "parameters"]);
|
|
208
|
+
payload.metadata = {
|
|
209
|
+
...payload.metadata,
|
|
210
|
+
...otherAttributes
|
|
211
|
+
};
|
|
212
|
+
} else {
|
|
213
|
+
payload.metadata = {
|
|
214
|
+
...payload.metadata,
|
|
215
|
+
...attributes
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
if (span.errorInfo) {
|
|
219
|
+
payload.error = span.errorInfo.message;
|
|
220
|
+
payload.metadata.errorDetails = span.errorInfo;
|
|
221
|
+
}
|
|
222
|
+
if (Object.keys(payload.metrics).length === 0) {
|
|
223
|
+
delete payload.metrics;
|
|
224
|
+
}
|
|
225
|
+
return payload;
|
|
226
|
+
}
|
|
227
|
+
async shutdown() {
|
|
228
|
+
if (!this.config) {
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
for (const [_traceId, spanData] of this.traceMap) {
|
|
232
|
+
for (const [_spanId, span] of spanData.spans) {
|
|
233
|
+
span.end();
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
this.traceMap.clear();
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
export { BraintrustExporter };
|
|
241
|
+
//# sourceMappingURL=index.js.map
|
|
242
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/ai-tracing.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Braintrust Exporter for Mastra AI Tracing\n *\n * This exporter sends tracing data to Braintrust for AI observability.\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 { AITracingExporter, AITracingEvent, AnyAISpan, LLMGenerationAttributes } from '@mastra/core/ai-tracing';\nimport { AISpanType, omitKeys } from '@mastra/core/ai-tracing';\nimport { ConsoleLogger } from '@mastra/core/logger';\nimport { initLogger } from 'braintrust';\nimport type { Span, Logger } from 'braintrust';\n\nexport interface BraintrustExporterConfig {\n /** Braintrust API key */\n apiKey?: string;\n /** Optional custom endpoint */\n endpoint?: string;\n /** Logger level for diagnostic messages (default: 'warn') */\n logLevel?: 'debug' | 'info' | 'warn' | 'error';\n /** Support tuning parameters */\n tuningParameters?: Record<string, any>;\n}\n\ntype SpanData = {\n logger: Logger<true>; // Braintrust logger (for root spans)\n spans: Map<string, Span>; // Maps span.id to Braintrust span\n};\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<AISpanType, string>> = {\n [AISpanType.LLM_GENERATION]: 'llm',\n [AISpanType.LLM_CHUNK]: 'llm',\n [AISpanType.TOOL_CALL]: 'tool',\n [AISpanType.MCP_TOOL_CALL]: 'tool',\n [AISpanType.WORKFLOW_CONDITIONAL_EVAL]: 'function',\n [AISpanType.WORKFLOW_WAIT_EVENT]: 'function',\n};\n\n// Mapping function - returns valid Braintrust span types\nfunction mapSpanType(spanType: AISpanType): 'llm' | 'score' | 'function' | 'eval' | 'task' | 'tool' {\n return (SPAN_TYPE_EXCEPTIONS[spanType] as any) ?? DEFAULT_SPAN_TYPE;\n}\n\nexport class BraintrustExporter implements AITracingExporter {\n name = 'braintrust';\n private traceMap = new Map<string, SpanData>();\n private logger: ConsoleLogger;\n private config: BraintrustExporterConfig;\n\n constructor(config: BraintrustExporterConfig) {\n this.logger = new ConsoleLogger({ level: config.logLevel ?? 'warn' });\n\n if (!config.apiKey) {\n this.logger.error('BraintrustExporter: Missing required credentials, exporter will be disabled', {\n hasApiKey: !!config.apiKey,\n });\n this.config = null as any;\n return;\n }\n\n this.config = config;\n }\n\n async exportEvent(event: AITracingEvent): Promise<void> {\n if (!this.config) {\n return;\n }\n\n if (event.span.isEvent) {\n await this.handleEventSpan(event.span);\n return;\n }\n\n switch (event.type) {\n case 'span_started':\n await this.handleSpanStarted(event.span);\n break;\n case 'span_updated':\n await this.handleSpanUpdateOrEnd(event.span, false);\n break;\n case 'span_ended':\n await this.handleSpanUpdateOrEnd(event.span, true);\n break;\n }\n }\n\n private async handleSpanStarted(span: AnyAISpan): Promise<void> {\n if (span.isRootSpan) {\n await this.initLogger(span);\n }\n\n const method = 'handleSpanStarted';\n const spanData = this.getSpanData({ span, method });\n if (!spanData) {\n return;\n }\n\n const braintrustParent = this.getBraintrustParent({ spanData, span, method });\n if (!braintrustParent) {\n return;\n }\n\n const payload = this.buildSpanPayload(span);\n\n const braintrustSpan = braintrustParent.startSpan({\n name: span.name,\n type: mapSpanType(span.type),\n ...payload,\n });\n\n spanData.spans.set(span.id, braintrustSpan);\n }\n\n private async handleSpanUpdateOrEnd(span: AnyAISpan, isEnd: boolean): Promise<void> {\n const method = isEnd ? 'handleSpanEnd' : 'handleSpanUpdate';\n\n const spanData = this.getSpanData({ span, method });\n if (!spanData) {\n return;\n }\n\n const braintrustSpan = spanData.spans.get(span.id);\n if (!braintrustSpan) {\n this.logger.warn('Braintrust exporter: No Braintrust span found for span update/end', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parent?.id,\n method,\n });\n return;\n }\n\n braintrustSpan.log(this.buildSpanPayload(span));\n\n if (isEnd) {\n // End the span with the correct endTime (convert milliseconds to seconds)\n if (span.endTime) {\n braintrustSpan.end({ endTime: span.endTime.getTime() / 1000 });\n } else {\n braintrustSpan.end();\n }\n\n if (span.isRootSpan) {\n this.traceMap.delete(span.traceId);\n }\n }\n }\n\n private async handleEventSpan(span: AnyAISpan): Promise<void> {\n if (span.isRootSpan) {\n this.logger.debug('Braintrust exporter: Creating logger for event', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n method: 'handleEventSpan',\n });\n await this.initLogger(span);\n }\n\n const method = 'handleEventSpan';\n const spanData = this.getSpanData({ span, method });\n if (!spanData) {\n return;\n }\n\n const braintrustParent = this.getBraintrustParent({ spanData, span, method });\n if (!braintrustParent) {\n return;\n }\n\n const payload = this.buildSpanPayload(span);\n\n // Create zero-duration span for event (convert milliseconds to seconds)\n const braintrustSpan = braintrustParent.startSpan({\n name: span.name,\n type: mapSpanType(span.type),\n startTime: span.startTime.getTime() / 1000,\n ...payload,\n });\n\n braintrustSpan.end({ endTime: span.startTime.getTime() / 1000 });\n spanData.spans.set(span.id, braintrustSpan);\n }\n\n private async initLogger(span: AnyAISpan): Promise<void> {\n const logger = await initLogger({\n projectName: 'mastra-tracing', // TODO: Make this configurable\n apiKey: this.config.apiKey,\n appUrl: this.config.endpoint,\n ...this.config.tuningParameters,\n });\n\n this.traceMap.set(span.traceId, { logger, spans: new Map() });\n }\n\n private getSpanData(options: { span: AnyAISpan; method: string }): SpanData | undefined {\n const { span, method } = options;\n if (this.traceMap.has(span.traceId)) {\n return this.traceMap.get(span.traceId);\n }\n\n this.logger.warn('Braintrust exporter: No span data found for span', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parent?.id,\n method,\n });\n }\n\n private getBraintrustParent(options: {\n spanData: SpanData;\n span: AnyAISpan;\n method: string;\n }): Logger<true> | Span | undefined {\n const { spanData, span, method } = options;\n\n const parentId = span.parent?.id;\n if (!parentId) {\n return spanData.logger;\n }\n\n if (spanData.spans.has(parentId)) {\n return spanData.spans.get(parentId);\n }\n\n this.logger.warn('Braintrust exporter: No parent data found for span', {\n traceId: span.traceId,\n spanId: span.id,\n spanName: span.name,\n spanType: span.type,\n isRootSpan: span.isRootSpan,\n parentSpanId: span.parent?.id,\n method,\n });\n }\n\n private buildSpanPayload(span: AnyAISpan): Record<string, any> {\n const payload: Record<string, any> = {};\n\n // Core span data\n if (span.input !== undefined) {\n payload.input = span.input;\n }\n\n if (span.output !== undefined) {\n payload.output = span.output;\n }\n\n // Initialize metrics and metadata objects\n payload.metrics = {};\n payload.metadata = {\n spanType: span.type,\n ...span.metadata,\n };\n\n const attributes = (span.attributes ?? {}) as Record<string, any>;\n\n if (span.type === AISpanType.LLM_GENERATION) {\n const llmAttr = attributes as LLMGenerationAttributes;\n\n // Model goes to metadata\n if (llmAttr.model !== undefined) {\n payload.metadata.model = llmAttr.model;\n }\n\n // Usage/token info goes to metrics\n if (llmAttr.usage !== undefined) {\n payload.metrics = {\n ...payload.metrics,\n ...llmAttr.usage,\n };\n }\n\n // Model parameters go to metadata\n if (llmAttr.parameters !== undefined) {\n payload.metadata.modelParameters = llmAttr.parameters;\n }\n\n // Other LLM attributes go to metadata\n const otherAttributes = omitKeys(attributes, ['model', 'usage', 'parameters']);\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 return payload;\n }\n\n async shutdown(): Promise<void> {\n if (!this.config) {\n return;\n }\n\n // End all active spans\n for (const [_traceId, spanData] of this.traceMap) {\n for (const [_spanId, span] of spanData.spans) {\n span.end();\n }\n // Loggers don't have an explicit shutdown method\n }\n this.traceMap.clear();\n }\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mastra/braintrust",
|
|
3
|
+
"version": "0.0.0-issue-7498-20250905233741",
|
|
4
|
+
"description": "Braintrust observability provider for Mastra - includes AI tracing and future observability features",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"CHANGELOG.md"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"import": {
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"default": "./dist/index.js"
|
|
17
|
+
},
|
|
18
|
+
"require": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.cjs"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"./package.json": "./package.json"
|
|
24
|
+
},
|
|
25
|
+
"license": "Apache-2.0",
|
|
26
|
+
"dependencies": {
|
|
27
|
+
"braintrust": "^0.3.6"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@microsoft/api-extractor": "^7.52.8",
|
|
31
|
+
"@types/node": "^20.19.0",
|
|
32
|
+
"eslint": "^9.30.1",
|
|
33
|
+
"tsup": "^8.5.0",
|
|
34
|
+
"typescript": "^5.8.3",
|
|
35
|
+
"vitest": "^3.2.4",
|
|
36
|
+
"@mastra/core": "0.0.0-issue-7498-20250905233741",
|
|
37
|
+
"@internal/types-builder": "0.0.0-issue-7498-20250905233741",
|
|
38
|
+
"@internal/lint": "0.0.0-issue-7498-20250905233741"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@mastra/core": "0.0.0-issue-7498-20250905233741"
|
|
42
|
+
},
|
|
43
|
+
"homepage": "https://mastra.ai",
|
|
44
|
+
"repository": {
|
|
45
|
+
"type": "git",
|
|
46
|
+
"url": "git+https://github.com/mastra-ai/mastra.git",
|
|
47
|
+
"directory": "observability/braintrust"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/mastra-ai/mastra/issues"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"build": "tsup --silent --config tsup.config.ts",
|
|
54
|
+
"build:watch": "pnpm build --watch",
|
|
55
|
+
"test": "vitest run",
|
|
56
|
+
"test:watch": "vitest watch",
|
|
57
|
+
"lint": "eslint ."
|
|
58
|
+
}
|
|
59
|
+
}
|