@mastra/laminar 1.3.5 → 1.3.6

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/dist/index.cjs CHANGED
@@ -1,521 +1,453 @@
1
- 'use strict';
2
-
3
- var observability$1 = require('@mastra/core/observability');
4
- var observability = require('@mastra/observability');
5
- var api = require('@opentelemetry/api');
6
- var exporterTraceOtlpProto = require('@opentelemetry/exporter-trace-otlp-proto');
7
- var resources = require('@opentelemetry/resources');
8
- var sdkTraceBase = require('@opentelemetry/sdk-trace-base');
9
- var semanticConventions = require('@opentelemetry/semantic-conventions');
10
-
11
- // src/tracing.ts
12
- var LMNR_SPAN_INPUT = "lmnr.span.input";
13
- var LMNR_SPAN_OUTPUT = "lmnr.span.output";
14
- var LMNR_SPAN_TYPE = "lmnr.span.type";
15
- var LMNR_SPAN_PATH = "lmnr.span.path";
16
- var LMNR_SPAN_IDS_PATH = "lmnr.span.ids_path";
17
- var LMNR_SPAN_INSTRUMENTATION_SOURCE = "lmnr.span.instrumentation_source";
18
- var LMNR_SPAN_SDK_VERSION = "lmnr.span.sdk_version";
19
- var LMNR_SPAN_LANGUAGE_VERSION = "lmnr.span.language_version";
20
- var LMNR_ASSOCIATION_PREFIX = "lmnr.association.properties";
21
- var LMNR_SESSION_ID = `${LMNR_ASSOCIATION_PREFIX}.session_id`;
22
- var LMNR_USER_ID = `${LMNR_ASSOCIATION_PREFIX}.user_id`;
23
- var LMNR_TAGS = `${LMNR_ASSOCIATION_PREFIX}.tags`;
24
- var GEN_AI_SYSTEM = "gen_ai.system";
25
- var GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
26
- var GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
27
- var GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens";
28
- var GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
29
- var GEN_AI_CACHE_WRITE_INPUT_TOKENS = "gen_ai.usage.cache_creation_input_tokens";
30
- var GEN_AI_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read_input_tokens";
31
- var LaminarExporter = class extends observability.BaseExporter {
32
- name = "laminar";
33
- config;
34
- traceMap = /* @__PURE__ */ new Map();
35
- resource;
36
- scope;
37
- processor;
38
- exporter;
39
- isSetup = false;
40
- constructor(config = {}) {
41
- super(config);
42
- const apiKey = config.apiKey ?? process.env.LMNR_PROJECT_API_KEY;
43
- if (!apiKey) {
44
- this.setDisabled(
45
- "Missing required API key. Set LMNR_PROJECT_API_KEY environment variable or pass apiKey in config."
46
- );
47
- this.config = null;
48
- return;
49
- }
50
- const envEndpoint = process.env.LAMINAR_ENDPOINT;
51
- const baseUrl = stripTrailingSlash(config.baseUrl ?? process.env.LMNR_BASE_URL ?? "https://api.lmnr.ai");
52
- const endpoint = config.endpoint ?? envEndpoint ?? `${baseUrl}/v1/traces`;
53
- const headers = {
54
- ...config.headers,
55
- Authorization: `Bearer ${apiKey}`
56
- };
57
- this.config = {
58
- apiKey,
59
- baseUrl,
60
- endpoint,
61
- headers,
62
- realtime: config.realtime ?? false,
63
- disableBatch: config.disableBatch ?? false,
64
- batchSize: config.batchSize ?? 512,
65
- timeoutMillis: config.timeoutMillis ?? 3e4
66
- };
67
- }
68
- init(options) {
69
- const serviceName = options.config?.serviceName || "mastra-service";
70
- this.resource = resources.resourceFromAttributes({
71
- [semanticConventions.ATTR_SERVICE_NAME]: serviceName,
72
- [semanticConventions.ATTR_SERVICE_VERSION]: "unknown",
73
- [semanticConventions.ATTR_TELEMETRY_SDK_NAME]: "@mastra/laminar",
74
- [semanticConventions.ATTR_TELEMETRY_SDK_VERSION]: "unknown",
75
- [semanticConventions.ATTR_TELEMETRY_SDK_LANGUAGE]: "nodejs"
76
- });
77
- this.scope = {
78
- name: "@mastra/laminar",
79
- version: "unknown"
80
- };
81
- }
82
- async _exportTracingEvent(event) {
83
- if (event.type === observability$1.TracingEventType.SPAN_STARTED && !event.exportedSpan.isEvent) {
84
- this.handleSpanStarted(event.exportedSpan);
85
- return;
86
- }
87
- if (event.type !== observability$1.TracingEventType.SPAN_ENDED) {
88
- return;
89
- }
90
- await this.handleSpanEnded(event.exportedSpan);
91
- }
92
- handleSpanStarted(span) {
93
- const traceState = this.getOrCreateTraceState(span.traceId);
94
- const name = span.name;
95
- const parentId = span.parentSpanId;
96
- const parentPath = parentId ? traceState.spanPathById.get(parentId) : void 0;
97
- const parentIdsPath = parentId ? traceState.spanIdsPathById.get(parentId) : void 0;
98
- const spanPath = parentPath ? [...parentPath, name] : [name];
99
- const spanIdsPath = parentIdsPath ? [...parentIdsPath, otelSpanIdToUUID(span.id)] : [otelSpanIdToUUID(span.id)];
100
- traceState.spanPathById.set(span.id, spanPath);
101
- traceState.spanIdsPathById.set(span.id, spanIdsPath);
102
- traceState.activeSpanIds.add(span.id);
103
- }
104
- async handleSpanEnded(span) {
105
- if (!this.config) {
106
- return;
107
- }
108
- await this.setupIfNeeded();
109
- if (!this.processor || !this.exporter) {
110
- return;
111
- }
112
- const traceState = this.getOrCreateTraceState(span.traceId);
113
- if (!traceState.spanPathById.has(span.id) || !traceState.spanIdsPathById.has(span.id)) {
114
- const name = span.name;
115
- const parentId = span.parentSpanId;
116
- const parentPath = parentId ? traceState.spanPathById.get(parentId) : void 0;
117
- const parentIdsPath = parentId ? traceState.spanIdsPathById.get(parentId) : void 0;
118
- const spanPath = parentPath ? [...parentPath, name] : [name];
119
- const spanIdsPath = parentIdsPath ? [...parentIdsPath, otelSpanIdToUUID(span.id)] : [otelSpanIdToUUID(span.id)];
120
- traceState.spanPathById.set(span.id, spanPath);
121
- traceState.spanIdsPathById.set(span.id, spanIdsPath);
122
- }
123
- try {
124
- const otelSpan = this.convertSpanToOtel(span, traceState);
125
- this.processor.onEnd(otelSpan);
126
- if (this.config.realtime) {
127
- await this.processor.forceFlush();
128
- }
129
- } catch (error) {
130
- this.logger.error("[LaminarExporter] Failed to export span", { error, spanId: span.id, traceId: span.traceId });
131
- } finally {
132
- traceState.activeSpanIds.delete(span.id);
133
- if (traceState.activeSpanIds.size === 0) {
134
- this.traceMap.delete(span.traceId);
135
- }
136
- }
137
- }
138
- getOrCreateTraceState(traceId) {
139
- const existing = this.traceMap.get(traceId);
140
- if (existing) return existing;
141
- const created = {
142
- spanPathById: /* @__PURE__ */ new Map(),
143
- spanIdsPathById: /* @__PURE__ */ new Map(),
144
- activeSpanIds: /* @__PURE__ */ new Set()
145
- };
146
- this.traceMap.set(traceId, created);
147
- return created;
148
- }
149
- convertSpanToOtel(span, traceState) {
150
- if (!this.resource || !this.scope) {
151
- this.resource = resources.resourceFromAttributes({
152
- [semanticConventions.ATTR_SERVICE_NAME]: "mastra-service",
153
- [semanticConventions.ATTR_SERVICE_VERSION]: "unknown",
154
- [semanticConventions.ATTR_TELEMETRY_SDK_NAME]: "@mastra/laminar",
155
- [semanticConventions.ATTR_TELEMETRY_SDK_VERSION]: "unknown",
156
- [semanticConventions.ATTR_TELEMETRY_SDK_LANGUAGE]: "nodejs"
157
- });
158
- this.scope = { name: "@mastra/laminar", version: "unknown" };
159
- }
160
- const name = span.name;
161
- const kind = getSpanKind(span.type);
162
- const startTime = dateToHrTime(span.startTime);
163
- const endTime = span.endTime ? dateToHrTime(span.endTime) : startTime;
164
- const duration = computeDuration(span.startTime, span.endTime);
165
- const { status, events } = buildStatusAndEvents(span, startTime);
166
- const traceId = normalizeTraceId(span.traceId);
167
- const spanId = normalizeSpanId(span.id);
168
- const spanContext = {
169
- traceId,
170
- spanId,
171
- traceFlags: api.TraceFlags.SAMPLED,
172
- isRemote: false
173
- };
174
- const parentSpanContext = span.parentSpanId ? {
175
- traceId,
176
- spanId: normalizeSpanId(span.parentSpanId),
177
- traceFlags: api.TraceFlags.SAMPLED,
178
- isRemote: false
179
- } : void 0;
180
- const attributes = buildLaminarAttributes(span, traceState);
181
- const links = [];
182
- const readable = {
183
- name,
184
- kind,
185
- spanContext: () => spanContext,
186
- parentSpanContext,
187
- startTime,
188
- endTime,
189
- status,
190
- attributes,
191
- links,
192
- events,
193
- duration,
194
- ended: true,
195
- resource: this.resource,
196
- instrumentationScope: this.scope,
197
- droppedAttributesCount: 0,
198
- droppedEventsCount: 0,
199
- droppedLinksCount: 0
200
- };
201
- return readable;
202
- }
203
- async setupIfNeeded() {
204
- if (this.isSetup || !this.config) {
205
- return;
206
- }
207
- this.exporter = new exporterTraceOtlpProto.OTLPTraceExporter({
208
- url: this.config.endpoint,
209
- headers: this.config.headers,
210
- timeoutMillis: this.config.timeoutMillis
211
- });
212
- this.processor = this.config.disableBatch ? new sdkTraceBase.SimpleSpanProcessor(this.exporter) : new sdkTraceBase.BatchSpanProcessor(this.exporter, {
213
- maxExportBatchSize: this.config.batchSize,
214
- exportTimeoutMillis: this.config.timeoutMillis
215
- });
216
- this.isSetup = true;
217
- }
218
- async submitScore(args) {
219
- if (!this.config) return;
220
- const { traceId, spanId, name, score, reason, metadata } = args;
221
- const payload = {
222
- name,
223
- score,
224
- source: "Code",
225
- metadata: { ...metadata ?? {}, ...reason ? { reason } : {} }
226
- };
227
- if (spanId) {
228
- payload.spanId = otelSpanIdToUUID(spanId);
229
- } else {
230
- payload.traceId = otelTraceIdToUUID(traceId);
231
- }
232
- const headers = {
233
- Authorization: `Bearer ${this.config.apiKey}`,
234
- "content-type": "application/json"
235
- };
236
- const controller = new AbortController();
237
- const timer = setTimeout(() => controller.abort(), this.config.timeoutMillis);
238
- try {
239
- const response = await fetch(`${stripTrailingSlash(this.config.baseUrl)}/v1/evaluators/score`, {
240
- method: "POST",
241
- headers,
242
- body: JSON.stringify(payload),
243
- signal: controller.signal
244
- });
245
- if (!response.ok) {
246
- this.logger.warn("[LaminarExporter] Failed to attach score to trace/span", {
247
- status: response.status,
248
- statusText: response.statusText,
249
- traceId,
250
- spanId,
251
- name
252
- });
253
- }
254
- } catch (error) {
255
- const isAbort = error instanceof Error && error.name === "AbortError";
256
- this.logger.error("[LaminarExporter] Error attaching score to trace/span", {
257
- error,
258
- timedOut: isAbort,
259
- timeoutMillis: this.config.timeoutMillis,
260
- traceId,
261
- spanId,
262
- name
263
- });
264
- } finally {
265
- clearTimeout(timer);
266
- }
267
- }
268
- async onScoreEvent(event) {
269
- const { score } = event;
270
- if (!score.traceId) return;
271
- await this.submitScore({
272
- traceId: score.traceId,
273
- spanId: score.spanId,
274
- name: score.scorerName ?? score.scorerId,
275
- score: score.score,
276
- reason: score.reason,
277
- metadata: score.metadata
278
- });
279
- }
280
- /**
281
- * @deprecated Use the observability score event pipeline (`mastra.observability.addScore`)
282
- * instead. Preserved for backwards compatibility; forwards to the same Laminar score endpoint
283
- * as `onScoreEvent`.
284
- */
285
- async _addScoreToTrace(args) {
286
- await this.submitScore({
287
- traceId: args.traceId,
288
- spanId: args.spanId,
289
- name: args.scorerName,
290
- score: args.score,
291
- reason: args.reason,
292
- metadata: args.metadata
293
- });
294
- }
295
- /**
296
- * Force flush any buffered spans without shutting down the exporter.
297
- * This is useful in serverless environments where you need to ensure spans
298
- * are exported before the runtime instance is terminated.
299
- */
300
- async flush() {
301
- if (this.isDisabled || !this.processor) return;
302
- try {
303
- await this.processor.forceFlush();
304
- this.logger.debug("[LaminarExporter] Flushed pending spans");
305
- } catch (error) {
306
- this.logger.error("[LaminarExporter] Error flushing spans", { error });
307
- }
308
- }
309
- async shutdown() {
310
- try {
311
- await this.processor?.shutdown();
312
- } finally {
313
- this.traceMap.clear();
314
- await super.shutdown();
315
- }
316
- }
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ let _mastra_core_observability = require("@mastra/core/observability");
3
+ let _mastra_observability = require("@mastra/observability");
4
+ let _opentelemetry_api = require("@opentelemetry/api");
5
+ let _opentelemetry_exporter_trace_otlp_proto = require("@opentelemetry/exporter-trace-otlp-proto");
6
+ let _opentelemetry_resources = require("@opentelemetry/resources");
7
+ let _opentelemetry_sdk_trace_base = require("@opentelemetry/sdk-trace-base");
8
+ let _opentelemetry_semantic_conventions = require("@opentelemetry/semantic-conventions");
9
+ //#region src/tracing.ts
10
+ const LMNR_SPAN_INPUT = "lmnr.span.input";
11
+ const LMNR_SPAN_OUTPUT = "lmnr.span.output";
12
+ const LMNR_SPAN_TYPE = "lmnr.span.type";
13
+ const LMNR_SPAN_PATH = "lmnr.span.path";
14
+ const LMNR_SPAN_IDS_PATH = "lmnr.span.ids_path";
15
+ const LMNR_SPAN_INSTRUMENTATION_SOURCE = "lmnr.span.instrumentation_source";
16
+ const LMNR_SPAN_SDK_VERSION = "lmnr.span.sdk_version";
17
+ const LMNR_SPAN_LANGUAGE_VERSION = "lmnr.span.language_version";
18
+ const LMNR_ASSOCIATION_PREFIX = "lmnr.association.properties";
19
+ const LMNR_SESSION_ID = `${LMNR_ASSOCIATION_PREFIX}.session_id`;
20
+ const LMNR_USER_ID = `${LMNR_ASSOCIATION_PREFIX}.user_id`;
21
+ const LMNR_TAGS = `${LMNR_ASSOCIATION_PREFIX}.tags`;
22
+ const GEN_AI_SYSTEM = "gen_ai.system";
23
+ const GEN_AI_REQUEST_MODEL = "gen_ai.request.model";
24
+ const GEN_AI_RESPONSE_MODEL = "gen_ai.response.model";
25
+ const GEN_AI_USAGE_INPUT_TOKENS = "gen_ai.usage.input_tokens";
26
+ const GEN_AI_USAGE_OUTPUT_TOKENS = "gen_ai.usage.output_tokens";
27
+ const GEN_AI_CACHE_WRITE_INPUT_TOKENS = "gen_ai.usage.cache_creation_input_tokens";
28
+ const GEN_AI_CACHE_READ_INPUT_TOKENS = "gen_ai.usage.cache_read_input_tokens";
29
+ var LaminarExporter = class extends _mastra_observability.BaseExporter {
30
+ name = "laminar";
31
+ config;
32
+ traceMap = /* @__PURE__ */ new Map();
33
+ resource;
34
+ scope;
35
+ processor;
36
+ exporter;
37
+ isSetup = false;
38
+ constructor(config = {}) {
39
+ super(config);
40
+ const apiKey = config.apiKey ?? process.env.LMNR_PROJECT_API_KEY;
41
+ if (!apiKey) {
42
+ this.setDisabled("Missing required API key. Set LMNR_PROJECT_API_KEY environment variable or pass apiKey in config.");
43
+ this.config = null;
44
+ return;
45
+ }
46
+ const envEndpoint = process.env.LAMINAR_ENDPOINT;
47
+ const baseUrl = stripTrailingSlash(config.baseUrl ?? process.env.LMNR_BASE_URL ?? "https://api.lmnr.ai");
48
+ const endpoint = config.endpoint ?? envEndpoint ?? `${baseUrl}/v1/traces`;
49
+ const headers = {
50
+ ...config.headers,
51
+ Authorization: `Bearer ${apiKey}`
52
+ };
53
+ this.config = {
54
+ apiKey,
55
+ baseUrl,
56
+ endpoint,
57
+ headers,
58
+ realtime: config.realtime ?? false,
59
+ disableBatch: config.disableBatch ?? false,
60
+ batchSize: config.batchSize ?? 512,
61
+ timeoutMillis: config.timeoutMillis ?? 3e4
62
+ };
63
+ }
64
+ init(options) {
65
+ const serviceName = options.config?.serviceName || "mastra-service";
66
+ this.resource = (0, _opentelemetry_resources.resourceFromAttributes)({
67
+ [_opentelemetry_semantic_conventions.ATTR_SERVICE_NAME]: serviceName,
68
+ [_opentelemetry_semantic_conventions.ATTR_SERVICE_VERSION]: "unknown",
69
+ [_opentelemetry_semantic_conventions.ATTR_TELEMETRY_SDK_NAME]: "@mastra/laminar",
70
+ [_opentelemetry_semantic_conventions.ATTR_TELEMETRY_SDK_VERSION]: "unknown",
71
+ [_opentelemetry_semantic_conventions.ATTR_TELEMETRY_SDK_LANGUAGE]: "nodejs"
72
+ });
73
+ this.scope = {
74
+ name: "@mastra/laminar",
75
+ version: "unknown"
76
+ };
77
+ }
78
+ async _exportTracingEvent(event) {
79
+ if (event.type === _mastra_core_observability.TracingEventType.SPAN_STARTED && !event.exportedSpan.isEvent) {
80
+ this.handleSpanStarted(event.exportedSpan);
81
+ return;
82
+ }
83
+ if (event.type !== _mastra_core_observability.TracingEventType.SPAN_ENDED) return;
84
+ await this.handleSpanEnded(event.exportedSpan);
85
+ }
86
+ handleSpanStarted(span) {
87
+ const traceState = this.getOrCreateTraceState(span.traceId);
88
+ const name = span.name;
89
+ const parentId = span.parentSpanId;
90
+ const parentPath = parentId ? traceState.spanPathById.get(parentId) : void 0;
91
+ const parentIdsPath = parentId ? traceState.spanIdsPathById.get(parentId) : void 0;
92
+ const spanPath = parentPath ? [...parentPath, name] : [name];
93
+ const spanIdsPath = parentIdsPath ? [...parentIdsPath, otelSpanIdToUUID(span.id)] : [otelSpanIdToUUID(span.id)];
94
+ traceState.spanPathById.set(span.id, spanPath);
95
+ traceState.spanIdsPathById.set(span.id, spanIdsPath);
96
+ traceState.activeSpanIds.add(span.id);
97
+ }
98
+ async handleSpanEnded(span) {
99
+ if (!this.config) return;
100
+ await this.setupIfNeeded();
101
+ if (!this.processor || !this.exporter) return;
102
+ const traceState = this.getOrCreateTraceState(span.traceId);
103
+ if (!traceState.spanPathById.has(span.id) || !traceState.spanIdsPathById.has(span.id)) {
104
+ const name = span.name;
105
+ const parentId = span.parentSpanId;
106
+ const parentPath = parentId ? traceState.spanPathById.get(parentId) : void 0;
107
+ const parentIdsPath = parentId ? traceState.spanIdsPathById.get(parentId) : void 0;
108
+ const spanPath = parentPath ? [...parentPath, name] : [name];
109
+ const spanIdsPath = parentIdsPath ? [...parentIdsPath, otelSpanIdToUUID(span.id)] : [otelSpanIdToUUID(span.id)];
110
+ traceState.spanPathById.set(span.id, spanPath);
111
+ traceState.spanIdsPathById.set(span.id, spanIdsPath);
112
+ }
113
+ try {
114
+ const otelSpan = this.convertSpanToOtel(span, traceState);
115
+ this.processor.onEnd(otelSpan);
116
+ if (this.config.realtime) await this.processor.forceFlush();
117
+ } catch (error) {
118
+ this.logger.error("[LaminarExporter] Failed to export span", {
119
+ error,
120
+ spanId: span.id,
121
+ traceId: span.traceId
122
+ });
123
+ } finally {
124
+ traceState.activeSpanIds.delete(span.id);
125
+ if (traceState.activeSpanIds.size === 0) this.traceMap.delete(span.traceId);
126
+ }
127
+ }
128
+ getOrCreateTraceState(traceId) {
129
+ const existing = this.traceMap.get(traceId);
130
+ if (existing) return existing;
131
+ const created = {
132
+ spanPathById: /* @__PURE__ */ new Map(),
133
+ spanIdsPathById: /* @__PURE__ */ new Map(),
134
+ activeSpanIds: /* @__PURE__ */ new Set()
135
+ };
136
+ this.traceMap.set(traceId, created);
137
+ return created;
138
+ }
139
+ convertSpanToOtel(span, traceState) {
140
+ if (!this.resource || !this.scope) {
141
+ this.resource = (0, _opentelemetry_resources.resourceFromAttributes)({
142
+ [_opentelemetry_semantic_conventions.ATTR_SERVICE_NAME]: "mastra-service",
143
+ [_opentelemetry_semantic_conventions.ATTR_SERVICE_VERSION]: "unknown",
144
+ [_opentelemetry_semantic_conventions.ATTR_TELEMETRY_SDK_NAME]: "@mastra/laminar",
145
+ [_opentelemetry_semantic_conventions.ATTR_TELEMETRY_SDK_VERSION]: "unknown",
146
+ [_opentelemetry_semantic_conventions.ATTR_TELEMETRY_SDK_LANGUAGE]: "nodejs"
147
+ });
148
+ this.scope = {
149
+ name: "@mastra/laminar",
150
+ version: "unknown"
151
+ };
152
+ }
153
+ const name = span.name;
154
+ const kind = getSpanKind(span.type);
155
+ const startTime = dateToHrTime(span.startTime);
156
+ const endTime = span.endTime ? dateToHrTime(span.endTime) : startTime;
157
+ const duration = computeDuration(span.startTime, span.endTime);
158
+ const { status, events } = buildStatusAndEvents(span, startTime);
159
+ const traceId = normalizeTraceId(span.traceId);
160
+ const spanContext = {
161
+ traceId,
162
+ spanId: normalizeSpanId(span.id),
163
+ traceFlags: _opentelemetry_api.TraceFlags.SAMPLED,
164
+ isRemote: false
165
+ };
166
+ return {
167
+ name,
168
+ kind,
169
+ spanContext: () => spanContext,
170
+ parentSpanContext: span.parentSpanId ? {
171
+ traceId,
172
+ spanId: normalizeSpanId(span.parentSpanId),
173
+ traceFlags: _opentelemetry_api.TraceFlags.SAMPLED,
174
+ isRemote: false
175
+ } : void 0,
176
+ startTime,
177
+ endTime,
178
+ status,
179
+ attributes: buildLaminarAttributes(span, traceState),
180
+ links: [],
181
+ events,
182
+ duration,
183
+ ended: true,
184
+ resource: this.resource,
185
+ instrumentationScope: this.scope,
186
+ droppedAttributesCount: 0,
187
+ droppedEventsCount: 0,
188
+ droppedLinksCount: 0
189
+ };
190
+ }
191
+ async setupIfNeeded() {
192
+ if (this.isSetup || !this.config) return;
193
+ this.exporter = new _opentelemetry_exporter_trace_otlp_proto.OTLPTraceExporter({
194
+ url: this.config.endpoint,
195
+ headers: this.config.headers,
196
+ timeoutMillis: this.config.timeoutMillis
197
+ });
198
+ this.processor = this.config.disableBatch ? new _opentelemetry_sdk_trace_base.SimpleSpanProcessor(this.exporter) : new _opentelemetry_sdk_trace_base.BatchSpanProcessor(this.exporter, {
199
+ maxExportBatchSize: this.config.batchSize,
200
+ exportTimeoutMillis: this.config.timeoutMillis
201
+ });
202
+ this.isSetup = true;
203
+ }
204
+ async submitScore(args) {
205
+ if (!this.config) return;
206
+ const { traceId, spanId, name, score, reason, metadata } = args;
207
+ const payload = {
208
+ name,
209
+ score,
210
+ source: "Code",
211
+ metadata: {
212
+ ...metadata ?? {},
213
+ ...reason ? { reason } : {}
214
+ }
215
+ };
216
+ if (spanId) payload.spanId = otelSpanIdToUUID(spanId);
217
+ else payload.traceId = otelTraceIdToUUID(traceId);
218
+ const headers = {
219
+ Authorization: `Bearer ${this.config.apiKey}`,
220
+ "content-type": "application/json"
221
+ };
222
+ const controller = new AbortController();
223
+ const timer = setTimeout(() => controller.abort(), this.config.timeoutMillis);
224
+ try {
225
+ const response = await fetch(`${stripTrailingSlash(this.config.baseUrl)}/v1/evaluators/score`, {
226
+ method: "POST",
227
+ headers,
228
+ body: JSON.stringify(payload),
229
+ signal: controller.signal
230
+ });
231
+ if (!response.ok) this.logger.warn("[LaminarExporter] Failed to attach score to trace/span", {
232
+ status: response.status,
233
+ statusText: response.statusText,
234
+ traceId,
235
+ spanId,
236
+ name
237
+ });
238
+ } catch (error) {
239
+ const isAbort = error instanceof Error && error.name === "AbortError";
240
+ this.logger.error("[LaminarExporter] Error attaching score to trace/span", {
241
+ error,
242
+ timedOut: isAbort,
243
+ timeoutMillis: this.config.timeoutMillis,
244
+ traceId,
245
+ spanId,
246
+ name
247
+ });
248
+ } finally {
249
+ clearTimeout(timer);
250
+ }
251
+ }
252
+ async onScoreEvent(event) {
253
+ const { score } = event;
254
+ if (!score.traceId) return;
255
+ await this.submitScore({
256
+ traceId: score.traceId,
257
+ spanId: score.spanId,
258
+ name: score.scorerName ?? score.scorerId,
259
+ score: score.score,
260
+ reason: score.reason,
261
+ metadata: score.metadata
262
+ });
263
+ }
264
+ /**
265
+ * @deprecated Use the observability score event pipeline (`mastra.observability.addScore`)
266
+ * instead. Preserved for backwards compatibility; forwards to the same Laminar score endpoint
267
+ * as `onScoreEvent`.
268
+ */
269
+ async _addScoreToTrace(args) {
270
+ await this.submitScore({
271
+ traceId: args.traceId,
272
+ spanId: args.spanId,
273
+ name: args.scorerName,
274
+ score: args.score,
275
+ reason: args.reason,
276
+ metadata: args.metadata
277
+ });
278
+ }
279
+ /**
280
+ * Force flush any buffered spans without shutting down the exporter.
281
+ * This is useful in serverless environments where you need to ensure spans
282
+ * are exported before the runtime instance is terminated.
283
+ */
284
+ async flush() {
285
+ if (this.isDisabled || !this.processor) return;
286
+ try {
287
+ await this.processor.forceFlush();
288
+ this.logger.debug("[LaminarExporter] Flushed pending spans");
289
+ } catch (error) {
290
+ this.logger.error("[LaminarExporter] Error flushing spans", { error });
291
+ }
292
+ }
293
+ async shutdown() {
294
+ try {
295
+ await this.processor?.shutdown();
296
+ } finally {
297
+ this.traceMap.clear();
298
+ await super.shutdown();
299
+ }
300
+ }
317
301
  };
318
302
  function buildLaminarAttributes(span, traceState) {
319
- const attributes = {};
320
- const spanPath = traceState.spanPathById.get(span.id);
321
- const spanIdsPath = traceState.spanIdsPathById.get(span.id);
322
- if (spanPath) {
323
- attributes[LMNR_SPAN_PATH] = spanPath;
324
- }
325
- if (spanIdsPath) {
326
- attributes[LMNR_SPAN_IDS_PATH] = spanIdsPath;
327
- }
328
- attributes[LMNR_SPAN_TYPE] = mapLaminarSpanType(span.type);
329
- attributes[LMNR_SPAN_INSTRUMENTATION_SOURCE] = "javascript";
330
- attributes[LMNR_SPAN_SDK_VERSION] = "unknown";
331
- attributes[LMNR_SPAN_LANGUAGE_VERSION] = process.version;
332
- const sessionId = span.metadata?.sessionId;
333
- if (typeof sessionId === "string" && sessionId.length > 0) {
334
- attributes[LMNR_SESSION_ID] = sessionId;
335
- }
336
- const userId = span.metadata?.userId;
337
- if (typeof userId === "string" && userId.length > 0) {
338
- attributes[LMNR_USER_ID] = userId;
339
- }
340
- if (span.metadata) {
341
- for (const [key, value] of Object.entries(span.metadata)) {
342
- if (key === "sessionId" || key === "userId" || value === void 0 || value === null) {
343
- continue;
344
- }
345
- const attributeValue = toLaminarAttributeValue(value);
346
- if (attributeValue === void 0) {
347
- continue;
348
- }
349
- attributes[`${LMNR_ASSOCIATION_PREFIX}.metadata.${key}`] = attributeValue;
350
- }
351
- }
352
- if (span.isRootSpan && span.tags?.length) {
353
- attributes[LMNR_TAGS] = span.tags;
354
- }
355
- if (span.input !== void 0) {
356
- attributes[LMNR_SPAN_INPUT] = serializeForLaminar(getLaminarSpanInput(span));
357
- }
358
- if (span.output !== void 0) {
359
- attributes[LMNR_SPAN_OUTPUT] = serializeForLaminar(span.output);
360
- }
361
- if (span.type === observability$1.SpanType.MODEL_GENERATION) {
362
- const modelAttrs = span.attributes ?? {};
363
- if (modelAttrs.provider) {
364
- attributes[GEN_AI_SYSTEM] = normalizeProvider(modelAttrs.provider);
365
- }
366
- if (modelAttrs.model) {
367
- attributes[GEN_AI_REQUEST_MODEL] = modelAttrs.model;
368
- }
369
- if (modelAttrs.responseModel) {
370
- attributes[GEN_AI_RESPONSE_MODEL] = modelAttrs.responseModel;
371
- }
372
- Object.assign(attributes, formatLaminarUsage(modelAttrs.usage));
373
- }
374
- return attributes;
303
+ const attributes = {};
304
+ const spanPath = traceState.spanPathById.get(span.id);
305
+ const spanIdsPath = traceState.spanIdsPathById.get(span.id);
306
+ if (spanPath) attributes[LMNR_SPAN_PATH] = spanPath;
307
+ if (spanIdsPath) attributes[LMNR_SPAN_IDS_PATH] = spanIdsPath;
308
+ attributes[LMNR_SPAN_TYPE] = mapLaminarSpanType(span.type);
309
+ attributes[LMNR_SPAN_INSTRUMENTATION_SOURCE] = "javascript";
310
+ attributes[LMNR_SPAN_SDK_VERSION] = "unknown";
311
+ attributes[LMNR_SPAN_LANGUAGE_VERSION] = process.version;
312
+ const sessionId = span.metadata?.sessionId;
313
+ if (typeof sessionId === "string" && sessionId.length > 0) attributes[LMNR_SESSION_ID] = sessionId;
314
+ const userId = span.metadata?.userId;
315
+ if (typeof userId === "string" && userId.length > 0) attributes[LMNR_USER_ID] = userId;
316
+ if (span.metadata) for (const [key, value] of Object.entries(span.metadata)) {
317
+ if (key === "sessionId" || key === "userId" || value === void 0 || value === null) continue;
318
+ const attributeValue = toLaminarAttributeValue(value);
319
+ if (attributeValue === void 0) continue;
320
+ attributes[`${LMNR_ASSOCIATION_PREFIX}.metadata.${key}`] = attributeValue;
321
+ }
322
+ if (span.isRootSpan && span.tags?.length) attributes[LMNR_TAGS] = span.tags;
323
+ if (span.input !== void 0) attributes[LMNR_SPAN_INPUT] = serializeForLaminar(getLaminarSpanInput(span));
324
+ if (span.output !== void 0) attributes[LMNR_SPAN_OUTPUT] = serializeForLaminar(span.output);
325
+ if (span.type === _mastra_core_observability.SpanType.MODEL_GENERATION) {
326
+ const modelAttrs = span.attributes ?? {};
327
+ if (modelAttrs.provider) attributes[GEN_AI_SYSTEM] = normalizeProvider(modelAttrs.provider);
328
+ if (modelAttrs.model) attributes[GEN_AI_REQUEST_MODEL] = modelAttrs.model;
329
+ if (modelAttrs.responseModel) attributes[GEN_AI_RESPONSE_MODEL] = modelAttrs.responseModel;
330
+ Object.assign(attributes, formatLaminarUsage(modelAttrs.usage));
331
+ }
332
+ return attributes;
375
333
  }
376
334
  function mapLaminarSpanType(spanType) {
377
- switch (spanType) {
378
- case observability$1.SpanType.MODEL_GENERATION:
379
- case observability$1.SpanType.MODEL_STEP:
380
- case observability$1.SpanType.MODEL_CHUNK:
381
- return "LLM";
382
- case observability$1.SpanType.TOOL_CALL:
383
- case observability$1.SpanType.MCP_TOOL_CALL:
384
- case observability$1.SpanType.PROVIDER_TOOL_CALL:
385
- return "TOOL";
386
- default:
387
- return "DEFAULT";
388
- }
335
+ switch (spanType) {
336
+ case _mastra_core_observability.SpanType.MODEL_GENERATION:
337
+ case _mastra_core_observability.SpanType.MODEL_STEP:
338
+ case _mastra_core_observability.SpanType.MODEL_CHUNK: return "LLM";
339
+ case _mastra_core_observability.SpanType.TOOL_CALL:
340
+ case _mastra_core_observability.SpanType.MCP_TOOL_CALL:
341
+ case _mastra_core_observability.SpanType.PROVIDER_TOOL_CALL: return "TOOL";
342
+ default: return "DEFAULT";
343
+ }
389
344
  }
390
345
  function formatLaminarUsage(usage) {
391
- if (!usage) return {};
392
- const out = {};
393
- if (usage.inputTokens !== void 0) {
394
- out[GEN_AI_USAGE_INPUT_TOKENS] = usage.inputTokens;
395
- }
396
- if (usage.outputTokens !== void 0) {
397
- out[GEN_AI_USAGE_OUTPUT_TOKENS] = usage.outputTokens;
398
- }
399
- if (usage.inputDetails?.cacheWrite !== void 0) {
400
- out[GEN_AI_CACHE_WRITE_INPUT_TOKENS] = usage.inputDetails.cacheWrite;
401
- }
402
- if (usage.inputDetails?.cacheRead !== void 0) {
403
- out[GEN_AI_CACHE_READ_INPUT_TOKENS] = usage.inputDetails.cacheRead;
404
- }
405
- return out;
346
+ if (!usage) return {};
347
+ const out = {};
348
+ if (usage.inputTokens !== void 0) out[GEN_AI_USAGE_INPUT_TOKENS] = usage.inputTokens;
349
+ if (usage.outputTokens !== void 0) out[GEN_AI_USAGE_OUTPUT_TOKENS] = usage.outputTokens;
350
+ if (usage.inputDetails?.cacheWrite !== void 0) out[GEN_AI_CACHE_WRITE_INPUT_TOKENS] = usage.inputDetails.cacheWrite;
351
+ if (usage.inputDetails?.cacheRead !== void 0) out[GEN_AI_CACHE_READ_INPUT_TOKENS] = usage.inputDetails.cacheRead;
352
+ return out;
406
353
  }
407
354
  function serializeForLaminar(value) {
408
- if (typeof value === "string") {
409
- return value;
410
- }
411
- try {
412
- return JSON.stringify(value);
413
- } catch {
414
- return "[unserializable]";
415
- }
355
+ if (typeof value === "string") return value;
356
+ try {
357
+ return JSON.stringify(value);
358
+ } catch {
359
+ return "[unserializable]";
360
+ }
416
361
  }
417
362
  function getLaminarSpanInput(span) {
418
- if (span.type !== observability$1.SpanType.MODEL_GENERATION) {
419
- return span.input;
420
- }
421
- const input = span.input;
422
- if (!input || typeof input !== "object" || Array.isArray(input)) {
423
- return input;
424
- }
425
- const maybeMessages = input.messages;
426
- return Array.isArray(maybeMessages) ? maybeMessages : input;
363
+ if (span.type !== _mastra_core_observability.SpanType.MODEL_GENERATION) return span.input;
364
+ const input = span.input;
365
+ if (!input || typeof input !== "object" || Array.isArray(input)) return input;
366
+ const maybeMessages = input.messages;
367
+ return Array.isArray(maybeMessages) ? maybeMessages : input;
427
368
  }
428
369
  function toLaminarAttributeValue(value) {
429
- if (value === void 0 || value === null) {
430
- return void 0;
431
- }
432
- if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
433
- return value;
434
- }
435
- if (Array.isArray(value)) {
436
- const isHomogeneous = value.every((v) => typeof v === "string") || value.every((v) => typeof v === "number") || value.every((v) => typeof v === "boolean");
437
- if (isHomogeneous) return value;
438
- }
439
- return serializeForLaminar(value);
370
+ if (value === void 0 || value === null) return;
371
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
372
+ if (Array.isArray(value)) {
373
+ if (value.every((v) => typeof v === "string") || value.every((v) => typeof v === "number") || value.every((v) => typeof v === "boolean")) return value;
374
+ }
375
+ return serializeForLaminar(value);
440
376
  }
