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