@introspection-sdk/introspection-node 0.4.3 → 0.4.4
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/.turbo/turbo-build.log +1 -1
- package/dist/otel/anthropic.d.ts +14 -12
- package/dist/otel/anthropic.d.ts.map +1 -1
- package/dist/otel/anthropic.js +89 -47
- package/dist/otel/anthropic.js.map +1 -1
- package/dist/otel/gemini.d.ts +15 -7
- package/dist/otel/gemini.d.ts.map +1 -1
- package/dist/otel/gemini.js +88 -37
- package/dist/otel/gemini.js.map +1 -1
- package/dist/otel/index.d.ts +5 -0
- package/dist/otel/index.d.ts.map +1 -1
- package/dist/otel/index.js +8 -0
- package/dist/otel/index.js.map +1 -1
- package/dist/otel/init.d.ts +126 -0
- package/dist/otel/init.d.ts.map +1 -0
- package/dist/otel/init.js +259 -0
- package/dist/otel/init.js.map +1 -0
- package/dist/otel/integrations/anthropic.d.ts +10 -0
- package/dist/otel/integrations/anthropic.d.ts.map +1 -0
- package/dist/otel/integrations/anthropic.js +22 -0
- package/dist/otel/integrations/anthropic.js.map +1 -0
- package/dist/otel/integrations/base.d.ts +83 -0
- package/dist/otel/integrations/base.d.ts.map +1 -0
- package/dist/otel/integrations/base.js +23 -0
- package/dist/otel/integrations/base.js.map +1 -0
- package/dist/otel/integrations/claude-agent.d.ts +13 -0
- package/dist/otel/integrations/claude-agent.d.ts.map +1 -0
- package/dist/otel/integrations/claude-agent.js +27 -0
- package/dist/otel/integrations/claude-agent.js.map +1 -0
- package/dist/otel/integrations/gemini.d.ts +11 -0
- package/dist/otel/integrations/gemini.d.ts.map +1 -0
- package/dist/otel/integrations/gemini.js +21 -0
- package/dist/otel/integrations/gemini.js.map +1 -0
- package/dist/otel/integrations/index.d.ts +39 -0
- package/dist/otel/integrations/index.d.ts.map +1 -0
- package/dist/otel/integrations/index.js +131 -0
- package/dist/otel/integrations/index.js.map +1 -0
- package/dist/otel/integrations/langchain.d.ts +16 -0
- package/dist/otel/integrations/langchain.d.ts.map +1 -0
- package/dist/otel/integrations/langchain.js +41 -0
- package/dist/otel/integrations/langchain.js.map +1 -0
- package/dist/otel/integrations/mastra.d.ts +14 -0
- package/dist/otel/integrations/mastra.d.ts.map +1 -0
- package/dist/otel/integrations/mastra.js +27 -0
- package/dist/otel/integrations/mastra.js.map +1 -0
- package/dist/otel/integrations/openai-agents.d.ts +11 -0
- package/dist/otel/integrations/openai-agents.d.ts.map +1 -0
- package/dist/otel/integrations/openai-agents.js +25 -0
- package/dist/otel/integrations/openai-agents.js.map +1 -0
- package/dist/otel/integrations/pi.d.ts +12 -0
- package/dist/otel/integrations/pi.d.ts.map +1 -0
- package/dist/otel/integrations/pi.js +20 -0
- package/dist/otel/integrations/pi.js.map +1 -0
- package/dist/otel/integrations/vercel.d.ts +15 -0
- package/dist/otel/integrations/vercel.d.ts.map +1 -0
- package/dist/otel/integrations/vercel.js +20 -0
- package/dist/otel/integrations/vercel.js.map +1 -0
- package/dist/otel/register.d.ts +8 -0
- package/dist/otel/register.d.ts.map +1 -0
- package/dist/otel/register.js +48 -0
- package/dist/otel/register.js.map +1 -0
- package/dist/otel/setup.d.ts +29 -0
- package/dist/otel/setup.d.ts.map +1 -1
- package/dist/otel/setup.js +22 -3
- package/dist/otel/setup.js.map +1 -1
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +12 -4
- package/dist/utils.js.map +1 -1
- package/package.json +17 -5
- package/src/otel/anthropic.ts +128 -63
- package/src/otel/gemini.ts +139 -45
- package/src/otel/index.ts +41 -0
- package/src/otel/init.ts +411 -0
- package/src/otel/integrations/anthropic.ts +26 -0
- package/src/otel/integrations/base.ts +95 -0
- package/src/otel/integrations/claude-agent.ts +32 -0
- package/src/otel/integrations/gemini.ts +25 -0
- package/src/otel/integrations/index.ts +151 -0
- package/src/otel/integrations/langchain.ts +45 -0
- package/src/otel/integrations/mastra.ts +30 -0
- package/src/otel/integrations/openai-agents.ts +31 -0
- package/src/otel/integrations/pi.ts +24 -0
- package/src/otel/integrations/vercel.ts +24 -0
- package/src/otel/register.ts +50 -0
- package/src/otel/setup.ts +44 -3
- package/src/utils.ts +12 -5
- package/.turbo/turbo-check-types.log +0 -4
- package/.turbo/turbo-lint.log +0 -5
- package/dist/ai-sdk-integration.d.ts +0 -123
- package/dist/ai-sdk-integration.d.ts.map +0 -1
- package/dist/ai-sdk-integration.js +0 -475
- package/dist/anthropic.d.ts +0 -54
- package/dist/anthropic.d.ts.map +0 -1
- package/dist/anthropic.js +0 -375
- package/dist/claude-hooks.d.ts +0 -328
- package/dist/claude-hooks.d.ts.map +0 -1
- package/dist/claude-hooks.js +0 -571
- package/dist/claude-wrapper.d.ts +0 -93
- package/dist/claude-wrapper.d.ts.map +0 -1
- package/dist/claude-wrapper.js +0 -154
- package/dist/converters/ai-sdk.d.ts +0 -65
- package/dist/converters/ai-sdk.d.ts.map +0 -1
- package/dist/converters/ai-sdk.js +0 -261
- package/dist/converters/openclaw.d.ts +0 -16
- package/dist/converters/openclaw.d.ts.map +0 -1
- package/dist/converters/openclaw.js +0 -49
- package/dist/langchain-handler.d.ts +0 -103
- package/dist/langchain-handler.d.ts.map +0 -1
- package/dist/langchain-handler.js +0 -518
- package/dist/mastra-exporter.d.ts +0 -85
- package/dist/mastra-exporter.d.ts.map +0 -1
- package/dist/mastra-exporter.js +0 -471
- package/dist/span-processor.d.ts +0 -62
- package/dist/span-processor.d.ts.map +0 -1
- package/dist/span-processor.js +0 -313
- package/dist/tracing-processor.d.ts +0 -136
- package/dist/tracing-processor.d.ts.map +0 -1
- package/dist/tracing-processor.js +0 -368
- package/dist/types/genai.d.ts +0 -165
- package/dist/types/genai.d.ts.map +0 -1
- package/dist/types/genai.js +0 -82
|
@@ -1,518 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LangChain/LangGraph Callback Handler for Introspection.
|
|
3
|
-
*
|
|
4
|
-
* Hooks into LangChain's native callback system to capture LLM, tool, and
|
|
5
|
-
* chain interactions as gen_ai.* OTel spans for the Introspection backend.
|
|
6
|
-
*
|
|
7
|
-
* Similar to IntrospectionTracingProcessor for OpenAI Agents SDK and
|
|
8
|
-
* IntrospectionClaudeHooks for Claude Agent SDK.
|
|
9
|
-
*
|
|
10
|
-
* **Important:** This module requires `@langchain/core` at runtime.
|
|
11
|
-
* Import via the subpath: `@introspection-sdk/introspection-node/langchain`
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```ts
|
|
15
|
-
* import { IntrospectionCallbackHandler } from "@introspection-sdk/introspection-node/langchain";
|
|
16
|
-
* import { ChatOpenAI } from "@langchain/openai";
|
|
17
|
-
*
|
|
18
|
-
* const handler = new IntrospectionCallbackHandler({
|
|
19
|
-
* serviceName: "my-langchain-app",
|
|
20
|
-
* });
|
|
21
|
-
*
|
|
22
|
-
* const model = new ChatOpenAI({ modelName: "gpt-4o" });
|
|
23
|
-
* const response = await model.invoke("Hello!", { callbacks: [handler] });
|
|
24
|
-
*
|
|
25
|
-
* await handler.shutdown();
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
28
|
-
import { BaseCallbackHandler } from "@langchain/core/callbacks/base";
|
|
29
|
-
import { context as otelContext, ROOT_CONTEXT, SpanStatusCode, trace as otelTrace, } from "@opentelemetry/api";
|
|
30
|
-
import { BasicTracerProvider, BatchSpanProcessor, SimpleSpanProcessor, } from "@opentelemetry/sdk-trace-base";
|
|
31
|
-
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
|
|
32
|
-
import { resourceFromAttributes } from "@opentelemetry/resources";
|
|
33
|
-
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
|
|
34
|
-
import { randomUUID } from "crypto";
|
|
35
|
-
import { logger, withOtlpHttpsProxy } from "./utils.js";
|
|
36
|
-
import { VERSION } from "./version.js";
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// Handler
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
/**
|
|
41
|
-
* LangChain callback handler that captures LLM, tool, and chain events
|
|
42
|
-
* as gen_ai.* OTel spans and exports them to the Introspection backend.
|
|
43
|
-
*
|
|
44
|
-
* Pass as a callback to any LangChain invoke call, or set globally via
|
|
45
|
-
* `setGlobalHandler()`.
|
|
46
|
-
*/
|
|
47
|
-
export class IntrospectionCallbackHandler extends BaseCallbackHandler {
|
|
48
|
-
name = "IntrospectionCallbackHandler";
|
|
49
|
-
_tracerProvider;
|
|
50
|
-
_tracer;
|
|
51
|
-
_spans = new Map();
|
|
52
|
-
_conversationId;
|
|
53
|
-
// Track span names and parents so children can resolve gen_ai.agent.name
|
|
54
|
-
_spanNames = new Map();
|
|
55
|
-
_spanParents = new Map();
|
|
56
|
-
_runRoots = new Map();
|
|
57
|
-
// LangChain wrapper names to skip when resolving agent names
|
|
58
|
-
static _wrapperNames = new Set([
|
|
59
|
-
"RunnableSequence",
|
|
60
|
-
"RunnableParallel",
|
|
61
|
-
"RunnableMap",
|
|
62
|
-
"RunnableLambda",
|
|
63
|
-
"RunnableRetry",
|
|
64
|
-
"_ConfigurableModel",
|
|
65
|
-
"ChatOpenAI",
|
|
66
|
-
"ChatAnthropic",
|
|
67
|
-
"ChatGoogleGenerativeAI",
|
|
68
|
-
"ChatGroq",
|
|
69
|
-
]);
|
|
70
|
-
// Store LLM input messages per runId for use in handleLLMEnd
|
|
71
|
-
_llmInputs = new Map();
|
|
72
|
-
constructor(options = {}) {
|
|
73
|
-
super();
|
|
74
|
-
const advanced = options.advanced;
|
|
75
|
-
const serviceName = options.serviceName || process.env.INTROSPECTION_SERVICE_NAME;
|
|
76
|
-
const resource = serviceName
|
|
77
|
-
? resourceFromAttributes({ [ATTR_SERVICE_NAME]: serviceName })
|
|
78
|
-
: undefined;
|
|
79
|
-
if (!advanced?.spanExporter) {
|
|
80
|
-
const token = options.token || process.env.INTROSPECTION_TOKEN;
|
|
81
|
-
if (!token) {
|
|
82
|
-
throw new Error("INTROSPECTION_TOKEN is required");
|
|
83
|
-
}
|
|
84
|
-
const baseUrl = options.baseUrl ||
|
|
85
|
-
process.env.INTROSPECTION_BASE_URL ||
|
|
86
|
-
"https://otel.introspection.dev";
|
|
87
|
-
const endpoint = baseUrl.endsWith("/v1/traces")
|
|
88
|
-
? baseUrl
|
|
89
|
-
: `${baseUrl.replace(/\/$/, "")}/v1/traces`;
|
|
90
|
-
const headers = {
|
|
91
|
-
"User-Agent": `introspection-sdk/${VERSION}`,
|
|
92
|
-
Authorization: `Bearer ${token}`,
|
|
93
|
-
...options.additionalHeaders,
|
|
94
|
-
};
|
|
95
|
-
logger.info(`IntrospectionCallbackHandler initialized: endpoint=${endpoint}`);
|
|
96
|
-
const spanExporter = new OTLPTraceExporter(withOtlpHttpsProxy({ url: endpoint, headers }));
|
|
97
|
-
const effectiveBatchSize = token.startsWith("intro_dev") || token.startsWith("intro_staging")
|
|
98
|
-
? 1
|
|
99
|
-
: undefined;
|
|
100
|
-
const useSimple = advanced?.useSimpleSpanProcessor || effectiveBatchSize === 1;
|
|
101
|
-
const spanProcessor = useSimple
|
|
102
|
-
? new SimpleSpanProcessor(spanExporter)
|
|
103
|
-
: new BatchSpanProcessor(spanExporter, {
|
|
104
|
-
scheduledDelayMillis: 1000,
|
|
105
|
-
...(effectiveBatchSize
|
|
106
|
-
? { maxExportBatchSize: effectiveBatchSize }
|
|
107
|
-
: {}),
|
|
108
|
-
});
|
|
109
|
-
this._tracerProvider = new BasicTracerProvider({
|
|
110
|
-
resource,
|
|
111
|
-
idGenerator: advanced?.idGenerator,
|
|
112
|
-
spanProcessors: [spanProcessor],
|
|
113
|
-
});
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
logger.info("IntrospectionCallbackHandler initialized in test mode with custom exporter");
|
|
117
|
-
const spanProcessor = advanced.useSimpleSpanProcessor
|
|
118
|
-
? new SimpleSpanProcessor(advanced.spanExporter)
|
|
119
|
-
: new BatchSpanProcessor(advanced.spanExporter, {
|
|
120
|
-
scheduledDelayMillis: 100,
|
|
121
|
-
});
|
|
122
|
-
this._tracerProvider = new BasicTracerProvider({
|
|
123
|
-
resource,
|
|
124
|
-
idGenerator: advanced.idGenerator,
|
|
125
|
-
spanProcessors: [spanProcessor],
|
|
126
|
-
});
|
|
127
|
-
}
|
|
128
|
-
this._tracer = this._tracerProvider.getTracer("langchain", VERSION);
|
|
129
|
-
this._conversationId = `intro_conv_${randomUUID().replace(/-/g, "")}`;
|
|
130
|
-
}
|
|
131
|
-
_rootRunId(runId, parentRunId) {
|
|
132
|
-
if (!parentRunId)
|
|
133
|
-
return runId;
|
|
134
|
-
return this._runRoots.get(parentRunId) || parentRunId;
|
|
135
|
-
}
|
|
136
|
-
/** Keep the top-level run span aligned with child conversation IDs. */
|
|
137
|
-
_setRootConversationId(conversationId, runId, parentRunId) {
|
|
138
|
-
const root = this._spans.get(this._rootRunId(runId, parentRunId));
|
|
139
|
-
root?.setAttribute("gen_ai.conversation.id", conversationId);
|
|
140
|
-
}
|
|
141
|
-
_endRootIfComplete(runId) {
|
|
142
|
-
this._runRoots.delete(runId);
|
|
143
|
-
this._spanNames.delete(runId);
|
|
144
|
-
this._spanParents.delete(runId);
|
|
145
|
-
}
|
|
146
|
-
/** Create a span under a LangChain parent when one exists.
|
|
147
|
-
* Sets gen_ai.agent.name to the parent span's name for hierarchy. */
|
|
148
|
-
_createChildSpan(name, runId, parentRunId) {
|
|
149
|
-
const parent = parentRunId ? this._spans.get(parentRunId) : undefined;
|
|
150
|
-
this._runRoots.set(runId, this._rootRunId(runId, parentRunId));
|
|
151
|
-
const ctx = parent
|
|
152
|
-
? otelTrace.setSpan(otelContext.active(), parent)
|
|
153
|
-
: ROOT_CONTEXT;
|
|
154
|
-
const span = this._tracer.startSpan(name, {}, ctx);
|
|
155
|
-
this._spanNames.set(runId, name);
|
|
156
|
-
if (parentRunId) {
|
|
157
|
-
this._spanParents.set(runId, parentRunId);
|
|
158
|
-
}
|
|
159
|
-
// Walk up parents to find first non-wrapper name for gen_ai.agent.name
|
|
160
|
-
const agentName = this._findAgentName(parentRunId);
|
|
161
|
-
if (agentName) {
|
|
162
|
-
span.setAttribute("gen_ai.agent.name", agentName);
|
|
163
|
-
}
|
|
164
|
-
return span;
|
|
165
|
-
}
|
|
166
|
-
// -------------------------------------------------------------------------
|
|
167
|
-
// Chat model callbacks (preferred over LLM callbacks for chat models)
|
|
168
|
-
// -------------------------------------------------------------------------
|
|
169
|
-
handleChatModelStart(llm, messages, runId, parentRunId, extraParams, _tags, metadata, runName) {
|
|
170
|
-
const modelName = this._extractModelName(llm, extraParams);
|
|
171
|
-
const spanName = modelName ? `chat ${modelName}` : runName || "chat";
|
|
172
|
-
const otelSpan = this._createChildSpan(spanName, runId, parentRunId);
|
|
173
|
-
this._spans.set(runId, otelSpan);
|
|
174
|
-
const conversationId = this._getConversationId(metadata);
|
|
175
|
-
this._setRootConversationId(conversationId, runId, parentRunId);
|
|
176
|
-
otelSpan.setAttribute("gen_ai.operation.name", "chat");
|
|
177
|
-
otelSpan.setAttribute("gen_ai.conversation.id", conversationId);
|
|
178
|
-
otelSpan.setAttribute("openinference.span.kind", "LLM");
|
|
179
|
-
if (modelName) {
|
|
180
|
-
otelSpan.setAttribute("gen_ai.request.model", modelName);
|
|
181
|
-
}
|
|
182
|
-
const flatMessages = messages[0] || [];
|
|
183
|
-
const { inputMessages, systemInstructions } = this._convertMessages(flatMessages);
|
|
184
|
-
if (inputMessages.length > 0) {
|
|
185
|
-
otelSpan.setAttribute("gen_ai.input.messages", JSON.stringify(inputMessages));
|
|
186
|
-
this._llmInputs.set(runId, inputMessages);
|
|
187
|
-
}
|
|
188
|
-
if (systemInstructions.length > 0) {
|
|
189
|
-
otelSpan.setAttribute("gen_ai.system_instructions", JSON.stringify(systemInstructions.map((s) => ({ type: "text", content: s }))));
|
|
190
|
-
}
|
|
191
|
-
const provider = this._extractProvider(llm);
|
|
192
|
-
if (provider) {
|
|
193
|
-
otelSpan.setAttribute("gen_ai.system", provider);
|
|
194
|
-
}
|
|
195
|
-
const invocationParams = extraParams?.["invocation_params"];
|
|
196
|
-
const tools = invocationParams?.["tools"];
|
|
197
|
-
if (tools && tools.length > 0) {
|
|
198
|
-
otelSpan.setAttribute("gen_ai.tool.definitions", JSON.stringify(tools.map((t) => {
|
|
199
|
-
const fn = (t.function || t);
|
|
200
|
-
return {
|
|
201
|
-
type: t.type || "function",
|
|
202
|
-
name: (fn.name || ""),
|
|
203
|
-
description: (fn.description || ""),
|
|
204
|
-
parameters: fn.parameters,
|
|
205
|
-
};
|
|
206
|
-
})));
|
|
207
|
-
}
|
|
208
|
-
if (invocationParams?.["temperature"] != null) {
|
|
209
|
-
otelSpan.setAttribute("gen_ai.request.temperature", invocationParams["temperature"]);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
handleLLMStart(llm, prompts, runId, parentRunId, extraParams, _tags, metadata, runName) {
|
|
213
|
-
const modelName = this._extractModelName(llm, extraParams);
|
|
214
|
-
const spanName = modelName ? `chat ${modelName}` : runName || "llm";
|
|
215
|
-
const otelSpan = this._createChildSpan(spanName, runId, parentRunId);
|
|
216
|
-
this._spans.set(runId, otelSpan);
|
|
217
|
-
const conversationId = this._getConversationId(metadata);
|
|
218
|
-
this._setRootConversationId(conversationId, runId, parentRunId);
|
|
219
|
-
otelSpan.setAttribute("gen_ai.operation.name", "chat");
|
|
220
|
-
otelSpan.setAttribute("gen_ai.conversation.id", conversationId);
|
|
221
|
-
otelSpan.setAttribute("openinference.span.kind", "LLM");
|
|
222
|
-
if (modelName) {
|
|
223
|
-
otelSpan.setAttribute("gen_ai.request.model", modelName);
|
|
224
|
-
}
|
|
225
|
-
if (prompts.length > 0) {
|
|
226
|
-
const inputMessages = prompts.map((p) => ({
|
|
227
|
-
role: "user",
|
|
228
|
-
parts: [{ type: "text", content: p }],
|
|
229
|
-
}));
|
|
230
|
-
otelSpan.setAttribute("gen_ai.input.messages", JSON.stringify(inputMessages));
|
|
231
|
-
this._llmInputs.set(runId, inputMessages);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
handleLLMEnd(output, runId) {
|
|
235
|
-
const otelSpan = this._spans.get(runId);
|
|
236
|
-
if (!otelSpan)
|
|
237
|
-
return;
|
|
238
|
-
this._spans.delete(runId);
|
|
239
|
-
this._spanNames.delete(runId);
|
|
240
|
-
this._llmInputs.delete(runId);
|
|
241
|
-
this._endRootIfComplete(runId);
|
|
242
|
-
const generations = output.generations?.[0];
|
|
243
|
-
if (generations && generations.length > 0) {
|
|
244
|
-
const parts = [];
|
|
245
|
-
for (const gen of generations) {
|
|
246
|
-
if (gen.text) {
|
|
247
|
-
parts.push({ type: "text", content: gen.text });
|
|
248
|
-
}
|
|
249
|
-
const msg = gen.message;
|
|
250
|
-
const kwargs = (msg?.["kwargs"] || msg);
|
|
251
|
-
const additionalKwargs = kwargs?.["additional_kwargs"];
|
|
252
|
-
const toolCalls = (additionalKwargs?.["tool_calls"] ||
|
|
253
|
-
kwargs?.["tool_calls"]);
|
|
254
|
-
if (toolCalls) {
|
|
255
|
-
for (const tc of toolCalls) {
|
|
256
|
-
const fn = (tc.function || tc);
|
|
257
|
-
parts.push({
|
|
258
|
-
type: "tool_call",
|
|
259
|
-
name: (fn.name || tc.name || ""),
|
|
260
|
-
id: (tc.id || ""),
|
|
261
|
-
arguments: (fn.arguments || tc.args || ""),
|
|
262
|
-
});
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
if (parts.length > 0) {
|
|
267
|
-
const outputMessages = [{ role: "assistant", parts }];
|
|
268
|
-
otelSpan.setAttribute("gen_ai.output.messages", JSON.stringify(outputMessages));
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
const usage = output.llmOutput;
|
|
272
|
-
const tokenUsage = (usage?.["tokenUsage"] ||
|
|
273
|
-
usage?.["token_usage"] ||
|
|
274
|
-
usage?.["usage"]);
|
|
275
|
-
if (tokenUsage) {
|
|
276
|
-
const inputTokens = tokenUsage["promptTokens"] ||
|
|
277
|
-
tokenUsage["prompt_tokens"] ||
|
|
278
|
-
tokenUsage["input_tokens"];
|
|
279
|
-
const outputTokens = tokenUsage["completionTokens"] ||
|
|
280
|
-
tokenUsage["completion_tokens"] ||
|
|
281
|
-
tokenUsage["output_tokens"];
|
|
282
|
-
if (typeof inputTokens === "number") {
|
|
283
|
-
otelSpan.setAttribute("gen_ai.usage.input_tokens", inputTokens);
|
|
284
|
-
}
|
|
285
|
-
if (typeof outputTokens === "number") {
|
|
286
|
-
otelSpan.setAttribute("gen_ai.usage.output_tokens", outputTokens);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
const model = usage?.["model"] || usage?.["model_name"];
|
|
290
|
-
if (typeof model === "string") {
|
|
291
|
-
otelSpan.setAttribute("gen_ai.response.model", model);
|
|
292
|
-
}
|
|
293
|
-
// gen_ai.response.id — required by the server for conversation tracking.
|
|
294
|
-
const responseId = usage?.["id"] || usage?.["system_fingerprint"];
|
|
295
|
-
otelSpan.setAttribute("gen_ai.response.id", typeof responseId === "string" ? responseId : `langchain-${runId}`);
|
|
296
|
-
otelSpan.end();
|
|
297
|
-
}
|
|
298
|
-
handleLLMError(err, runId) {
|
|
299
|
-
const otelSpan = this._spans.get(runId);
|
|
300
|
-
if (!otelSpan)
|
|
301
|
-
return;
|
|
302
|
-
this._spans.delete(runId);
|
|
303
|
-
this._spanNames.delete(runId);
|
|
304
|
-
this._llmInputs.delete(runId);
|
|
305
|
-
this._endRootIfComplete(runId);
|
|
306
|
-
otelSpan.recordException(err);
|
|
307
|
-
otelSpan.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
|
|
308
|
-
otelSpan.setAttribute("exception.message", err.message);
|
|
309
|
-
otelSpan.end();
|
|
310
|
-
}
|
|
311
|
-
// -------------------------------------------------------------------------
|
|
312
|
-
// Chain callbacks
|
|
313
|
-
// -------------------------------------------------------------------------
|
|
314
|
-
handleChainStart(chain, _inputs, runId, _runType, _tags, metadata, runName, parentRunId) {
|
|
315
|
-
const name = runName || chain?.name || chain?.id?.[chain.id.length - 1] || "chain";
|
|
316
|
-
const otelSpan = this._createChildSpan(name, runId, parentRunId);
|
|
317
|
-
this._spans.set(runId, otelSpan);
|
|
318
|
-
const conversationId = this._getConversationId(metadata);
|
|
319
|
-
this._setRootConversationId(conversationId, runId, parentRunId);
|
|
320
|
-
otelSpan.setAttribute("gen_ai.conversation.id", conversationId);
|
|
321
|
-
}
|
|
322
|
-
handleChainEnd(_outputs, runId) {
|
|
323
|
-
const otelSpan = this._spans.get(runId);
|
|
324
|
-
if (!otelSpan)
|
|
325
|
-
return;
|
|
326
|
-
this._spans.delete(runId);
|
|
327
|
-
this._endRootIfComplete(runId);
|
|
328
|
-
otelSpan.end();
|
|
329
|
-
}
|
|
330
|
-
handleChainError(err, runId) {
|
|
331
|
-
const otelSpan = this._spans.get(runId);
|
|
332
|
-
if (!otelSpan)
|
|
333
|
-
return;
|
|
334
|
-
this._spans.delete(runId);
|
|
335
|
-
this._endRootIfComplete(runId);
|
|
336
|
-
otelSpan.recordException(err);
|
|
337
|
-
otelSpan.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
|
|
338
|
-
otelSpan.setAttribute("exception.message", err.message);
|
|
339
|
-
otelSpan.end();
|
|
340
|
-
}
|
|
341
|
-
// -------------------------------------------------------------------------
|
|
342
|
-
// Tool callbacks
|
|
343
|
-
// -------------------------------------------------------------------------
|
|
344
|
-
handleToolStart(tool, input, runId, parentRunId, _tags, metadata, runName) {
|
|
345
|
-
const toolName = runName || tool?.name || tool?.id?.[tool.id.length - 1] || "tool";
|
|
346
|
-
const otelSpan = this._createChildSpan(toolName, runId, parentRunId);
|
|
347
|
-
this._spans.set(runId, otelSpan);
|
|
348
|
-
const conversationId = this._getConversationId(metadata);
|
|
349
|
-
this._setRootConversationId(conversationId, runId, parentRunId);
|
|
350
|
-
otelSpan.setAttribute("gen_ai.tool.name", toolName);
|
|
351
|
-
otelSpan.setAttribute("openinference.span.kind", "TOOL");
|
|
352
|
-
otelSpan.setAttribute("gen_ai.tool.input", input);
|
|
353
|
-
otelSpan.setAttribute("gen_ai.conversation.id", conversationId);
|
|
354
|
-
}
|
|
355
|
-
handleToolEnd(output, runId) {
|
|
356
|
-
const otelSpan = this._spans.get(runId);
|
|
357
|
-
if (!otelSpan)
|
|
358
|
-
return;
|
|
359
|
-
this._spans.delete(runId);
|
|
360
|
-
this._endRootIfComplete(runId);
|
|
361
|
-
if (output != null) {
|
|
362
|
-
otelSpan.setAttribute("gen_ai.tool.output", typeof output === "string" ? output : JSON.stringify(output));
|
|
363
|
-
}
|
|
364
|
-
otelSpan.end();
|
|
365
|
-
}
|
|
366
|
-
handleToolError(err, runId) {
|
|
367
|
-
const otelSpan = this._spans.get(runId);
|
|
368
|
-
if (!otelSpan)
|
|
369
|
-
return;
|
|
370
|
-
this._spans.delete(runId);
|
|
371
|
-
this._endRootIfComplete(runId);
|
|
372
|
-
otelSpan.recordException(err);
|
|
373
|
-
otelSpan.setStatus({ code: SpanStatusCode.ERROR, message: err.message });
|
|
374
|
-
otelSpan.setAttribute("exception.message", err.message);
|
|
375
|
-
otelSpan.end();
|
|
376
|
-
}
|
|
377
|
-
// -------------------------------------------------------------------------
|
|
378
|
-
// Lifecycle
|
|
379
|
-
// -------------------------------------------------------------------------
|
|
380
|
-
async shutdown() {
|
|
381
|
-
for (const span of this._spans.values())
|
|
382
|
-
span.end();
|
|
383
|
-
this._spans.clear();
|
|
384
|
-
this._spanNames.clear();
|
|
385
|
-
this._spanParents.clear();
|
|
386
|
-
this._runRoots.clear();
|
|
387
|
-
await this._tracerProvider.shutdown();
|
|
388
|
-
}
|
|
389
|
-
async forceFlush() {
|
|
390
|
-
await this._tracerProvider.forceFlush();
|
|
391
|
-
}
|
|
392
|
-
// -------------------------------------------------------------------------
|
|
393
|
-
// Helpers
|
|
394
|
-
// -------------------------------------------------------------------------
|
|
395
|
-
_extractModelName(llm, extraParams) {
|
|
396
|
-
const invocationParams = extraParams?.["invocation_params"];
|
|
397
|
-
if (invocationParams) {
|
|
398
|
-
const model = invocationParams["model"] ||
|
|
399
|
-
invocationParams["model_name"] ||
|
|
400
|
-
invocationParams["modelName"];
|
|
401
|
-
if (typeof model === "string")
|
|
402
|
-
return model;
|
|
403
|
-
}
|
|
404
|
-
const kwargs = llm?.kwargs;
|
|
405
|
-
if (kwargs) {
|
|
406
|
-
const model = kwargs["model"] || kwargs["model_name"] || kwargs["modelName"];
|
|
407
|
-
if (typeof model === "string")
|
|
408
|
-
return model;
|
|
409
|
-
}
|
|
410
|
-
return undefined;
|
|
411
|
-
}
|
|
412
|
-
_extractProvider(llm) {
|
|
413
|
-
const id = llm?.id;
|
|
414
|
-
if (Array.isArray(id) && id.length > 0) {
|
|
415
|
-
return id[id.length - 1];
|
|
416
|
-
}
|
|
417
|
-
return undefined;
|
|
418
|
-
}
|
|
419
|
-
/** Walk up the span tree to find the first non-wrapper span name. */
|
|
420
|
-
_findAgentName(runId) {
|
|
421
|
-
let current = runId;
|
|
422
|
-
for (let i = 0; i < 20 && current; i++) {
|
|
423
|
-
const name = this._spanNames.get(current);
|
|
424
|
-
if (name && !IntrospectionCallbackHandler._wrapperNames.has(name)) {
|
|
425
|
-
return name;
|
|
426
|
-
}
|
|
427
|
-
current = this._spanParents.get(current);
|
|
428
|
-
}
|
|
429
|
-
return undefined;
|
|
430
|
-
}
|
|
431
|
-
_getConversationId(metadata) {
|
|
432
|
-
const metaConvId = metadata?.["gen_ai.conversation.id"];
|
|
433
|
-
if (metaConvId !== undefined && metaConvId !== null) {
|
|
434
|
-
return String(metaConvId);
|
|
435
|
-
}
|
|
436
|
-
const threadId = metadata?.["thread_id"];
|
|
437
|
-
if (threadId !== undefined && threadId !== null) {
|
|
438
|
-
return String(threadId);
|
|
439
|
-
}
|
|
440
|
-
return this._conversationId;
|
|
441
|
-
}
|
|
442
|
-
_convertMessages(messages) {
|
|
443
|
-
const inputMessages = [];
|
|
444
|
-
const systemInstructions = [];
|
|
445
|
-
for (const msg of messages) {
|
|
446
|
-
const msgType = msg._getType?.() ||
|
|
447
|
-
msg.type ||
|
|
448
|
-
"unknown";
|
|
449
|
-
const role = this._mapRole(msgType);
|
|
450
|
-
const content = this._extractContent(msg.content);
|
|
451
|
-
if (role === "system") {
|
|
452
|
-
systemInstructions.push(typeof msg.content === "string"
|
|
453
|
-
? msg.content
|
|
454
|
-
: JSON.stringify(msg.content));
|
|
455
|
-
continue;
|
|
456
|
-
}
|
|
457
|
-
const parts = [];
|
|
458
|
-
if (role === "tool") {
|
|
459
|
-
const toolCallId = msg
|
|
460
|
-
.tool_call_id || "";
|
|
461
|
-
parts.push({
|
|
462
|
-
type: "tool_call_response",
|
|
463
|
-
id: toolCallId,
|
|
464
|
-
response: content,
|
|
465
|
-
});
|
|
466
|
-
}
|
|
467
|
-
else if (content) {
|
|
468
|
-
parts.push({ type: "text", content });
|
|
469
|
-
}
|
|
470
|
-
const toolCalls = msg
|
|
471
|
-
.tool_calls;
|
|
472
|
-
if (toolCalls) {
|
|
473
|
-
for (const tc of toolCalls) {
|
|
474
|
-
parts.push({
|
|
475
|
-
type: "tool_call",
|
|
476
|
-
name: (tc.name || ""),
|
|
477
|
-
id: (tc.id || ""),
|
|
478
|
-
arguments: typeof tc.args === "string" ? tc.args : JSON.stringify(tc.args),
|
|
479
|
-
});
|
|
480
|
-
}
|
|
481
|
-
}
|
|
482
|
-
if (parts.length > 0) {
|
|
483
|
-
inputMessages.push({ role, parts });
|
|
484
|
-
}
|
|
485
|
-
}
|
|
486
|
-
return { inputMessages, systemInstructions };
|
|
487
|
-
}
|
|
488
|
-
_mapRole(msgType) {
|
|
489
|
-
switch (msgType) {
|
|
490
|
-
case "human":
|
|
491
|
-
return "user";
|
|
492
|
-
case "ai":
|
|
493
|
-
return "assistant";
|
|
494
|
-
case "system":
|
|
495
|
-
return "system";
|
|
496
|
-
case "tool":
|
|
497
|
-
return "tool";
|
|
498
|
-
default:
|
|
499
|
-
return "user";
|
|
500
|
-
}
|
|
501
|
-
}
|
|
502
|
-
_extractContent(content) {
|
|
503
|
-
if (typeof content === "string")
|
|
504
|
-
return content;
|
|
505
|
-
if (Array.isArray(content)) {
|
|
506
|
-
return content
|
|
507
|
-
.map((part) => {
|
|
508
|
-
const p = part;
|
|
509
|
-
if (p.type === "text")
|
|
510
|
-
return (p.text || "");
|
|
511
|
-
return "";
|
|
512
|
-
})
|
|
513
|
-
.filter(Boolean)
|
|
514
|
-
.join("");
|
|
515
|
-
}
|
|
516
|
-
return String(content || "");
|
|
517
|
-
}
|
|
518
|
-
}
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Mastra-compatible Exporter for Introspection.
|
|
3
|
-
*
|
|
4
|
-
* Extends Mastra's BaseExporter to receive TracingEvents (Mastra's native
|
|
5
|
-
* span format) and convert them to gen_ai.* OTel spans for the Introspection
|
|
6
|
-
* backend.
|
|
7
|
-
*
|
|
8
|
-
* **Important:** This module requires `@mastra/observability` and `@mastra/core`
|
|
9
|
-
* at runtime. Import via the subpath: `@introspection-sdk/introspection-node/mastra`
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* import { Mastra } from "@mastra/core/mastra";
|
|
14
|
-
* import { IntrospectionMastraExporter } from "@introspection-sdk/introspection-node/mastra";
|
|
15
|
-
*
|
|
16
|
-
* const mastra = new Mastra({
|
|
17
|
-
* agents: { myAgent },
|
|
18
|
-
* observability: {
|
|
19
|
-
* configs: {
|
|
20
|
-
* otel: {
|
|
21
|
-
* serviceName: "my-mastra-app",
|
|
22
|
-
* exporters: [new IntrospectionMastraExporter()],
|
|
23
|
-
* },
|
|
24
|
-
* },
|
|
25
|
-
* },
|
|
26
|
-
* });
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
29
|
-
import { BaseExporter } from "@mastra/observability";
|
|
30
|
-
import type { TracingEvent, InitExporterOptions } from "@mastra/core/observability";
|
|
31
|
-
import { type SpanExporter, type IdGenerator } from "@opentelemetry/sdk-trace-base";
|
|
32
|
-
/** Advanced options for testing and customization. */
|
|
33
|
-
export interface MastraExporterAdvancedOptions {
|
|
34
|
-
/** Custom span exporter for testing. */
|
|
35
|
-
spanExporter?: SpanExporter;
|
|
36
|
-
/** Custom ID generator for testing. */
|
|
37
|
-
idGenerator?: IdGenerator;
|
|
38
|
-
/** Use SimpleSpanProcessor instead of BatchSpanProcessor. */
|
|
39
|
-
useSimpleSpanProcessor?: boolean;
|
|
40
|
-
}
|
|
41
|
-
/** Configuration for {@link IntrospectionMastraExporter}. */
|
|
42
|
-
export interface IntrospectionMastraExporterOptions {
|
|
43
|
-
/** Authentication token (env: INTROSPECTION_TOKEN). */
|
|
44
|
-
token?: string;
|
|
45
|
-
/** Base URL for the API (env: INTROSPECTION_BASE_URL). */
|
|
46
|
-
baseUrl?: string;
|
|
47
|
-
/** Additional headers to include in requests. */
|
|
48
|
-
additionalHeaders?: Record<string, string>;
|
|
49
|
-
/** Enable debug logging. */
|
|
50
|
-
debug?: boolean;
|
|
51
|
-
/** Advanced options for testing. */
|
|
52
|
-
advanced?: MastraExporterAdvancedOptions;
|
|
53
|
-
}
|
|
54
|
-
export declare class IntrospectionMastraExporter extends BaseExporter {
|
|
55
|
-
name: string;
|
|
56
|
-
private _options;
|
|
57
|
-
private _tracerProvider?;
|
|
58
|
-
private _tracer?;
|
|
59
|
-
private _conversationIds;
|
|
60
|
-
private _rootSpans;
|
|
61
|
-
constructor(options?: IntrospectionMastraExporterOptions);
|
|
62
|
-
/**
|
|
63
|
-
* Called by Mastra after the instance is fully configured.
|
|
64
|
-
* We defer TracerProvider creation to here so we can use the service name.
|
|
65
|
-
*/
|
|
66
|
-
init(options: InitExporterOptions): void;
|
|
67
|
-
private _initProvider;
|
|
68
|
-
protected _exportTracingEvent(event: TracingEvent): Promise<void>;
|
|
69
|
-
flush(): Promise<void>;
|
|
70
|
-
shutdown(): Promise<void>;
|
|
71
|
-
private _exportSpan;
|
|
72
|
-
/** Ensure a root OTel span exists for this Mastra trace.
|
|
73
|
-
* Events arrive in end-order (children first, agent_run last),
|
|
74
|
-
* so we create a synthetic root on first encounter. */
|
|
75
|
-
private _ensureRoot;
|
|
76
|
-
private _createSpan;
|
|
77
|
-
private _exportAgentRun;
|
|
78
|
-
private _exportModelStep;
|
|
79
|
-
private _exportToolCall;
|
|
80
|
-
private _getConversationId;
|
|
81
|
-
private _convertInput;
|
|
82
|
-
private _convertOutput;
|
|
83
|
-
private _extractParts;
|
|
84
|
-
}
|
|
85
|
-
//# sourceMappingURL=mastra-exporter.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"mastra-exporter.d.ts","sourceRoot":"","sources":["../src/mastra-exporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EACV,YAAY,EACZ,mBAAmB,EAEpB,MAAM,4BAA4B,CAAC;AAOpC,OAAO,EAIL,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,+BAA+B,CAAC;AAkBvC,sDAAsD;AACtD,MAAM,WAAW,6BAA6B;IAC5C,wCAAwC;IACxC,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,uCAAuC;IACvC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,6DAA6D;IAC7D,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,6DAA6D;AAC7D,MAAM,WAAW,kCAAkC;IACjD,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iDAAiD;IACjD,iBAAiB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3C,4BAA4B;IAC5B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,6BAA6B,CAAC;CAC1C;AAMD,qBAAa,2BAA4B,SAAQ,YAAY;IAC3D,IAAI,SAAmB;IAEvB,OAAO,CAAC,QAAQ,CAAqC;IACrD,OAAO,CAAC,eAAe,CAAC,CAAsB;IAC9C,OAAO,CAAC,OAAO,CAAC,CAA+C;IAC/D,OAAO,CAAC,gBAAgB,CAAkC;IAE1D,OAAO,CAAC,UAAU,CAAoC;gBAE1C,OAAO,GAAE,kCAAuC;IAU5D;;;OAGG;IACH,IAAI,CAAC,OAAO,EAAE,mBAAmB,GAAG,IAAI;IASxC,OAAO,CAAC,aAAa;cA4EL,mBAAmB,CAAC,KAAK,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC;IAajE,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAItB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAW/B,OAAO,CAAC,WAAW;IAsBnB;;4DAEwD;IACxD,OAAO,CAAC,WAAW;IAQnB,OAAO,CAAC,WAAW;IAiBnB,OAAO,CAAC,eAAe;IAoCvB,OAAO,CAAC,gBAAgB;IA+HxB,OAAO,CAAC,eAAe;IAiCvB,OAAO,CAAC,kBAAkB;IAoB1B,OAAO,CAAC,aAAa;IAiDrB,OAAO,CAAC,cAAc;IA6CtB,OAAO,CAAC,aAAa;CA6DtB"}
|