@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 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"]}
@@ -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
+ }