@introspection-sdk/introspection-node 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/.turbo/turbo-build.log +4 -0
  2. package/.turbo/turbo-check-types.log +5 -0
  3. package/.turbo/turbo-lint.log +5 -0
  4. package/dist/ai-sdk-integration.d.ts +123 -0
  5. package/dist/ai-sdk-integration.d.ts.map +1 -0
  6. package/dist/ai-sdk-integration.js +475 -0
  7. package/dist/anthropic.d.ts +54 -0
  8. package/dist/anthropic.d.ts.map +1 -0
  9. package/dist/anthropic.js +375 -0
  10. package/dist/claude-hooks.d.ts +328 -0
  11. package/dist/claude-hooks.d.ts.map +1 -0
  12. package/dist/claude-hooks.js +571 -0
  13. package/dist/claude-wrapper.d.ts +93 -0
  14. package/dist/claude-wrapper.d.ts.map +1 -0
  15. package/dist/claude-wrapper.js +154 -0
  16. package/dist/client.d.ts +157 -0
  17. package/dist/client.d.ts.map +1 -0
  18. package/dist/client.js +394 -0
  19. package/dist/converters/ai-sdk.d.ts +65 -0
  20. package/dist/converters/ai-sdk.d.ts.map +1 -0
  21. package/dist/converters/ai-sdk.js +238 -0
  22. package/dist/converters/claude.d.ts +146 -0
  23. package/dist/converters/claude.d.ts.map +1 -0
  24. package/dist/converters/claude.js +202 -0
  25. package/dist/converters/index.d.ts +9 -0
  26. package/dist/converters/index.d.ts.map +1 -0
  27. package/dist/converters/index.js +8 -0
  28. package/dist/converters/openai.d.ts +51 -0
  29. package/dist/converters/openai.d.ts.map +1 -0
  30. package/dist/converters/openai.js +268 -0
  31. package/dist/converters/openclaw.d.ts +16 -0
  32. package/dist/converters/openclaw.d.ts.map +1 -0
  33. package/dist/converters/openclaw.js +49 -0
  34. package/dist/converters/openinference.d.ts +107 -0
  35. package/dist/converters/openinference.d.ts.map +1 -0
  36. package/dist/converters/openinference.js +552 -0
  37. package/dist/converters/vercel.d.ts +21 -0
  38. package/dist/converters/vercel.d.ts.map +1 -0
  39. package/dist/converters/vercel.js +208 -0
  40. package/dist/index.d.ts +38 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +39 -0
  43. package/dist/langchain-handler.d.ts +101 -0
  44. package/dist/langchain-handler.d.ts.map +1 -0
  45. package/dist/langchain-handler.js +489 -0
  46. package/dist/mastra-exporter.d.ts +85 -0
  47. package/dist/mastra-exporter.d.ts.map +1 -0
  48. package/dist/mastra-exporter.js +471 -0
  49. package/dist/span-processor.d.ts +62 -0
  50. package/dist/span-processor.d.ts.map +1 -0
  51. package/dist/span-processor.js +310 -0
  52. package/dist/tracing-processor.d.ts +136 -0
  53. package/dist/tracing-processor.d.ts.map +1 -0
  54. package/dist/tracing-processor.js +368 -0
  55. package/dist/types/genai.d.ts +165 -0
  56. package/dist/types/genai.d.ts.map +1 -0
  57. package/dist/types/genai.js +82 -0
  58. package/dist/types.d.ts +7 -0
  59. package/dist/types.d.ts.map +1 -0
  60. package/dist/types.js +1 -0
  61. package/dist/utils.d.ts +19 -0
  62. package/dist/utils.d.ts.map +1 -0
  63. package/dist/utils.js +57 -0
  64. package/dist/version.d.ts +3 -0
  65. package/dist/version.d.ts.map +1 -0
  66. package/dist/version.js +2 -0
  67. package/eslint.config.mjs +4 -0
  68. package/package.json +78 -0
  69. package/src/ai-sdk-integration.ts +689 -0
  70. package/src/anthropic.ts +465 -0
  71. package/src/claude-hooks.ts +1010 -0
  72. package/src/claude-wrapper.ts +240 -0
  73. package/src/client.ts +513 -0
  74. package/src/converters/ai-sdk.ts +298 -0
  75. package/src/converters/claude.ts +334 -0
  76. package/src/converters/index.ts +44 -0
  77. package/src/converters/openai.ts +302 -0
  78. package/src/converters/openinference.ts +673 -0
  79. package/src/converters/vercel.ts +264 -0
  80. package/src/index.ts +121 -0
  81. package/src/langchain-handler.ts +722 -0
  82. package/src/mastra-exporter.ts +650 -0
  83. package/src/span-processor.ts +418 -0
  84. package/src/tracing-processor.ts +548 -0
  85. package/src/types/genai.ts +264 -0
  86. package/src/types.ts +13 -0
  87. package/src/utils.ts +69 -0
  88. package/src/version.ts +2 -0
  89. package/tsconfig.json +8 -0
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Converter for Vercel AI SDK telemetry spans → Gen AI semantic conventions.
3
+ *
4
+ * The Vercel AI SDK emits spans with `ai.*` attributes when
5
+ * `experimental_telemetry: { isEnabled: true }` is set on streamText/generateText.
6
+ * This converter translates those to `gen_ai.*` so the Introspection frontend
7
+ * can render them in the conversation view.
8
+ */
9
+
10
+ import type { Attributes } from "@opentelemetry/api";
11
+
12
+ interface VercelMessage {
13
+ role: string;
14
+ content?: string | VercelContentPart[];
15
+ }
16
+
17
+ interface VercelContentPart {
18
+ type: string;
19
+ text?: string;
20
+ content?: string;
21
+ toolName?: string;
22
+ name?: string;
23
+ toolCallId?: string;
24
+ id?: string;
25
+ args?: string | Record<string, unknown>;
26
+ }
27
+
28
+ interface VercelTool {
29
+ type?: string;
30
+ name?: string;
31
+ description?: string;
32
+ inputSchema?: Record<string, unknown>;
33
+ parameters?: Record<string, unknown>;
34
+ }
35
+
36
+ interface GenAIPart {
37
+ type: string;
38
+ content?: string;
39
+ name?: string;
40
+ id?: string;
41
+ arguments?: string;
42
+ }
43
+
44
+ interface GenAIMessage {
45
+ role: string;
46
+ parts: GenAIPart[];
47
+ }
48
+
49
+ /** Check if a span is a Vercel AI SDK span worth converting.
50
+ * Only converts child "do*" spans (doStream, doGenerate) which have
51
+ * the actual prompt/response data with proper message windows.
52
+ * Parent wrapper spans (ai.streamText, ai.generateText) are skipped
53
+ * to avoid duplicate conversation steps without user messages. */
54
+ export function isVercelAISpan(attrs: Attributes): boolean {
55
+ const operationId = attrs["ai.operationId"];
56
+ return typeof operationId === "string" && operationId.includes(".do");
57
+ }
58
+
59
+ /**
60
+ * Convert Vercel AI SDK `ai.*` attributes to `gen_ai.*` semconv attributes.
61
+ * Returns only the gen_ai attributes to merge — does NOT remove originals.
62
+ */
63
+ export function convertVercelAIToGenAI(
64
+ attrs: Attributes,
65
+ ): Record<string, unknown> {
66
+ const result: Record<string, unknown> = {};
67
+
68
+ // gen_ai.operation.name
69
+ result["gen_ai.operation.name"] = "chat";
70
+
71
+ // gen_ai.usage.cache_read.input_tokens from ai.usage.cachedInputTokens
72
+ const cachedInput = attrs["ai.usage.cachedInputTokens"];
73
+ if (typeof cachedInput === "number") {
74
+ result["gen_ai.usage.cache_read.input_tokens"] = cachedInput;
75
+ }
76
+
77
+ // gen_ai.conversation.id from telemetry metadata (set by app)
78
+ const metadataConvId = attrs["ai.telemetry.metadata.gen_ai.conversation.id"];
79
+ if (typeof metadataConvId === "string") {
80
+ result["gen_ai.conversation.id"] = metadataConvId;
81
+ }
82
+
83
+ // gen_ai.request.model and gen_ai.system from ai.model.*
84
+ const modelId = attrs["ai.model.id"];
85
+ if (typeof modelId === "string" && !attrs["gen_ai.request.model"]) {
86
+ result["gen_ai.request.model"] = modelId;
87
+ }
88
+ const modelProvider = attrs["ai.model.provider"];
89
+ if (typeof modelProvider === "string" && !attrs["gen_ai.system"]) {
90
+ result["gen_ai.system"] = modelProvider;
91
+ }
92
+
93
+ // Vercel AI SDK stores prompt data in two formats:
94
+ // - Child spans (doStream): flat keys like ai.prompt.messages, ai.prompt.toolChoice
95
+ // - Parent spans (streamText): single JSON string in ai.prompt
96
+ const promptMessages = attrs["ai.prompt.messages"];
97
+ const promptRaw = attrs["ai.prompt"];
98
+ let parsedMessages: VercelMessage[] | null = null;
99
+ let parsedSystem: string | null = null;
100
+
101
+ if (typeof promptMessages === "string") {
102
+ try {
103
+ const parsed: unknown = JSON.parse(promptMessages);
104
+ if (Array.isArray(parsed)) parsedMessages = parsed as VercelMessage[];
105
+ } catch {
106
+ /* ignore */
107
+ }
108
+ } else if (typeof promptRaw === "string") {
109
+ try {
110
+ const parsed = JSON.parse(promptRaw) as Record<string, unknown>;
111
+ if (Array.isArray(parsed?.messages))
112
+ parsedMessages = parsed.messages as VercelMessage[];
113
+ if (typeof parsed?.system === "string") parsedSystem = parsed.system;
114
+ } catch {
115
+ /* ignore */
116
+ }
117
+ }
118
+
119
+ // gen_ai.input.messages — excluding system messages
120
+ const nonSystemMessages = parsedMessages
121
+ ? parsedMessages.filter((m) => m.role !== "system")
122
+ : null;
123
+ if (nonSystemMessages && nonSystemMessages.length > 0) {
124
+ const converted = nonSystemMessages.map(convertVercelMessageToGenAI);
125
+ result["gen_ai.input.messages"] = JSON.stringify(converted);
126
+ }
127
+
128
+ // gen_ai.output.messages — build from ai.response.text + ai.response.reasoning
129
+ const responseText = attrs["ai.response.text"];
130
+ const responseReasoning = attrs["ai.response.reasoning"];
131
+ if (typeof responseText === "string" && responseText) {
132
+ const parts: GenAIPart[] = [];
133
+ if (typeof responseReasoning === "string" && responseReasoning) {
134
+ parts.push({ type: "thinking", content: responseReasoning });
135
+ }
136
+ parts.push({ type: "text", content: responseText });
137
+ result["gen_ai.output.messages"] = JSON.stringify([
138
+ { role: "assistant", parts },
139
+ ]);
140
+ }
141
+
142
+ // gen_ai.tool.definitions — parse ai.prompt.tools JSON strings
143
+ const promptTools = attrs["ai.prompt.tools"];
144
+ if (typeof promptTools === "string") {
145
+ try {
146
+ const parsed: unknown = JSON.parse(promptTools);
147
+ if (Array.isArray(parsed)) {
148
+ result["gen_ai.tool.definitions"] = JSON.stringify(
149
+ (parsed as (string | VercelTool)[])
150
+ .map((t) => {
151
+ if (typeof t === "string") {
152
+ try {
153
+ return JSON.parse(t) as VercelTool;
154
+ } catch {
155
+ return { name: t } as VercelTool;
156
+ }
157
+ }
158
+ return t;
159
+ })
160
+ .map((t) => ({
161
+ type: t.type || "function",
162
+ name: t.name || "",
163
+ description: t.description || "",
164
+ parameters: t.inputSchema || t.parameters || undefined,
165
+ })),
166
+ );
167
+ }
168
+ } catch {
169
+ // ignore
170
+ }
171
+ } else if (Array.isArray(promptTools)) {
172
+ const tools = (promptTools as string[]).map((t) => {
173
+ const parsed = (typeof t === "string" ? JSON.parse(t) : t) as VercelTool;
174
+ return {
175
+ type: parsed.type || "function",
176
+ name: parsed.name || "",
177
+ description: parsed.description || "",
178
+ parameters: parsed.inputSchema || parsed.parameters || undefined,
179
+ };
180
+ });
181
+ result["gen_ai.tool.definitions"] = JSON.stringify(tools);
182
+ }
183
+
184
+ // gen_ai.agent.name — from telemetry functionId or operation
185
+ const functionId = attrs["ai.telemetry.functionId"];
186
+ if (typeof functionId === "string" && functionId) {
187
+ result["gen_ai.agent.name"] = functionId;
188
+ }
189
+
190
+ // gen_ai.system_instructions — from parsed system prompt or system role messages
191
+ if (parsedSystem) {
192
+ result["gen_ai.system_instructions"] = JSON.stringify([
193
+ { type: "text", content: parsedSystem },
194
+ ]);
195
+ } else if (parsedMessages) {
196
+ const systemMsgs = parsedMessages.filter((m) => m.role === "system");
197
+ if (systemMsgs.length > 0) {
198
+ result["gen_ai.system_instructions"] = JSON.stringify(
199
+ systemMsgs.map((m) => ({
200
+ type: "text",
201
+ content:
202
+ typeof m.content === "string"
203
+ ? m.content
204
+ : JSON.stringify(m.content),
205
+ })),
206
+ );
207
+ }
208
+ }
209
+
210
+ return result;
211
+ }
212
+
213
+ function convertVercelMessageToGenAI(msg: VercelMessage): GenAIMessage {
214
+ const role = msg.role || "user";
215
+
216
+ if (role === "system") {
217
+ return {
218
+ role,
219
+ parts: [{ type: "text", content: extractTextContent(msg.content) }],
220
+ };
221
+ }
222
+
223
+ return {
224
+ role,
225
+ parts: extractParts(msg.content),
226
+ };
227
+ }
228
+
229
+ function extractTextContent(content: VercelMessage["content"]): string {
230
+ if (typeof content === "string") return content;
231
+ if (Array.isArray(content)) {
232
+ return content
233
+ .filter((p) => p.type === "text")
234
+ .map((p) => p.text || p.content || "")
235
+ .join("");
236
+ }
237
+ return String(content || "");
238
+ }
239
+
240
+ function extractParts(content: VercelMessage["content"]): GenAIPart[] {
241
+ if (typeof content === "string") {
242
+ return [{ type: "text", content }];
243
+ }
244
+ if (Array.isArray(content)) {
245
+ return content.map((part) => {
246
+ if (part.type === "text") {
247
+ return { type: "text", content: part.text || part.content || "" };
248
+ }
249
+ if (part.type === "tool-call" || part.type === "tool_call") {
250
+ return {
251
+ type: "tool_call",
252
+ name: part.toolName || part.name || "",
253
+ id: part.toolCallId || part.id || "",
254
+ arguments:
255
+ typeof part.args === "string"
256
+ ? part.args
257
+ : JSON.stringify(part.args),
258
+ };
259
+ }
260
+ return { type: "text", content: JSON.stringify(part) };
261
+ });
262
+ }
263
+ return [{ type: "text", content: String(content || "") }];
264
+ }
package/src/index.ts ADDED
@@ -0,0 +1,121 @@
1
+ import { IntrospectionSpanProcessor } from "./span-processor.js";
2
+ import type { IntrospectionSpanProcessorOptions } from "./span-processor.js";
3
+
4
+ /**
5
+ * Factory that creates an {@link IntrospectionSpanProcessor}.
6
+ *
7
+ * Provided as the default export for convenient use with logfire's
8
+ * `additionalSpanProcessors` option.
9
+ *
10
+ * @param options - Optional processor configuration (token, serviceName, etc.).
11
+ * @returns A ready-to-use {@link IntrospectionSpanProcessor}.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * import introspectionSpanProcessor from "@introspection-sdk/introspection-node";
16
+ *
17
+ * logfire.configure({ additionalSpanProcessors: [introspectionSpanProcessor()] });
18
+ * ```
19
+ */
20
+ export default function introspectionSpanProcessor(
21
+ options?: IntrospectionSpanProcessorOptions,
22
+ ): IntrospectionSpanProcessor {
23
+ return new IntrospectionSpanProcessor(options);
24
+ }
25
+
26
+ // Span processor exports (for OpenTelemetry integration)
27
+ export { IntrospectionSpanProcessor } from "./span-processor.js";
28
+ export type { IntrospectionSpanProcessorOptions } from "./span-processor.js";
29
+
30
+ // Client exports
31
+ export { IntrospectionClient } from "./client.js";
32
+
33
+ // Type exports
34
+ export type {
35
+ AdvancedOptions,
36
+ IntrospectionClientOptions,
37
+ FeedbackOptions,
38
+ UserTraits,
39
+ } from "./types.js";
40
+
41
+ // Tracing processor exports (for OpenAI Agents SDK integration)
42
+ export { IntrospectionTracingProcessor } from "./tracing-processor.js";
43
+ export type {
44
+ IntrospectionTracingProcessorOptions,
45
+ TracingProcessorAdvancedOptions,
46
+ } from "./tracing-processor.js";
47
+
48
+ // Claude Agent SDK hooks exports
49
+ export { IntrospectionClaudeHooks } from "./claude-hooks.js";
50
+ export type {
51
+ IntrospectionClaudeHooksOptions,
52
+ ClaudeHooksAdvancedOptions,
53
+ ClaudeHooksConfig,
54
+ ClaudeHookCallbackMatcher,
55
+ ClaudeHookEvent,
56
+ ClaudeHookInput,
57
+ ClaudeHookOutput,
58
+ ClaudeHookCallback,
59
+ ClaudeUsage,
60
+ ClaudeModelUsage,
61
+ ClaudeResultMessage,
62
+ ClaudeAssistantMessage,
63
+ ClaudeSDKMessage,
64
+ } from "./claude-hooks.js";
65
+
66
+ // Claude Agent SDK wrapper exports
67
+ export { withIntrospection } from "./claude-wrapper.js";
68
+ export type {
69
+ WithIntrospectionOptions,
70
+ InstrumentedClaudeAgentSDK,
71
+ ClaudeAgentSDKModule,
72
+ } from "./claude-wrapper.js";
73
+
74
+ // GenAI types exports
75
+ export type {
76
+ GenAiAttributes,
77
+ InputMessage,
78
+ OutputMessage,
79
+ SystemInstruction,
80
+ ToolDefinition,
81
+ TextPart,
82
+ ToolCallRequestPart,
83
+ ToolCallResponsePart,
84
+ MessagePart,
85
+ } from "./types/genai.js";
86
+ export { toAttributes } from "./types/genai.js";
87
+
88
+ // OpenAI converter exports
89
+ export {
90
+ convertResponsesInputsToSemconv,
91
+ convertResponsesOutputsToSemconv,
92
+ convertResponsesToolsToSemconv,
93
+ convertResponsesInstructionsToSemconv,
94
+ } from "./converters/openai.js";
95
+ export type {
96
+ ResponseInputItem,
97
+ ResponseOutputItem,
98
+ ResponseTool,
99
+ ResponseUsage,
100
+ Response as OpenAIResponse,
101
+ } from "./converters/openai.js";
102
+
103
+ // AI SDK integration exports (for Vercel AI SDK)
104
+ export { IntrospectionAISDKIntegration } from "./ai-sdk-integration.js";
105
+ export type {
106
+ IntrospectionAISDKIntegrationOptions,
107
+ AISDKIntegrationAdvancedOptions,
108
+ } from "./ai-sdk-integration.js";
109
+
110
+ // OpenInference converter exports
111
+ export {
112
+ addOpenInferenceAttributes,
113
+ OpenInferenceSpanExporter,
114
+ } from "./converters/openinference.js";
115
+
116
+ // Anthropic instrumentor exports
117
+ export {
118
+ AnthropicInstrumentor,
119
+ tracedMessagesCreate,
120
+ REDACTED_THINKING_CONTENT,
121
+ } from "./anthropic.js";