377
+ /**
378
+ * Convert JavaScript Date to hrtime format
379
+ */
441
380
  function dateToHrTime(date) {
442
- const ms = date.getTime();
443
- const seconds = Math.floor(ms / 1e3);
444
- const nanoseconds = ms % 1e3 * 1e6;
445
- return [seconds, nanoseconds];
381
+ const ms = date.getTime();
382
+ return [Math.floor(ms / 1e3), ms % 1e3 * 1e6];
446
383
  }
447
384
  function computeDuration(start, end) {
448
- if (!end) return [0, 0];
449
- const diffMs = end.getTime() - start.getTime();
450
- return [Math.floor(diffMs / 1e3), diffMs % 1e3 * 1e6];
385
+ if (!end) return [0, 0];
386
+ const diffMs = end.getTime() - start.getTime();
387
+ return [Math.floor(diffMs / 1e3), diffMs % 1e3 * 1e6];
451
388
  }
452
389
  function buildStatusAndEvents(span, defaultTime) {
453
- const events = [];
454
- if (span.errorInfo) {
455
- const status = {
456
- code: api.SpanStatusCode.ERROR,
457
- message: span.errorInfo.message
458
- };
459
- events.push({
460
- name: "exception",
461
- attributes: {
462
- "exception.message": span.errorInfo.message,
463
- "exception.type": "Error",
464
- ...span.errorInfo.details?.stack && {
465
- "exception.stacktrace": span.errorInfo.details.stack
466
- }
467
- },
468
- time: defaultTime,
469
- droppedAttributesCount: 0
470
- });
471
- return { status, events };
472
- }
473
- return {
474
- status: { code: api.SpanStatusCode.OK },
475
- events
476
- };
390
+ const events = [];
391
+ if (span.errorInfo) {
392
+ const status = {
393
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
394
+ message: span.errorInfo.message
395
+ };
396
+ events.push({
397
+ name: "exception",
398
+ attributes: {
399
+ "exception.message": span.errorInfo.message,
400
+ "exception.type": "Error",
401
+ ...span.errorInfo.details?.stack && { "exception.stacktrace": span.errorInfo.details.stack }
402
+ },
403
+ time: defaultTime,
404
+ droppedAttributesCount: 0
405
+ });
406
+ return {
407
+ status,
408
+ events
409
+ };
410
+ }
411
+ return {
412
+ status: { code: _opentelemetry_api.SpanStatusCode.OK },
413
+ events
414
+ };
477
415
  }
