@lmnr-ai/lmnr 0.8.20 → 0.8.21

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.
Files changed (39) hide show
  1. package/dist/cli/worker/build.cjs +1 -1
  2. package/dist/cli/worker/build.mjs +1 -1
  3. package/dist/cli/worker/index.cjs +1 -1
  4. package/dist/cli/worker/index.mjs +1 -1
  5. package/dist/cli.cjs +3 -3
  6. package/dist/cli.d.cts +1 -1
  7. package/dist/cli.d.mts +1 -1
  8. package/dist/cli.mjs +3 -3
  9. package/dist/{decorators-Cf0xs-v_.mjs → decorators-BDFgXfgI.mjs} +820 -139
  10. package/dist/decorators-BDFgXfgI.mjs.map +1 -0
  11. package/dist/{decorators-u7xFb_9W.cjs → decorators-CaTC0lJq.cjs} +831 -138
  12. package/dist/decorators-CaTC0lJq.cjs.map +1 -0
  13. package/dist/{dist-BwllJ__m.mjs → dist-B5CmOQ4s.mjs} +3 -3
  14. package/dist/{dist-BwllJ__m.mjs.map → dist-B5CmOQ4s.mjs.map} +1 -1
  15. package/dist/{dist-zDtQ_gVq.cjs → dist-B5cKzRzq.cjs} +3 -3
  16. package/dist/{dist-zDtQ_gVq.cjs.map → dist-B5cKzRzq.cjs.map} +1 -1
  17. package/dist/{evaluations-D2duRqbG.d.cts → evaluations-BJkIwpmH.d.cts} +15 -1
  18. package/dist/{evaluations-DEbBIXng.d.mts → evaluations-tqpNgp0P.d.mts} +15 -1
  19. package/dist/{file-utils-C-auksVC.mjs → file-utils-CHoR9VB8.mjs} +2 -2
  20. package/dist/{file-utils-C-auksVC.mjs.map → file-utils-CHoR9VB8.mjs.map} +1 -1
  21. package/dist/file-utils-IBEBwZxq.cjs +2 -0
  22. package/dist/{file-utils-0mVr0eIv.cjs → file-utils-yJ5ephze.cjs} +2 -2
  23. package/dist/{file-utils-0mVr0eIv.cjs.map → file-utils-yJ5ephze.cjs.map} +1 -1
  24. package/dist/index.cjs +563 -9
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.cts +125 -3
  27. package/dist/index.d.mts +125 -3
  28. package/dist/index.mjs +564 -11
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/{utils-BmWNkeX5.cjs → utils-C8Tl1vKD.cjs} +23 -1
  31. package/dist/utils-C8Tl1vKD.cjs.map +1 -0
  32. package/dist/{utils-0WC6BVqs.mjs → utils-CHJ0KZUR.mjs} +12 -2
  33. package/dist/utils-CHJ0KZUR.mjs.map +1 -0
  34. package/package.json +4 -3
  35. package/dist/decorators-Cf0xs-v_.mjs.map +0 -1
  36. package/dist/decorators-u7xFb_9W.cjs.map +0 -1
  37. package/dist/file-utils-2hY2DP65.cjs +0 -2
  38. package/dist/utils-0WC6BVqs.mjs.map +0 -1
  39. package/dist/utils-BmWNkeX5.cjs.map +0 -1
package/dist/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
2
  const require_chunk = require("./chunk-Do9eywBl.cjs");
3
- const require_dist = require("./dist-zDtQ_gVq.cjs");
4
- const require_utils = require("./utils-BmWNkeX5.cjs");
5
- const require_decorators = require("./decorators-u7xFb_9W.cjs");
3
+ const require_dist = require("./dist-B5cKzRzq.cjs");
4
+ const require_utils = require("./utils-C8Tl1vKD.cjs");
5
+ const require_decorators = require("./decorators-CaTC0lJq.cjs");
6
6
  let _opentelemetry_api = require("@opentelemetry/api");
7
7
  let cli_progress = require("cli-progress");
8
8
  cli_progress = require_chunk.__toESM(cli_progress);
