@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.
- package/.turbo/turbo-build.log +4 -0
- package/.turbo/turbo-check-types.log +5 -0
- package/.turbo/turbo-lint.log +5 -0
- package/dist/ai-sdk-integration.d.ts +123 -0
- package/dist/ai-sdk-integration.d.ts.map +1 -0
- package/dist/ai-sdk-integration.js +475 -0
- package/dist/anthropic.d.ts +54 -0
- package/dist/anthropic.d.ts.map +1 -0
- package/dist/anthropic.js +375 -0
- package/dist/claude-hooks.d.ts +328 -0
- package/dist/claude-hooks.d.ts.map +1 -0
- package/dist/claude-hooks.js +571 -0
- package/dist/claude-wrapper.d.ts +93 -0
- package/dist/claude-wrapper.d.ts.map +1 -0
- package/dist/claude-wrapper.js +154 -0
- package/dist/client.d.ts +157 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +394 -0
- package/dist/converters/ai-sdk.d.ts +65 -0
- package/dist/converters/ai-sdk.d.ts.map +1 -0
- package/dist/converters/ai-sdk.js +238 -0
- package/dist/converters/claude.d.ts +146 -0
- package/dist/converters/claude.d.ts.map +1 -0
- package/dist/converters/claude.js +202 -0
- package/dist/converters/index.d.ts +9 -0
- package/dist/converters/index.d.ts.map +1 -0
- package/dist/converters/index.js +8 -0
- package/dist/converters/openai.d.ts +51 -0
- package/dist/converters/openai.d.ts.map +1 -0
- package/dist/converters/openai.js +268 -0
- package/dist/converters/openclaw.d.ts +16 -0
- package/dist/converters/openclaw.d.ts.map +1 -0
- package/dist/converters/openclaw.js +49 -0
- package/dist/converters/openinference.d.ts +107 -0
- package/dist/converters/openinference.d.ts.map +1 -0
- package/dist/converters/openinference.js +552 -0
- package/dist/converters/vercel.d.ts +21 -0
- package/dist/converters/vercel.d.ts.map +1 -0
- package/dist/converters/vercel.js +208 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +39 -0
- package/dist/langchain-handler.d.ts +101 -0
- package/dist/langchain-handler.d.ts.map +1 -0
- package/dist/langchain-handler.js +489 -0
- package/dist/mastra-exporter.d.ts +85 -0
- package/dist/mastra-exporter.d.ts.map +1 -0
- package/dist/mastra-exporter.js +471 -0
- package/dist/span-processor.d.ts +62 -0
- package/dist/span-processor.d.ts.map +1 -0
- package/dist/span-processor.js +310 -0
- package/dist/tracing-processor.d.ts +136 -0
- package/dist/tracing-processor.d.ts.map +1 -0
- package/dist/tracing-processor.js +368 -0
- package/dist/types/genai.d.ts +165 -0
- package/dist/types/genai.d.ts.map +1 -0
- package/dist/types/genai.js +82 -0
- package/dist/types.d.ts +7 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/utils.d.ts +19 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +57 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +2 -0
- package/eslint.config.mjs +4 -0
- package/package.json +78 -0
- package/src/ai-sdk-integration.ts +689 -0
- package/src/anthropic.ts +465 -0
- package/src/claude-hooks.ts +1010 -0
- package/src/claude-wrapper.ts +240 -0
- package/src/client.ts +513 -0
- package/src/converters/ai-sdk.ts +298 -0
- package/src/converters/claude.ts +334 -0
- package/src/converters/index.ts +44 -0
- package/src/converters/openai.ts +302 -0
- package/src/converters/openinference.ts +673 -0
- package/src/converters/vercel.ts +264 -0
- package/src/index.ts +121 -0
- package/src/langchain-handler.ts +722 -0
- package/src/mastra-exporter.ts +650 -0
- package/src/span-processor.ts +418 -0
- package/src/tracing-processor.ts +548 -0
- package/src/types/genai.ts +264 -0
- package/src/types.ts +13 -0
- package/src/utils.ts +69 -0
- package/src/version.ts +2 -0
- package/tsconfig.json +8 -0
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converter for AI SDK event data → Gen AI semantic conventions.
|
|
3
|
+
*
|
|
4
|
+
* Converts typed event objects (messages, tool definitions, step results)
|
|
5
|
+
* from the AI SDK's TelemetryIntegration callbacks into the standardized
|
|
6
|
+
* gen_ai format used by the Introspection backend.
|
|
7
|
+
*
|
|
8
|
+
* Used by {@link IntrospectionAISDKIntegration}.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type {
|
|
12
|
+
InputMessage,
|
|
13
|
+
OutputMessage,
|
|
14
|
+
SystemInstruction,
|
|
15
|
+
ToolDefinition,
|
|
16
|
+
MessagePart,
|
|
17
|
+
} from "../types/genai.js";
|
|
18
|
+
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Local types — minimal shapes matching AI SDK data structures.
|
|
21
|
+
// Defined locally to avoid requiring `ai` as a compile-time dependency.
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
|
|
24
|
+
/** A content part within an AI SDK message. */
|
|
25
|
+
interface ContentPart {
|
|
26
|
+
type: string;
|
|
27
|
+
text?: string;
|
|
28
|
+
toolCallId?: string;
|
|
29
|
+
toolName?: string;
|
|
30
|
+
args?: unknown;
|
|
31
|
+
input?: unknown;
|
|
32
|
+
result?: unknown;
|
|
33
|
+
output?: unknown;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// ---------------------------------------------------------------------------
|
|
37
|
+
// Public conversion functions
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Convert AI SDK messages (ModelMessage[]) to gen_ai InputMessage[].
|
|
42
|
+
* System messages are excluded — they're handled separately via
|
|
43
|
+
* {@link extractSystemInstructions}.
|
|
44
|
+
*
|
|
45
|
+
* @param messages - Array of AI SDK ModelMessage objects.
|
|
46
|
+
* @returns Array of gen_ai InputMessage objects (without system messages).
|
|
47
|
+
*/
|
|
48
|
+
export function convertMessagesToInputMessages(
|
|
49
|
+
messages: readonly unknown[],
|
|
50
|
+
): InputMessage[] {
|
|
51
|
+
const result: InputMessage[] = [];
|
|
52
|
+
for (const msg of messages) {
|
|
53
|
+
const m = msg as { role?: string; content?: unknown };
|
|
54
|
+
if (!m || !m.role || m.role === "system") continue;
|
|
55
|
+
result.push(convertOneMessage(m));
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Extract gen_ai system instructions from AI SDK's system field and messages.
|
|
62
|
+
*
|
|
63
|
+
* The AI SDK provides system prompts in two places:
|
|
64
|
+
* - The `system` parameter (string, SystemModelMessage, or array)
|
|
65
|
+
* - Messages with role "system"
|
|
66
|
+
*
|
|
67
|
+
* @param system - The AI SDK `system` field.
|
|
68
|
+
* @param messages - The messages array (may contain system-role messages).
|
|
69
|
+
* @returns System instructions in gen_ai format, or undefined if none.
|
|
70
|
+
*/
|
|
71
|
+
export function extractSystemInstructions(
|
|
72
|
+
system: unknown,
|
|
73
|
+
messages: readonly unknown[],
|
|
74
|
+
): SystemInstruction[] | undefined {
|
|
75
|
+
const instructions: SystemInstruction[] = [];
|
|
76
|
+
|
|
77
|
+
// From the `system` field
|
|
78
|
+
if (typeof system === "string" && system) {
|
|
79
|
+
instructions.push({ type: "text", content: system });
|
|
80
|
+
} else if (system && typeof system === "object") {
|
|
81
|
+
if (Array.isArray(system)) {
|
|
82
|
+
for (const s of system) {
|
|
83
|
+
const text = extractTextFromSystemMessage(s);
|
|
84
|
+
if (text) instructions.push({ type: "text", content: text });
|
|
85
|
+
}
|
|
86
|
+
} else {
|
|
87
|
+
const text = extractTextFromSystemMessage(system);
|
|
88
|
+
if (text) instructions.push({ type: "text", content: text });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// From system-role messages
|
|
93
|
+
for (const msg of messages) {
|
|
94
|
+
const m = msg as { role?: string; content?: unknown };
|
|
95
|
+
if (m?.role === "system") {
|
|
96
|
+
const content = m.content;
|
|
97
|
+
if (typeof content === "string") {
|
|
98
|
+
instructions.push({ type: "text", content });
|
|
99
|
+
} else if (Array.isArray(content)) {
|
|
100
|
+
const text = (content as ContentPart[])
|
|
101
|
+
.filter((p) => p.type === "text")
|
|
102
|
+
.map((p) => p.text || "")
|
|
103
|
+
.join("");
|
|
104
|
+
if (text) instructions.push({ type: "text", content: text });
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return instructions.length > 0 ? instructions : undefined;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Build gen_ai output messages from AI SDK step result data.
|
|
114
|
+
*
|
|
115
|
+
* Combines text, reasoning, and tool call outputs into a single assistant
|
|
116
|
+
* OutputMessage, mirroring the order used by vercel.ts (reasoning → text → tool calls).
|
|
117
|
+
*
|
|
118
|
+
* @param options - Step result fields.
|
|
119
|
+
* @returns Array of gen_ai OutputMessage objects.
|
|
120
|
+
*/
|
|
121
|
+
export function buildOutputMessages(options: {
|
|
122
|
+
text?: string;
|
|
123
|
+
reasoningText?: string;
|
|
124
|
+
reasoning?: readonly { text?: string }[];
|
|
125
|
+
toolCalls?: readonly {
|
|
126
|
+
toolCallId: string;
|
|
127
|
+
toolName: string;
|
|
128
|
+
input: unknown;
|
|
129
|
+
}[];
|
|
130
|
+
finishReason?: string;
|
|
131
|
+
}): OutputMessage[] {
|
|
132
|
+
const parts: MessagePart[] = [];
|
|
133
|
+
|
|
134
|
+
// Reasoning first (mirrors vercel.ts order)
|
|
135
|
+
if (options.reasoningText) {
|
|
136
|
+
parts.push({ type: "thinking", content: options.reasoningText });
|
|
137
|
+
} else if (options.reasoning?.length) {
|
|
138
|
+
for (const r of options.reasoning) {
|
|
139
|
+
if (r.text) parts.push({ type: "thinking", content: r.text });
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Text content
|
|
144
|
+
if (options.text) {
|
|
145
|
+
parts.push({ type: "text", content: options.text });
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Tool calls
|
|
149
|
+
if (options.toolCalls?.length) {
|
|
150
|
+
for (const tc of options.toolCalls) {
|
|
151
|
+
parts.push({
|
|
152
|
+
type: "tool_call",
|
|
153
|
+
name: tc.toolName,
|
|
154
|
+
id: tc.toolCallId,
|
|
155
|
+
arguments:
|
|
156
|
+
typeof tc.input === "string" ? tc.input : JSON.stringify(tc.input),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
if (parts.length === 0) return [];
|
|
162
|
+
|
|
163
|
+
return [
|
|
164
|
+
{
|
|
165
|
+
role: "assistant",
|
|
166
|
+
parts,
|
|
167
|
+
finish_reason: options.finishReason,
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Convert AI SDK ToolSet to gen_ai ToolDefinition[].
|
|
174
|
+
*
|
|
175
|
+
* The AI SDK represents tools as `Record<string, Tool>` where each Tool has
|
|
176
|
+
* description and parameters (usually a Zod schema). We extract what we can
|
|
177
|
+
* without importing Zod.
|
|
178
|
+
*
|
|
179
|
+
* @param tools - AI SDK ToolSet (Record<string, Tool>).
|
|
180
|
+
* @returns Array of gen_ai ToolDefinition objects, or undefined if no tools.
|
|
181
|
+
*/
|
|
182
|
+
export function convertToolsToToolDefinitions(
|
|
183
|
+
tools: unknown,
|
|
184
|
+
): ToolDefinition[] | undefined {
|
|
185
|
+
if (!tools || typeof tools !== "object") return undefined;
|
|
186
|
+
|
|
187
|
+
const defs: ToolDefinition[] = [];
|
|
188
|
+
for (const [name, tool] of Object.entries(tools as Record<string, unknown>)) {
|
|
189
|
+
if (!tool || typeof tool !== "object") continue;
|
|
190
|
+
const t = tool as Record<string, unknown>;
|
|
191
|
+
defs.push({
|
|
192
|
+
name,
|
|
193
|
+
description:
|
|
194
|
+
typeof t.description === "string" ? t.description : undefined,
|
|
195
|
+
parameters: extractToolParameters(t.parameters),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return defs.length > 0 ? defs : undefined;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ---------------------------------------------------------------------------
|
|
203
|
+
// Internal helpers
|
|
204
|
+
// ---------------------------------------------------------------------------
|
|
205
|
+
|
|
206
|
+
function convertOneMessage(msg: {
|
|
207
|
+
role?: string;
|
|
208
|
+
content?: unknown;
|
|
209
|
+
}): InputMessage {
|
|
210
|
+
const role = (msg.role || "user") as InputMessage["role"];
|
|
211
|
+
const content = msg.content;
|
|
212
|
+
const parts: MessagePart[] = [];
|
|
213
|
+
|
|
214
|
+
if (typeof content === "string") {
|
|
215
|
+
parts.push({ type: "text", content });
|
|
216
|
+
} else if (Array.isArray(content)) {
|
|
217
|
+
for (const part of content as ContentPart[]) {
|
|
218
|
+
const converted = convertContentPart(part);
|
|
219
|
+
if (converted) parts.push(converted);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return { role, parts };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function convertContentPart(part: ContentPart): MessagePart | null {
|
|
227
|
+
switch (part.type) {
|
|
228
|
+
case "text":
|
|
229
|
+
return { type: "text", content: part.text || "" };
|
|
230
|
+
|
|
231
|
+
case "tool-call": {
|
|
232
|
+
const args = part.args ?? part.input;
|
|
233
|
+
return {
|
|
234
|
+
type: "tool_call",
|
|
235
|
+
name: part.toolName || "",
|
|
236
|
+
id: part.toolCallId || "",
|
|
237
|
+
arguments: typeof args === "string" ? args : JSON.stringify(args ?? {}),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
case "tool-result": {
|
|
242
|
+
const rawResult = part.result ?? part.output;
|
|
243
|
+
let response: string;
|
|
244
|
+
if (typeof rawResult === "string") {
|
|
245
|
+
response = rawResult;
|
|
246
|
+
} else if (
|
|
247
|
+
rawResult &&
|
|
248
|
+
typeof rawResult === "object" &&
|
|
249
|
+
"value" in (rawResult as Record<string, unknown>)
|
|
250
|
+
) {
|
|
251
|
+
response = JSON.stringify((rawResult as Record<string, unknown>).value);
|
|
252
|
+
} else {
|
|
253
|
+
response = JSON.stringify(rawResult);
|
|
254
|
+
}
|
|
255
|
+
return {
|
|
256
|
+
type: "tool_call_response",
|
|
257
|
+
id: part.toolCallId || "",
|
|
258
|
+
response,
|
|
259
|
+
};
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
case "reasoning":
|
|
263
|
+
return { type: "thinking", content: part.text || "" };
|
|
264
|
+
|
|
265
|
+
default:
|
|
266
|
+
// Unknown part type — serialize as text fallback
|
|
267
|
+
return { type: "text", content: JSON.stringify(part) };
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function extractTextFromSystemMessage(msg: unknown): string | null {
|
|
272
|
+
if (typeof msg === "string") return msg;
|
|
273
|
+
if (msg && typeof msg === "object") {
|
|
274
|
+
const m = msg as Record<string, unknown>;
|
|
275
|
+
if (typeof m.content === "string") return m.content;
|
|
276
|
+
if (typeof m.text === "string") return m.text;
|
|
277
|
+
}
|
|
278
|
+
return null;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function extractToolParameters(
|
|
282
|
+
parameters: unknown,
|
|
283
|
+
): Record<string, unknown> | undefined {
|
|
284
|
+
if (!parameters || typeof parameters !== "object") return undefined;
|
|
285
|
+
const p = parameters as Record<string, unknown>;
|
|
286
|
+
|
|
287
|
+
// jsonSchema() wrapper — has a jsonSchema property
|
|
288
|
+
if (p.jsonSchema && typeof p.jsonSchema === "object") {
|
|
289
|
+
return p.jsonSchema as Record<string, unknown>;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Plain JSON Schema object (has 'type' and 'properties')
|
|
293
|
+
if (p.type === "object" && p.properties) {
|
|
294
|
+
return p as Record<string, unknown>;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
return undefined;
|
|
298
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claude Agent SDK format conversion functions for OTel Gen AI Semantic Conventions.
|
|
3
|
+
*
|
|
4
|
+
* These functions convert Claude Agent SDK message formats to the standardized
|
|
5
|
+
* OTel Gen AI Semantic Convention format for gen_ai.input.messages and gen_ai.output.messages.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
InputMessage,
|
|
10
|
+
OutputMessage,
|
|
11
|
+
MessagePart,
|
|
12
|
+
ReasoningPart,
|
|
13
|
+
GenAiAttributes,
|
|
14
|
+
ToolDefinition,
|
|
15
|
+
} from "../types/genai.js";
|
|
16
|
+
import { toAttributes } from "../types/genai.js";
|
|
17
|
+
|
|
18
|
+
/** A plain-text content block from the Claude API. */
|
|
19
|
+
export interface ClaudeTextBlock {
|
|
20
|
+
type: "text";
|
|
21
|
+
text: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** A tool-use (function call) content block from the Claude API. */
|
|
25
|
+
export interface ClaudeToolUseBlock {
|
|
26
|
+
type: "tool_use";
|
|
27
|
+
id: string;
|
|
28
|
+
name: string;
|
|
29
|
+
input: unknown;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A tool-result content block returned to the Claude API. */
|
|
33
|
+
export interface ClaudeToolResultBlock {
|
|
34
|
+
type: "tool_result";
|
|
35
|
+
tool_use_id: string;
|
|
36
|
+
content: string | unknown[];
|
|
37
|
+
is_error?: boolean;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/** Union of all Claude API content block shapes. */
|
|
41
|
+
export type ClaudeContentBlock =
|
|
42
|
+
| ClaudeTextBlock
|
|
43
|
+
| ClaudeToolUseBlock
|
|
44
|
+
| ClaudeToolResultBlock
|
|
45
|
+
| { type: string; [key: string]: unknown };
|
|
46
|
+
|
|
47
|
+
/** A single message in a Claude multi-turn conversation. */
|
|
48
|
+
export interface ClaudeMessage {
|
|
49
|
+
role: "user" | "assistant";
|
|
50
|
+
content: string | ClaudeContentBlock[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Subset of the Claude Messages API response used for conversion. */
|
|
54
|
+
export interface ClaudeResponse {
|
|
55
|
+
id?: string;
|
|
56
|
+
model?: string;
|
|
57
|
+
content?: ClaudeContentBlock[];
|
|
58
|
+
stop_reason?: string;
|
|
59
|
+
usage?: {
|
|
60
|
+
input_tokens?: number;
|
|
61
|
+
output_tokens?: number;
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Convert a user prompt string to OTel Gen AI input message format.
|
|
67
|
+
*
|
|
68
|
+
* @param prompt - The user's text prompt.
|
|
69
|
+
* @returns An array containing a single user-role {@link InputMessage}.
|
|
70
|
+
*
|
|
71
|
+
* @example
|
|
72
|
+
* ```ts
|
|
73
|
+
* const msgs = convertClaudePromptToInputMessages("Hello, Claude!");
|
|
74
|
+
* // [{ role: "user", parts: [{ type: "text", content: "Hello, Claude!" }] }]
|
|
75
|
+
* ```
|
|
76
|
+
*/
|
|
77
|
+
export function convertClaudePromptToInputMessages(
|
|
78
|
+
prompt: string,
|
|
79
|
+
): InputMessage[] {
|
|
80
|
+
return [
|
|
81
|
+
{
|
|
82
|
+
role: "user",
|
|
83
|
+
parts: [{ type: "text", content: prompt }],
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Convert Claude content blocks to message parts.
|
|
90
|
+
*/
|
|
91
|
+
function convertContentBlocksToParts(
|
|
92
|
+
content: string | ClaudeContentBlock[],
|
|
93
|
+
): MessagePart[] {
|
|
94
|
+
if (typeof content === "string") {
|
|
95
|
+
return [{ type: "text", content }];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const parts: MessagePart[] = [];
|
|
99
|
+
|
|
100
|
+
for (const block of content) {
|
|
101
|
+
if (block.type === "text" && "text" in block) {
|
|
102
|
+
parts.push({ type: "text", content: block.text as string });
|
|
103
|
+
} else if (block.type === "tool_use" && "name" in block) {
|
|
104
|
+
parts.push({
|
|
105
|
+
type: "tool_call",
|
|
106
|
+
id: block.id as string,
|
|
107
|
+
name: block.name as string,
|
|
108
|
+
arguments: block.input,
|
|
109
|
+
});
|
|
110
|
+
} else if (block.type === "tool_result" && "tool_use_id" in block) {
|
|
111
|
+
let response: unknown = block.content;
|
|
112
|
+
if (typeof response === "string") {
|
|
113
|
+
// Keep as string
|
|
114
|
+
} else if (Array.isArray(response)) {
|
|
115
|
+
// Extract text from content array
|
|
116
|
+
response = response
|
|
117
|
+
.filter(
|
|
118
|
+
(item): item is { type: "text"; text: string } =>
|
|
119
|
+
typeof item === "object" &&
|
|
120
|
+
item !== null &&
|
|
121
|
+
(item as Record<string, unknown>).type === "text",
|
|
122
|
+
)
|
|
123
|
+
.map((item) => item.text)
|
|
124
|
+
.join("");
|
|
125
|
+
}
|
|
126
|
+
parts.push({
|
|
127
|
+
type: "tool_call_response",
|
|
128
|
+
id: block.tool_use_id as string,
|
|
129
|
+
response,
|
|
130
|
+
});
|
|
131
|
+
} else if (block.type === "thinking" && "thinking" in block) {
|
|
132
|
+
const thinking = block.thinking as string | undefined;
|
|
133
|
+
const signature = (block as Record<string, unknown>).signature as
|
|
134
|
+
| string
|
|
135
|
+
| undefined;
|
|
136
|
+
const thinkingPart: ReasoningPart = {
|
|
137
|
+
type: "thinking",
|
|
138
|
+
content: thinking || undefined,
|
|
139
|
+
signature: signature || undefined,
|
|
140
|
+
provider_name: "anthropic",
|
|
141
|
+
};
|
|
142
|
+
parts.push(thinkingPart);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return parts;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Convert Claude assistant response content to OTel Gen AI output message format.
|
|
151
|
+
*
|
|
152
|
+
* @param content - A plain string or array of {@link ClaudeContentBlock} objects.
|
|
153
|
+
* @param stopReason - Optional stop/finish reason (e.g. `"end_turn"`).
|
|
154
|
+
* @returns An array containing a single assistant-role {@link OutputMessage}.
|
|
155
|
+
*
|
|
156
|
+
* @example
|
|
157
|
+
* ```ts
|
|
158
|
+
* const msgs = convertClaudeResponseToOutputMessages(response.content, response.stop_reason);
|
|
159
|
+
* span.setAttribute("gen_ai.output.messages", JSON.stringify(msgs));
|
|
160
|
+
* ```
|
|
161
|
+
*/
|
|
162
|
+
export function convertClaudeResponseToOutputMessages(
|
|
163
|
+
content: string | ClaudeContentBlock[],
|
|
164
|
+
stopReason?: string,
|
|
165
|
+
): OutputMessage[] {
|
|
166
|
+
const parts = convertContentBlocksToParts(content);
|
|
167
|
+
|
|
168
|
+
const message: OutputMessage = {
|
|
169
|
+
role: "assistant",
|
|
170
|
+
parts,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
if (stopReason) {
|
|
174
|
+
message.finish_reason = stopReason;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return [message];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Convert a Claude multi-turn conversation to OTel Gen AI input message format.
|
|
182
|
+
*
|
|
183
|
+
* @param messages - Array of {@link ClaudeMessage} objects (user + assistant turns).
|
|
184
|
+
* @returns An array of {@link InputMessage} objects preserving turn order.
|
|
185
|
+
*
|
|
186
|
+
* @example
|
|
187
|
+
* ```ts
|
|
188
|
+
* const msgs = convertClaudeMessagesToInputMessages(conversation);
|
|
189
|
+
* span.setAttribute("gen_ai.input.messages", JSON.stringify(msgs));
|
|
190
|
+
* ```
|
|
191
|
+
*/
|
|
192
|
+
export function convertClaudeMessagesToInputMessages(
|
|
193
|
+
messages: ClaudeMessage[],
|
|
194
|
+
): InputMessage[] {
|
|
195
|
+
return messages.map((msg) => ({
|
|
196
|
+
role: msg.role as InputMessage["role"],
|
|
197
|
+
parts: convertContentBlocksToParts(msg.content),
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Aggregated session data from the Claude Agent SDK, used as input for
|
|
203
|
+
* the {@link convertClaudeSessionToGenAI} and
|
|
204
|
+
* {@link convertClaudeSessionToOtelAttributes} converters.
|
|
205
|
+
*/
|
|
206
|
+
export interface ClaudeSessionData {
|
|
207
|
+
/** Input prompt */
|
|
208
|
+
inputPrompt?: string;
|
|
209
|
+
/** Response content (text extracted from assistant messages) */
|
|
210
|
+
responseContent?: string;
|
|
211
|
+
/** Full response content blocks (if available) */
|
|
212
|
+
responseBlocks?: ClaudeContentBlock[];
|
|
213
|
+
/** Model name */
|
|
214
|
+
model?: string;
|
|
215
|
+
/** Session ID */
|
|
216
|
+
sessionId?: string;
|
|
217
|
+
/** Input token count */
|
|
218
|
+
inputTokens?: number;
|
|
219
|
+
/** Output token count */
|
|
220
|
+
outputTokens?: number;
|
|
221
|
+
/** Cost in USD */
|
|
222
|
+
costUsd?: number;
|
|
223
|
+
/** Stop/finish reason */
|
|
224
|
+
stopReason?: string;
|
|
225
|
+
/** Response ID */
|
|
226
|
+
responseId?: string;
|
|
227
|
+
/** Tool names from init message */
|
|
228
|
+
toolNames?: string[];
|
|
229
|
+
/** System instructions / system prompt */
|
|
230
|
+
systemInstructions?: string;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Convert Claude session data to camelCase {@link GenAiAttributes}.
|
|
235
|
+
*
|
|
236
|
+
* @param data - Aggregated {@link ClaudeSessionData} for one session.
|
|
237
|
+
* @returns A {@link GenAiAttributes} object ready for {@link toAttributes}.
|
|
238
|
+
*
|
|
239
|
+
* @example
|
|
240
|
+
* ```ts
|
|
241
|
+
* const genAi = convertClaudeSessionToGenAI({ model: "claude-sonnet-4-20250514", inputTokens: 100 });
|
|
242
|
+
* const otel = toAttributes(genAi);
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
245
|
+
export function convertClaudeSessionToGenAI(
|
|
246
|
+
data: ClaudeSessionData,
|
|
247
|
+
): GenAiAttributes {
|
|
248
|
+
const result: GenAiAttributes = {};
|
|
249
|
+
|
|
250
|
+
// Set system
|
|
251
|
+
result.system = "anthropic";
|
|
252
|
+
|
|
253
|
+
// Set model
|
|
254
|
+
if (data.model) {
|
|
255
|
+
result.requestModel = data.model;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// Convert input to gen_ai.input.messages format
|
|
259
|
+
if (data.inputPrompt) {
|
|
260
|
+
result.inputMessages = convertClaudePromptToInputMessages(data.inputPrompt);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Convert output to gen_ai.output.messages format
|
|
264
|
+
if (data.responseBlocks) {
|
|
265
|
+
result.outputMessages = convertClaudeResponseToOutputMessages(
|
|
266
|
+
data.responseBlocks,
|
|
267
|
+
data.stopReason,
|
|
268
|
+
);
|
|
269
|
+
} else if (data.responseContent) {
|
|
270
|
+
result.outputMessages = convertClaudeResponseToOutputMessages(
|
|
271
|
+
data.responseContent,
|
|
272
|
+
data.stopReason,
|
|
273
|
+
);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Set token counts
|
|
277
|
+
if (data.inputTokens !== undefined) {
|
|
278
|
+
result.inputTokens = data.inputTokens;
|
|
279
|
+
}
|
|
280
|
+
if (data.outputTokens !== undefined) {
|
|
281
|
+
result.outputTokens = data.outputTokens;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Set response ID
|
|
285
|
+
if (data.responseId) {
|
|
286
|
+
result.responseId = data.responseId;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
// Set tool definitions from tool names
|
|
290
|
+
if (data.toolNames && data.toolNames.length > 0) {
|
|
291
|
+
result.toolDefinitions = data.toolNames.map(
|
|
292
|
+
(name): ToolDefinition => ({ name }),
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Set system instructions
|
|
297
|
+
if (data.systemInstructions) {
|
|
298
|
+
result.systemInstructions = [
|
|
299
|
+
{ type: "text" as const, content: data.systemInstructions },
|
|
300
|
+
];
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Convert Claude session data directly to OTel span attributes with `gen_ai.*`
|
|
308
|
+
* dot-notation keys, plus Claude-specific keys like `claude.session_id`.
|
|
309
|
+
*
|
|
310
|
+
* @param data - Aggregated {@link ClaudeSessionData} for one session.
|
|
311
|
+
* @returns A flat dictionary suitable for `span.setAttributes()`.
|
|
312
|
+
*
|
|
313
|
+
* @example
|
|
314
|
+
* ```ts
|
|
315
|
+
* const attrs = convertClaudeSessionToOtelAttributes(sessionData);
|
|
316
|
+
* for (const [k, v] of Object.entries(attrs)) span.setAttribute(k, v);
|
|
317
|
+
* ```
|
|
318
|
+
*/
|
|
319
|
+
export function convertClaudeSessionToOtelAttributes(
|
|
320
|
+
data: ClaudeSessionData,
|
|
321
|
+
): Record<string, string | number> {
|
|
322
|
+
const genaiAttrs = convertClaudeSessionToGenAI(data);
|
|
323
|
+
const result = toAttributes(genaiAttrs);
|
|
324
|
+
|
|
325
|
+
// Add Claude-specific attributes
|
|
326
|
+
if (data.sessionId) {
|
|
327
|
+
result["claude.session_id"] = data.sessionId;
|
|
328
|
+
}
|
|
329
|
+
if (data.costUsd !== undefined) {
|
|
330
|
+
result["gen_ai.cost.usd"] = data.costUsd;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return result;
|
|
334
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converters for transforming various API formats to OTel Gen AI Semantic Conventions.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export {
|
|
6
|
+
convertResponsesInputsToSemconv,
|
|
7
|
+
convertResponsesOutputsToSemconv,
|
|
8
|
+
convertResponsesToolsToSemconv,
|
|
9
|
+
convertResponsesInstructionsToSemconv,
|
|
10
|
+
type ResponseInputItem,
|
|
11
|
+
type ResponseOutputItem,
|
|
12
|
+
type ResponseTool,
|
|
13
|
+
type ResponseUsage,
|
|
14
|
+
type Response as OpenAIResponse,
|
|
15
|
+
} from "./openai.js";
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
isOpenInferenceSpan,
|
|
19
|
+
convertOpenInferenceToGenAI,
|
|
20
|
+
replaceOpenInferenceWithGenAI,
|
|
21
|
+
addOpenInferenceAttributes,
|
|
22
|
+
OpenInferenceSpanExporter,
|
|
23
|
+
} from "./openinference.js";
|
|
24
|
+
|
|
25
|
+
export { isVercelAISpan, convertVercelAIToGenAI } from "./vercel.js";
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
convertMessagesToInputMessages as convertAISDKMessagesToInputMessages,
|
|
29
|
+
extractSystemInstructions as extractAISDKSystemInstructions,
|
|
30
|
+
buildOutputMessages as buildAISDKOutputMessages,
|
|
31
|
+
convertToolsToToolDefinitions as convertAISDKToolsToToolDefinitions,
|
|
32
|
+
} from "./ai-sdk.js";
|
|
33
|
+
|
|
34
|
+
export {
|
|
35
|
+
convertClaudePromptToInputMessages,
|
|
36
|
+
convertClaudeResponseToOutputMessages,
|
|
37
|
+
convertClaudeMessagesToInputMessages,
|
|
38
|
+
convertClaudeSessionToGenAI,
|
|
39
|
+
convertClaudeSessionToOtelAttributes,
|
|
40
|
+
type ClaudeContentBlock,
|
|
41
|
+
type ClaudeMessage,
|
|
42
|
+
type ClaudeResponse,
|
|
43
|
+
type ClaudeSessionData,
|
|
44
|
+
} from "./claude.js";
|