478
416
  function getSpanKind(type) {
479
- switch (type) {
480
- case observability$1.SpanType.MODEL_GENERATION:
481
- case observability$1.SpanType.MCP_TOOL_CALL:
482
- return api.SpanKind.CLIENT;
483
- default:
484
- return api.SpanKind.INTERNAL;
485
- }
417
+ switch (type) {
418
+ case _mastra_core_observability.SpanType.MODEL_GENERATION:
419
+ case _mastra_core_observability.SpanType.MCP_TOOL_CALL: return _opentelemetry_api.SpanKind.CLIENT;
420
+ default: return _opentelemetry_api.SpanKind.INTERNAL;
421
+ }
486
422
  }
487
423
  function stripTrailingSlash(url) {
488
- let end = url.length;
489
- while (end > 0 && url.charCodeAt(end - 1) === 47) {
490
- end--;
491
- }
492
- return end === url.length ? url : url.slice(0, end);
424
+ let end = url.length;
425
+ while (end > 0 && url.charCodeAt(end - 1) === 47) end--;
426
+ return end === url.length ? url : url.slice(0, end);
493
427
  }
494
428
  function normalizeTraceId(traceId) {
495
- let id = traceId.toLowerCase();
496
- if (id.startsWith("0x")) id = id.slice(2);
497
- return id.padStart(32, "0").slice(-32);
429
+ let id = traceId.toLowerCase();
430
+ if (id.startsWith("0x")) id = id.slice(2);
431
+ return id.padStart(32, "0").slice(-32);
498
432
  }