@@ -59,7 +59,7 @@ var LaminarDataset = class extends EvaluationDataset {
59
59
  */
60
60
  async push(paths, recursive = false) {
61
61
  if (!this.client) throw new Error("Client not set");
62
- const { loadFromPaths } = await Promise.resolve().then(() => require("./file-utils-2hY2DP65.cjs"));
62
+ const { loadFromPaths } = await Promise.resolve().then(() => require("./file-utils-IBEBwZxq.cjs"));
63
63
  const data = await loadFromPaths(Array.isArray(paths) ? paths : [paths], recursive);
64
64
  if (data.length === 0) {
65
65
  console.warn("No data to push. Skipping");
@@ -78,7 +78,7 @@ var LaminarDataset = class extends EvaluationDataset {
78
78
  require_utils.loadEnv();
79
79
  const DEFAULT_CONCURRENCY = 5;
80
80
  const MAX_EXPORT_BATCH_SIZE = 64;
81
- const logger = require_utils.initializeLogger();
81
+ const logger$1 = require_utils.initializeLogger();
82
82
  const getEvaluationUrl = (projectId, evaluationId, baseUrl, frontendPort) => {
83
83
  return `${require_utils.getFrontendUrl(baseUrl, frontendPort)}/project/${projectId}/evaluations/${evaluationId}`;
84
84
  };
@@ -151,7 +151,7 @@ var Evaluation = class {
151
151
  this.name = name;
152
152
  if (config) {
153
153
  if (config.concurrencyLimit !== void 0 && config.concurrencyLimit < 1) {
154
- logger.warn(`concurrencyLimit must be greater than 0. Setting to default of ${DEFAULT_CONCURRENCY}`);
154
+ logger$1.warn(`concurrencyLimit must be greater than 0. Setting to default of ${DEFAULT_CONCURRENCY}`);
155
155
  this.concurrencyLimit = DEFAULT_CONCURRENCY;
156
156
  } else this.concurrencyLimit = config.concurrencyLimit ?? DEFAULT_CONCURRENCY;
157
157
  this.traceDisableBatch = config.traceDisableBatch ?? false;
@@ -163,7 +163,7 @@ var Evaluation = class {
163
163
  baseUrl: require_decorators.Laminar.getHttpUrl(),
164
164
  projectApiKey: require_decorators.Laminar.getProjectApiKey()
165
165
  });
166
- if (config?.projectApiKey && config.projectApiKey !== require_decorators.Laminar.getProjectApiKey()) logger.warn("Laminar was already initialized with a different project API key. Ignoring the project API key from the evaluation config.");
166
+ if (config?.projectApiKey && config.projectApiKey !== require_decorators.Laminar.getProjectApiKey()) logger$1.warn("Laminar was already initialized with a different project API key. Ignoring the project API key from the evaluation config.");
167
167
  return;
168
168
  }
169
169
  const key = config?.projectApiKey ?? process.env.LMNR_PROJECT_API_KEY;
@@ -195,9 +195,9 @@ var Evaluation = class {
195
195
  if (!this.data.id) try {
196
196
  const datasets = await this.client.datasets.getDatasetByName(this.data.name);
197
197
  if (datasets.length > 0) this.data.id = datasets[0].id;
198
- else logger.warn(`Dataset ${this.data.name} not found`);
198
+ else logger$1.warn(`Dataset ${this.data.name} not found`);
199
199
  } catch (error) {
200
- logger.warn(`Error getting dataset ${this.data.name}: ${error instanceof Error ? error.message : String(error)}`);
200
+ logger$1.warn(`Error getting dataset ${this.data.name}: ${error instanceof Error ? error.message : String(error)}`);
201
201
  }
202
202
  }
203
203
  let resultDatapoints;
@@ -865,6 +865,559 @@ function wrapLanguageModel(languageModel) {
865
865
  return new LaminarLanguageModelV2(languageModel);
866
866
  }
867
867
  //#endregion
868
+ //#region src/opentelemetry-lib/instrumentation/mastra/types.ts
869
+ const MastraSpanType = {
870
+ MODEL_GENERATION: "model_generation",
871
+ MODEL_STEP: "model_step",
872
+ MODEL_CHUNK: "model_chunk",
873
+ TOOL_CALL: "tool_call",
874
+ MCP_TOOL_CALL: "mcp_tool_call"
875
+ };
876
+ //#endregion
877
+ //#region src/opentelemetry-lib/instrumentation/mastra/utils.ts
878
+ const dateToHrTime = (date) => {
879
+ const ms = date.getTime();
880
+ return [Math.floor(ms / 1e3), ms % 1e3 * 1e6];
881
+ };
882
+ const normalizeProvider = (provider) => provider.split(".").shift()?.toLowerCase().trim() || provider.toLowerCase().trim();
883
+ const mapLaminarSpanType = (spanType) => {
884
+ switch (spanType) {
885
+ case MastraSpanType.MODEL_STEP: return "LLM";
886
+ case MastraSpanType.TOOL_CALL:
887
+ case MastraSpanType.MCP_TOOL_CALL: return "TOOL";
888
+ default: return "DEFAULT";
889
+ }
890
+ };
891
+ const serializeJSON = (value) => {
892
+ if (typeof value === "string") return value;
893
+ try {
894
+ return JSON.stringify(value);
895
+ } catch {
896
+ return "[unserializable]";
897
+ }
898
+ };
899
+ const toAttributeValue = (value) => {
900
+ if (value === void 0 || value === null) return void 0;
901
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
902
+ if (Array.isArray(value)) {
903
+ if (value.every((v) => typeof v === "string")) return value;
904
+ if (value.every((v) => typeof v === "number")) return value;
905
+ if (value.every((v) => typeof v === "boolean")) return value;
906
+ }
907
+ return serializeJSON(value);
908
+ };
909
+ const extractMessages = (input) => {
910
+ if (!input) return void 0;
911
+ if (Array.isArray(input)) return input;
912
+ if (typeof input === "object") {
913
+ const asObj = input;
914
+ if (Array.isArray(asObj.messages)) return asObj.messages;
915
+ if (Array.isArray(asObj.prompt)) return asObj.prompt;
916
+ if (Array.isArray(asObj.input)) return asObj.input;
917
+ }
918
+ };
919
+ const normalizeInputMessages = (raw) => {
920
+ const out = [];
921
+ for (const m of raw) {
922
+ if (m == null) continue;
923
+ if (typeof m === "object") {
924
+ out.push(m);
925
+ continue;
926
+ }
927
+ out.push({
928
+ role: "user",
929
+ content: typeof m === "string" ? m : serializeJSON(m)
930
+ });
931
+ }
932
+ return out;
933
+ };
934
+ const buildAssistantMessageFromStepOutput = (output) => {
935
+ if (!output || typeof output !== "object") return null;
936
+ const outObj = output;
937
+ const parts = [];
938
+ if (typeof outObj.text === "string" && outObj.text.length > 0) parts.push({
939
+ type: "text",
940
+ text: outObj.text
941
+ });
942
+ if (Array.isArray(outObj.toolCalls)) for (const tc of outObj.toolCalls) {
943
+ if (!tc) continue;
944
+ parts.push({
945
+ type: "tool-call",
946
+ toolCallId: tc.toolCallId,
947
+ toolName: tc.toolName,
948
+ input: tc.input ?? tc.args
949
+ });
950
+ }
951
+ if (parts.length === 0) return null;
952
+ return {
953
+ role: "assistant",
954
+ content: parts
955
+ };
956
+ };
957
+ const extractToolCallId = (span) => {
958
+ const v = span.attributes?.toolCallId;
959
+ return typeof v === "string" ? v : void 0;
960
+ };
961
+ const cleanMastraToolName = (name) => name?.replace(/^(?:mcp_tool|tool):\s*'?(.*?)'?(?:\s+on\s+'[^']*')?$/, "$1");
962
+ const stringifyArgs = (raw) => {
963
+ if (raw === void 0) return void 0;
964
+ if (typeof raw === "string") return raw;
965
+ try {
966
+ return JSON.stringify(raw);
967
+ } catch {
968
+ return "[unserializable]";
969
+ }
970
+ };
971
+ const formatUsage = (usage) => {
972
+ if (!usage) return {};
973
+ const out = {};
974
+ if (typeof usage.inputTokens === "number") out[require_utils.LaminarAttributes.INPUT_TOKEN_COUNT] = usage.inputTokens;
975
+ if (typeof usage.outputTokens === "number") out[require_utils.LaminarAttributes.OUTPUT_TOKEN_COUNT] = usage.outputTokens;
976
+ const total = (usage.inputTokens ?? 0) + (usage.outputTokens ?? 0);
977
+ if (total > 0) out[require_utils.LaminarAttributes.TOTAL_TOKEN_COUNT] = total;
978
+ if (typeof usage.inputDetails?.cacheWrite === "number") out["gen_ai.usage.cache_creation_input_tokens"] = usage.inputDetails.cacheWrite;
979
+ if (typeof usage.inputDetails?.cacheRead === "number") out["gen_ai.usage.cache_read_input_tokens"] = usage.inputDetails.cacheRead;
980
+ if (typeof usage.outputDetails?.reasoning === "number") out["gen_ai.usage.reasoning_tokens"] = usage.outputDetails.reasoning;
981
+ return out;
982
+ };
983
+ //#endregion
984
+ //#region src/opentelemetry-lib/instrumentation/mastra/exporter.ts
985
+ const logger = require_utils.initializeLogger();
986
+ /**
987
+ * Bridges Mastra's ObservabilityExporter contract to Laminar's OTLP ingestion.
988
+ *
989
+ * Span-type mapping:
990
+ * - `model_step` → LLM (atomic LLM call; rendered with message history).
991
+ * - `tool_call`, `mcp_tool_call` → TOOL.
992
+ * - `model_chunk` → dropped (per-delta, noise).
993
+ * - everything else → DEFAULT.
994
+ *
995
+ * Tool-call reconstruction: Mastra's `extractStepInput` collapses prior tool
996
+ * calls/results into empty user messages on MODEL_STEP inputs, so we
997
+ * reconstruct the full conversation by accumulating (baseMessages → step0's
998
+ * assistant message → tool-result message(s) → step1's assistant message →
999
+ * …) using the parent MODEL_GENERATION's input and the children TOOL_CALL
1000
+ * spans paired by arrival order with each step's declared toolCalls.
1001
+ *
1002
+ * For LLM spans we emit `ai.prompt.messages` (stringified AI SDK messages
1003
+ * with tool-call / tool-result content parts) and `ai.response.text` +
1004
+ * `ai.response.toolCalls` so Laminar's backend parser threads them into
1005
+ * the LLM message history view.
1006
+ */
1007
+ var MastraExporter = class {
1008
+ constructor(options = {}) {
1009
+ this.name = "laminar";
1010
+ this.traceMap = /* @__PURE__ */ new Map();
1011
+ this.generationStateById = /* @__PURE__ */ new Map();
1012
+ this.generationAttrsById = /* @__PURE__ */ new Map();
1013
+ this.generationIdByStepId = /* @__PURE__ */ new Map();
1014
+ this.stepIndexBySpanId = /* @__PURE__ */ new Map();
1015
+ this.liveOtelSpanByMastraId = /* @__PURE__ */ new Map();
1016
+ this.warnedNotInitialized = false;
1017
+ this.config = {
1018
+ realtime: options.realtime ?? false,
1019
+ linkToActiveContext: options.linkToActiveContext ?? true
1020
+ };
1021
+ }
1022
+ init(_options) {}
1023
+ async exportTracingEvent(event) {
1024
+ const span = event.exportedSpan;
1025
+ if (span.isEvent) return;
1026
+ if (span.type === MastraSpanType.MODEL_GENERATION && span.attributes) {
1027
+ const existing = this.generationAttrsById.get(span.id);
1028
+ this.generationAttrsById.set(span.id, existing ? {
1029
+ ...existing,
1030
+ ...span.attributes
1031
+ } : { ...span.attributes });
1032
+ }
1033
+ if (event.type === "span_started") {
1034
+ this.handleSpanStarted(span);
1035
+ return;
1036
+ }
1037
+ if (event.type !== "span_ended") return;
1038
+ await this.handleSpanEnded(span);
1039
+ }
1040
+ async onTracingEvent(event) {
1041
+ await this.exportTracingEvent(event);
1042
+ }
1043
+ async flush() {
1044
+ const processor = require_decorators.getSpanProcessor();
1045
+ if (!processor) return;
1046
+ try {
1047
+ await processor.forceFlush();
1048
+ } catch (err) {
1049
+ logger.error(`[MastraExporter] forceFlush failed: ${err instanceof Error ? err.message : String(err)}`);
1050
+ }
1051
+ }
1052
+ shutdown() {
1053
+ this.traceMap.clear();
1054
+ this.generationStateById.clear();
1055
+ this.generationAttrsById.clear();
1056
+ this.generationIdByStepId.clear();
1057
+ this.stepIndexBySpanId.clear();
1058
+ this.liveOtelSpanByMastraId.clear();
1059
+ return Promise.resolve();
1060
+ }
1061
+ handleSpanStarted(span) {
1062
+ if (span.type === MastraSpanType.MODEL_CHUNK) return;
1063
+ const traceState = this.getOrCreateTraceState(span.traceId);
1064
+ const parentId = span.parentSpanId;
1065
+ this.recordSpanPath(span, traceState);
1066
+ traceState.activeSpanIds.add(span.id);
1067
+ if (span.type === MastraSpanType.MODEL_GENERATION) this.initGenerationState(span);
1068
+ else if (span.type === MastraSpanType.MODEL_STEP && parentId) {
1069
+ this.generationIdByStepId.set(span.id, parentId);
1070
+ const stepIndex = typeof span.attributes?.stepIndex === "number" ? span.attributes.stepIndex : 0;
1071
+ this.stepIndexBySpanId.set(span.id, stepIndex);
1072
+ }
1073
+ this.startOtelSpan(span, traceState);
1074
+ }
1075
+ recordSpanPath(span, traceState) {
1076
+ const parentId = span.parentSpanId;
1077
+ const parentPath = parentId ? traceState.spanPathById.get(parentId) : traceState.otelParentSpanPath;
1078
+ const parentIdsPath = parentId ? traceState.spanIdsPathById.get(parentId) : traceState.otelParentSpanIdsPath;
1079
+ const spanUuid = require_utils.otelSpanIdToUUID(require_utils.normalizeOtelSpanId(span.id));
1080
+ const spanPath = parentPath ? [...parentPath, span.name] : [span.name];
1081
+ const spanIdsPath = parentIdsPath ? [...parentIdsPath, spanUuid] : [spanUuid];
1082
+ traceState.spanPathById.set(span.id, spanPath);
1083
+ traceState.spanIdsPathById.set(span.id, spanIdsPath);
1084
+ }
1085
+ async handleSpanEnded(span) {
1086
+ if (span.type === MastraSpanType.MODEL_CHUNK) {
1087
+ this.captureReasoningChunk(span);
1088
+ return;
1089
+ }
1090
+ const traceState = this.getOrCreateTraceState(span.traceId);
1091
+ try {
1092
+ if (!traceState.spanPathById.has(span.id)) {
1093
+ this.recordSpanPath(span, traceState);
1094
+ traceState.activeSpanIds.add(span.id);
1095
+ }
1096
+ if (span.type === MastraSpanType.MODEL_GENERATION && !this.generationStateById.has(span.id)) this.initGenerationState(span);
1097
+ if (span.type === MastraSpanType.MODEL_STEP && !this.generationIdByStepId.has(span.id) && span.parentSpanId) this.generationIdByStepId.set(span.id, span.parentSpanId);
1098
+ if (!this.liveOtelSpanByMastraId.has(span.id)) this.startOtelSpan(span, traceState);
1099
+ this.updateGenerationStateOnSpanEnd(span);
1100
+ const otelSpan = this.liveOtelSpanByMastraId.get(span.id);
1101
+ if (!otelSpan) return;
1102
+ this.applyEndAttributes(span, otelSpan, traceState);
1103
+ if (span.errorInfo) {
1104
+ otelSpan.setStatus({
1105
+ code: _opentelemetry_api.SpanStatusCode.ERROR,
1106
+ message: span.errorInfo.message
1107
+ });
1108
+ otelSpan.recordException({
1109
+ name: span.errorInfo.name ?? "Error",
1110
+ message: span.errorInfo.message,
1111
+ stack: span.errorInfo.stack
1112
+ });
1113
+ }
1114
+ const endTime = span.endTime ? dateToHrTime(span.endTime) : dateToHrTime(span.startTime);
1115
+ otelSpan.end(endTime);
1116
+ if (this.config.realtime) await require_decorators.getSpanProcessor()?.forceFlush();
1117
+ } catch (err) {
1118
+ logger.error(`[MastraExporter] failed to export span ${span.id}: ${err instanceof Error ? err.message : String(err)}`);
1119
+ } finally {
1120
+ traceState.activeSpanIds.delete(span.id);
1121
+ if (traceState.activeSpanIds.size === 0) this.traceMap.delete(span.traceId);
1122
+ if (span.type === MastraSpanType.MODEL_GENERATION) {
1123
+ this.generationStateById.delete(span.id);
1124
+ this.generationAttrsById.delete(span.id);
1125
+ }
1126
+ if (span.type === MastraSpanType.MODEL_STEP) {
1127
+ this.generationIdByStepId.delete(span.id);
1128
+ this.stepIndexBySpanId.delete(span.id);
1129
+ }
1130
+ this.liveOtelSpanByMastraId.delete(span.id);
1131
+ }
1132
+ }
1133
+ initGenerationState(span) {
1134
+ const rawMessages = extractMessages(span.input);
1135
+ const baseMessages = rawMessages ? normalizeInputMessages(rawMessages) : [];
1136
+ this.generationStateById.set(span.id, {
1137
+ baseMessages,
1138
+ turnsByStepIndex: /* @__PURE__ */ new Map(),
1139
+ toolCallChildrenByStepIndex: /* @__PURE__ */ new Map(),
1140
+ reasoningTextByStepIndex: /* @__PURE__ */ new Map()
1141
+ });
1142
+ }
1143
+ captureReasoningChunk(span) {
1144
+ if ((span.attributes ?? {}).chunkType !== "reasoning") return;
1145
+ const output = span.output;
1146
+ if (!output || typeof output !== "object") return;
1147
+ const text = output.text;
1148
+ if (typeof text !== "string" || text.length === 0) return;
1149
+ const stepSpanId = span.parentSpanId;
1150
+ if (!stepSpanId) return;
1151
+ const generationId = this.generationIdByStepId.get(stepSpanId);
1152
+ if (!generationId) return;
1153
+ const gen = this.generationStateById.get(generationId);
1154
+ if (!gen) return;
1155
+ const stepIndex = this.stepIndexBySpanId.get(stepSpanId);
1156
+ if (stepIndex === void 0) return;
1157
+ const existing = gen.reasoningTextByStepIndex.get(stepIndex);
1158
+ gen.reasoningTextByStepIndex.set(stepIndex, existing ? existing + text : text);
1159
+ }
1160
+ updateGenerationStateOnSpanEnd(span) {
1161
+ if (span.type === MastraSpanType.MODEL_STEP) {
1162
+ const generationId = this.generationIdByStepId.get(span.id);
1163
+ if (!generationId) return;
1164
+ const gen = this.generationStateById.get(generationId);
1165
+ if (!gen) return;
1166
+ const stepIndex = this.stepIndexBySpanId.get(span.id) ?? (typeof span.attributes?.stepIndex === "number" ? span.attributes.stepIndex : 0);
1167
+ this.stepIndexBySpanId.set(span.id, stepIndex);
1168
+ const output = span.output;
1169
+ const declaredToolCalls = Array.isArray(output?.toolCalls) ? output.toolCalls : [];
1170
+ const children = gen.toolCallChildrenByStepIndex.get(stepIndex) ?? [];
1171
+ const childrenById = /* @__PURE__ */ new Map();
1172
+ for (const c of children) if (c.toolCallId) childrenById.set(c.toolCallId, c);
1173
+ const consumed = /* @__PURE__ */ new Set();
1174
+ const turnMessages = [];
1175
+ const assistantMsg = buildAssistantMessageFromStepOutput(output);
1176
+ if (assistantMsg) turnMessages.push(assistantMsg);
1177
+ for (const dec of declaredToolCalls) {
1178
+ const toolCallId = dec?.toolCallId ?? dec?.id;
1179
+ let child = toolCallId ? childrenById.get(toolCallId) : void 0;
1180
+ if (child && consumed.has(child)) child = void 0;
1181
+ if (!child) child = children.find((c) => !consumed.has(c));
1182
+ if (!child) continue;
1183
+ consumed.add(child);
1184
+ const resolvedToolCallId = toolCallId ?? child.toolCallId;
1185
+ const toolName = dec?.toolName ?? dec?.name ?? child.toolName;
1186
+ turnMessages.push({
1187
+ role: "tool",
1188
+ tool_call_id: resolvedToolCallId,
1189
+ content: [{
1190
+ type: "tool-result",
1191
+ toolCallId: resolvedToolCallId,
1192
+ toolName,
1193
+ output: child.output
1194
+ }]
1195
+ });
1196
+ }
1197
+ gen.turnsByStepIndex.set(stepIndex, turnMessages);
1198
+ } else if (span.type === MastraSpanType.TOOL_CALL || span.type === MastraSpanType.MCP_TOOL_CALL) {
1199
+ const parentId = span.parentSpanId;
1200
+ if (!parentId) return;
1201
+ const generationId = this.generationIdByStepId.get(parentId);
1202
+ if (!generationId) return;
1203
+ const gen = this.generationStateById.get(generationId);
1204
+ if (!gen) return;
1205
+ const stepIndex = this.stepIndexBySpanId.get(parentId);
1206
+ if (stepIndex === void 0) return;
1207
+ const list = gen.toolCallChildrenByStepIndex.get(stepIndex) ?? [];
1208
+ list.push({
1209
+ toolCallId: extractToolCallId(span),
1210
+ toolName: cleanMastraToolName(span.name) || "tool",
1211
+ input: span.input,
1212
+ output: span.output
1213
+ });
1214
+ gen.toolCallChildrenByStepIndex.set(stepIndex, list);
1215
+ }
1216
+ }
1217
+ getOrCreateTraceState(traceId) {
1218
+ const existing = this.traceMap.get(traceId);
1219
+ if (existing) return existing;
1220
+ const created = {
1221
+ spanPathById: /* @__PURE__ */ new Map(),
1222
+ spanIdsPathById: /* @__PURE__ */ new Map(),
1223
+ activeSpanIds: /* @__PURE__ */ new Set()
1224
+ };
1225
+ if (this.config?.linkToActiveContext) {
1226
+ const activeSpan = _opentelemetry_api.trace.getSpan(require_decorators.LaminarContextManager.getContext()) ?? _opentelemetry_api.trace.getActiveSpan();
1227
+ const ctx = activeSpan?.spanContext();
1228
+ if (ctx && ctx.traceId && ctx.spanId) {
1229
+ created.otelTraceId = require_utils.normalizeOtelTraceId(ctx.traceId);
1230
+ created.otelRootParentSpanId = require_utils.normalizeOtelSpanId(ctx.spanId);
1231
+ const attrs = activeSpan.attributes;
1232
+ if (attrs) {
1233
+ const parentPath = attrs[require_utils.SPAN_PATH];
1234
+ if (Array.isArray(parentPath) && parentPath.every((p) => typeof p === "string")) created.otelParentSpanPath = parentPath;
1235
+ const parentIdsPath = attrs[require_utils.SPAN_IDS_PATH];
1236
+ if (Array.isArray(parentIdsPath) && parentIdsPath.every((p) => typeof p === "string")) created.otelParentSpanIdsPath = parentIdsPath;
1237
+ }
1238
+ }
1239
+ }
1240
+ this.traceMap.set(traceId, created);
1241
+ return created;
1242
+ }
1243
+ startOtelSpan(span, traceState) {
1244
+ const tracer = require_decorators.getTracerProvider().getTracer("@lmnr-ai/lmnr", require_dist.version);
1245
+ const parentCtx = this.buildParentContext(span, traceState);
1246
+ const otelSpan = tracer.startSpan(span.name, {
1247
+ startTime: dateToHrTime(span.startTime),
1248
+ kind: _opentelemetry_api.SpanKind.INTERNAL
1249
+ }, parentCtx);
1250
+ if (!otelSpan.isRecording()) {
1251
+ this.warnNotInitializedOnce();
1252
+ return;
1253
+ }
1254
+ const sdkAllocatedSpanId = otelSpan.spanContext().spanId;
1255
+ const mastraSpanId = require_utils.normalizeOtelSpanId(span.id);
1256
+ const mastraTraceId = traceState.otelTraceId ?? require_utils.normalizeOtelTraceId(span.traceId);
1257
+ Object.assign(otelSpan.spanContext(), {
1258
+ traceId: mastraTraceId,
1259
+ spanId: mastraSpanId
1260
+ });
1261
+ if (sdkAllocatedSpanId !== mastraSpanId) require_decorators.getSpanProcessor()?.dropPathInfo(sdkAllocatedSpanId);
1262
+ const parentSpanIdNormalized = span.parentSpanId ? require_utils.normalizeOtelSpanId(span.parentSpanId) : traceState.otelRootParentSpanId;
1263
+ if (parentSpanIdNormalized) {
1264
+ const parentSpanContext = {
1265
+ traceId: mastraTraceId,
1266
+ spanId: parentSpanIdNormalized,
1267
+ traceFlags: _opentelemetry_api.TraceFlags.SAMPLED,
1268
+ isRemote: false
1269
+ };
1270
+ Object.assign(otelSpan, {
1271
+ parentSpanId: parentSpanIdNormalized,
1272
+ parentSpanContext
1273
+ });
1274
+ } else Object.assign(otelSpan, {
1275
+ parentSpanId: void 0,
1276
+ parentSpanContext: void 0
1277
+ });
1278
+ const spanPath = traceState.spanPathById.get(span.id);
1279
+ const spanIdsPath = traceState.spanIdsPathById.get(span.id);
1280
+ if (spanPath) otelSpan.setAttribute(require_utils.SPAN_PATH, spanPath);
1281
+ if (spanIdsPath) otelSpan.setAttribute(require_utils.SPAN_IDS_PATH, spanIdsPath);
1282
+ this.liveOtelSpanByMastraId.set(span.id, otelSpan);
1283
+ }
1284
+ warnNotInitializedOnce() {
1285
+ if (this.warnedNotInitialized) return;
1286
+ this.warnedNotInitialized = true;
1287
+ logger.warn("[MastraExporter] Laminar tracer is not initialized — Mastra spans will be dropped. Call `Laminar.initialize({ projectApiKey: ... })` before constructing `MastraExporter`.");
1288
+ }
1289
+ buildParentContext(span, traceState) {
1290
+ const baseCtx = require_decorators.LaminarContextManager.getContext() ?? _opentelemetry_api.context.active();
1291
+ if (span.parentSpanId) {
1292
+ const parentLive = this.liveOtelSpanByMastraId.get(span.parentSpanId);
1293
+ if (parentLive) return _opentelemetry_api.trace.setSpan(baseCtx, parentLive);
1294
+ }
1295
+ if (traceState.otelTraceId && traceState.otelRootParentSpanId) {
1296
+ const wrapped = _opentelemetry_api.trace.wrapSpanContext({
1297
+ traceId: traceState.otelTraceId,
1298
+ spanId: traceState.otelRootParentSpanId,
1299
+ traceFlags: _opentelemetry_api.TraceFlags.SAMPLED,
1300
+ isRemote: false
1301
+ });
1302
+ return _opentelemetry_api.trace.setSpan(baseCtx, wrapped);
1303
+ }
1304
+ return baseCtx;
1305
+ }
1306
+ applyEndAttributes(span, otelSpan, traceState) {
1307
+ const attributes = this.buildLaminarAttributes(span, traceState);
1308
+ delete attributes[require_utils.SPAN_PATH];
1309
+ delete attributes[require_utils.SPAN_IDS_PATH];
1310
+ otelSpan.setAttributes(attributes);
1311
+ }
1312
+ buildLaminarAttributes(span, traceState) {
1313
+ const attributes = {};
1314
+ const spanPath = traceState.spanPathById.get(span.id);
1315
+ const spanIdsPath = traceState.spanIdsPathById.get(span.id);
1316
+ if (spanPath) attributes[require_utils.SPAN_PATH] = spanPath;
1317
+ if (spanIdsPath) attributes[require_utils.SPAN_IDS_PATH] = spanIdsPath;
1318
+ const laminarSpanType = mapLaminarSpanType(span.type);
1319
+ attributes[require_utils.SPAN_TYPE] = laminarSpanType;
1320
+ attributes[require_utils.SPAN_INSTRUMENTATION_SOURCE] = "javascript";
1321
+ attributes[require_utils.SPAN_SDK_VERSION] = require_dist.version;
1322
+ const langVersion = require_decorators.getLangVersion();
1323
+ if (langVersion) attributes[require_utils.SPAN_LANGUAGE_VERSION] = langVersion;
1324
+ attributes["lmnr.internal.mastra.span_type"] = span.type;
1325
+ const metadata = span.metadata ?? {};
1326
+ const sessionId = metadata.sessionId ?? metadata.session_id;
1327
+ if (typeof sessionId === "string" && sessionId.length > 0) attributes[require_utils.SESSION_ID] = sessionId;
1328
+ const userId = metadata.userId ?? metadata.user_id;
1329
+ if (typeof userId === "string" && userId.length > 0) attributes[require_utils.USER_ID] = userId;
1330
+ for (const [key, value] of Object.entries(metadata)) {
1331
+ if (key === "sessionId" || key === "session_id" || key === "userId" || key === "user_id" || value === void 0 || value === null) continue;
1332
+ const av = toAttributeValue(value);
1333
+ if (av !== void 0) attributes[`${require_utils.ASSOCIATION_PROPERTIES}.metadata.${key}`] = av;
1334
+ }
1335
+ if (span.isRootSpan && span.tags?.length) attributes[`${require_utils.ASSOCIATION_PROPERTIES}.tags`] = span.tags;
1336
+ if (laminarSpanType === "LLM") this.applyLlmAttributes(span, attributes);
1337
+ else if (laminarSpanType === "TOOL") this.applyToolAttributes(span, attributes);
1338
+ else {
1339
+ if (span.input !== void 0) attributes[require_utils.SPAN_INPUT] = serializeJSON(span.input);
1340
+ if (span.output !== void 0) attributes[require_utils.SPAN_OUTPUT] = serializeJSON(span.output);
1341
+ }
1342
+ return attributes;
1343
+ }
1344
+ applyLlmAttributes(span, attributes) {
1345
+ const generationId = this.generationIdByStepId.get(span.id) ?? span.parentSpanId;
1346
+ const gen = generationId ? this.generationStateById.get(generationId) : void 0;
1347
+ const generationAttrs = generationId ? this.generationAttrsById.get(generationId) : void 0;
1348
+ const stepAttrs = span.attributes ?? {};
1349
+ const stepIndex = this.stepIndexBySpanId.get(span.id) ?? (typeof stepAttrs.stepIndex === "number" ? stepAttrs.stepIndex : 0);
1350
+ const provider = generationAttrs?.provider;
1351
+ const model = generationAttrs?.model;
1352
+ const responseModel = generationAttrs?.responseModel;
1353
+ if (provider) attributes[require_utils.LaminarAttributes.PROVIDER] = normalizeProvider(provider);
1354
+ if (model) attributes[require_utils.LaminarAttributes.REQUEST_MODEL] = model;
1355
+ if (responseModel) attributes[require_utils.LaminarAttributes.RESPONSE_MODEL] = responseModel;
1356
+ else if (model) attributes[require_utils.LaminarAttributes.RESPONSE_MODEL] = model;
1357
+ const usage = stepAttrs.usage ?? generationAttrs?.usage;
1358
+ Object.assign(attributes, formatUsage(usage));
1359
+ if (typeof stepAttrs.finishReason === "string") {
1360
+ attributes["gen_ai.response.finish_reason"] = stepAttrs.finishReason;
1361
+ attributes["ai.response.finishReason"] = stepAttrs.finishReason;
1362
+ }
1363
+ const messages = gen ? [...gen.baseMessages] : [];
1364
+ if (gen) for (let i = 0; i < stepIndex; i++) {
1365
+ const turn = gen.turnsByStepIndex.get(i);
1366
+ if (turn) messages.push(...turn);
1367
+ }
1368
+ if (messages.length > 0) attributes["ai.prompt.messages"] = serializeJSON(messages);
1369
+ else if (span.input !== void 0) attributes[require_utils.SPAN_INPUT] = serializeJSON(span.input);
1370
+ const out = span.output;
1371
+ const reasoningText = gen?.reasoningTextByStepIndex.get(stepIndex);
1372
+ if (out && typeof out === "object") {
1373
+ const outObj = out;
1374
+ if (typeof outObj.text === "string" && outObj.text.length > 0) attributes["ai.response.text"] = outObj.text;
1375
+ const normalizedToolCalls = Array.isArray(outObj.toolCalls) && outObj.toolCalls.length > 0 ? outObj.toolCalls.map((tc) => ({
1376
+ toolCallType: tc.toolCallType ?? "function",
1377
+ toolCallId: tc.toolCallId,
1378
+ toolName: tc.toolName,
1379
+ args: stringifyArgs(tc.args ?? tc.input)
1380
+ })) : [];
1381
+ if (normalizedToolCalls.length > 0) attributes["ai.response.toolCalls"] = JSON.stringify(normalizedToolCalls);
1382
+ if (typeof reasoningText === "string" && reasoningText.length > 0) {
1383
+ const parts = [{
1384
+ type: "thinking",
1385
+ content: reasoningText
1386
+ }];
1387
+ if (typeof outObj.text === "string" && outObj.text.length > 0) parts.push({
1388
+ type: "text",
1389
+ content: outObj.text
1390
+ });
1391
+ for (const tc of normalizedToolCalls) {
1392
+ let argsObj = tc.args;
1393
+ if (typeof argsObj === "string") try {
1394
+ argsObj = JSON.parse(argsObj);
1395
+ } catch {}
1396
+ parts.push({
1397
+ type: "tool_call",
1398
+ id: tc.toolCallId,
1399
+ name: tc.toolName,
1400
+ arguments: argsObj
1401
+ });
1402
+ }
1403
+ attributes["gen_ai.output.messages"] = JSON.stringify([{
1404
+ role: "assistant",
1405
+ parts
1406
+ }]);
1407
+ }
1408
+ } else if (out !== void 0) attributes[require_utils.SPAN_OUTPUT] = serializeJSON(out);
1409
+ }
1410
+ applyToolAttributes(span, attributes) {
1411
+ if (span.input !== void 0) attributes[require_utils.SPAN_INPUT] = serializeJSON(span.input);
1412
+ if (span.output !== void 0) attributes[require_utils.SPAN_OUTPUT] = serializeJSON(span.output);
1413
+ const toolAttrs = span.attributes ?? {};
1414
+ const cleanedName = cleanMastraToolName(span.name) || span.name;
1415
+ if (cleanedName) attributes["ai.toolCall.name"] = cleanedName;
1416
+ if (typeof toolAttrs.toolType === "string") attributes["ai.toolCall.type"] = toolAttrs.toolType;
1417
+ if (typeof toolAttrs.mcpServer === "string") attributes["mcp.server"] = toolAttrs.mcpServer;
1418
+ }
1419
+ };
1420
+ //#endregion
868
1421
  exports.Dataset = EvaluationDataset;
869
1422
  exports.HumanEvaluator = HumanEvaluator;
870
1423
  exports.Laminar = require_decorators.Laminar;
@@ -872,6 +1425,7 @@ exports.LaminarAttributes = require_utils.LaminarAttributes;
872
1425
  exports.LaminarClient = require_dist.LaminarClient;
873
1426
  exports.LaminarDataset = LaminarDataset;
874
1427
  exports.LaminarSpanProcessor = require_decorators.LaminarSpanProcessor;
1428
+ exports.MastraExporter = MastraExporter;
875
1429
  exports.TracingLevel = require_decorators.TracingLevel;
876
1430
  exports.evaluate = evaluate;
877
1431
  exports.getTracer = require_decorators.getTracer;