@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.
- package/dist/cli/worker/build.cjs +1 -1
- package/dist/cli/worker/build.mjs +1 -1
- package/dist/cli/worker/index.cjs +1 -1
- package/dist/cli/worker/index.mjs +1 -1
- package/dist/cli.cjs +3 -3
- package/dist/cli.d.cts +1 -1
- package/dist/cli.d.mts +1 -1
- package/dist/cli.mjs +3 -3
- package/dist/{decorators-Cf0xs-v_.mjs → decorators-BDFgXfgI.mjs} +820 -139
- package/dist/decorators-BDFgXfgI.mjs.map +1 -0
- package/dist/{decorators-u7xFb_9W.cjs → decorators-CaTC0lJq.cjs} +831 -138
- package/dist/decorators-CaTC0lJq.cjs.map +1 -0
- package/dist/{dist-BwllJ__m.mjs → dist-B5CmOQ4s.mjs} +3 -3
- package/dist/{dist-BwllJ__m.mjs.map → dist-B5CmOQ4s.mjs.map} +1 -1
- package/dist/{dist-zDtQ_gVq.cjs → dist-B5cKzRzq.cjs} +3 -3
- package/dist/{dist-zDtQ_gVq.cjs.map → dist-B5cKzRzq.cjs.map} +1 -1
- package/dist/{evaluations-D2duRqbG.d.cts → evaluations-BJkIwpmH.d.cts} +15 -1
- package/dist/{evaluations-DEbBIXng.d.mts → evaluations-tqpNgp0P.d.mts} +15 -1
- package/dist/{file-utils-C-auksVC.mjs → file-utils-CHoR9VB8.mjs} +2 -2
- package/dist/{file-utils-C-auksVC.mjs.map → file-utils-CHoR9VB8.mjs.map} +1 -1
- package/dist/file-utils-IBEBwZxq.cjs +2 -0
- package/dist/{file-utils-0mVr0eIv.cjs → file-utils-yJ5ephze.cjs} +2 -2
- package/dist/{file-utils-0mVr0eIv.cjs.map → file-utils-yJ5ephze.cjs.map} +1 -1
- package/dist/index.cjs +563 -9
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +125 -3
- package/dist/index.d.mts +125 -3
- package/dist/index.mjs +564 -11
- package/dist/index.mjs.map +1 -1
- package/dist/{utils-BmWNkeX5.cjs → utils-C8Tl1vKD.cjs} +23 -1
- package/dist/utils-C8Tl1vKD.cjs.map +1 -0
- package/dist/{utils-0WC6BVqs.mjs → utils-CHJ0KZUR.mjs} +12 -2
- package/dist/utils-CHJ0KZUR.mjs.map +1 -0
- package/package.json +4 -3
- package/dist/decorators-Cf0xs-v_.mjs.map +0 -1
- package/dist/decorators-u7xFb_9W.cjs.map +0 -1
- package/dist/file-utils-2hY2DP65.cjs +0 -2
- package/dist/utils-0WC6BVqs.mjs.map +0 -1
- 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-
|
|
4
|
-
const require_utils = require("./utils-
|
|
5
|
-
const require_decorators = require("./decorators-
|
|
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-
|
|
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;
|