499
433
  function normalizeSpanId(spanId) {
500
- let id = spanId.toLowerCase();
501
- if (id.startsWith("0x")) id = id.slice(2);
502
- return id.padStart(16, "0").slice(-16);
434
+ let id = spanId.toLowerCase();
435
+ if (id.startsWith("0x")) id = id.slice(2);
436
+ return id.padStart(16, "0").slice(-16);
503
437
  }
504
438
  function otelSpanIdToUUID(spanId) {
505
- const normalized = normalizeSpanId(spanId);
506
- return normalized.padStart(32, "0").replace(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/, "$1-$2-$3-$4-$5");
439
+ return normalizeSpanId(spanId).padStart(32, "0").replace(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/, "$1-$2-$3-$4-$5");
507
440
  }
508
441
  function otelTraceIdToUUID(traceId) {
509
- const normalized = normalizeTraceId(traceId);
510
- return normalized.replace(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/, "$1-$2-$3-$4-$5");
442
+ return normalizeTraceId(traceId).replace(/^([0-9a-f]{8})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{4})([0-9a-f]{12})$/, "$1-$2-$3-$4-$5");
511
443
  }
512
444
  function normalizeProvider(provider) {
513
- return provider.split(".").shift()?.toLowerCase().trim() || provider.toLowerCase().trim();
445
+ return provider.split(".").shift()?.toLowerCase().trim() || provider.toLowerCase().trim();
514
446
  }
515
-
447
+ //#endregion
516
448
  exports.LaminarExporter = LaminarExporter;
517
449
  exports.otelSpanIdToUUID = otelSpanIdToUUID;
518
450
  exports.otelTraceIdToUUID = otelTraceIdToUUID;
519
451
  exports.stripTrailingSlash = stripTrailingSlash;
520
- //# sourceMappingURL=index.cjs.map
452
+
521
453
  //# sourceMappingURL=index.cjs.map