@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
package/dist/client.js ADDED
@@ -0,0 +1,394 @@
1
+ /**
2
+ * Introspection Client for Node.js
3
+ * Provides an API for tracking events and feedback using OTLP Logs.
4
+ *
5
+ * Identity and gen_ai context are automatically extracted from OpenTelemetry
6
+ * baggage.
7
+ */
8
+ import { SeverityNumber } from "@opentelemetry/api-logs";
9
+ import { LoggerProvider, BatchLogRecordProcessor, } from "@opentelemetry/sdk-logs";
10
+ import { OTLPLogExporter } from "@opentelemetry/exporter-logs-otlp-proto";
11
+ import { resourceFromAttributes, defaultResource, } from "@opentelemetry/resources";
12
+ import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
13
+ import { propagation, context } from "@opentelemetry/api";
14
+ import { logger as sdkLogger } from "./utils.js";
15
+ import { VERSION } from "./version.js";
16
+ import { generateEventId, } from "./types.js";
17
+ /**
18
+ * Introspection client for tracking events and feedback using OTLP Logs.
19
+ *
20
+ * @example
21
+ * ```typescript
22
+ * const client = new IntrospectionClient({
23
+ * token: process.env.INTROSPECTION_TOKEN,
24
+ * });
25
+ *
26
+ * client.identify("user_123", { email: "user@example.com" });
27
+ * client.feedback("thumbs_up", { comments: "Great response!" });
28
+ *
29
+ * await client.shutdown();
30
+ * ```
31
+ */
32
+ export class IntrospectionClient {
33
+ loggerProvider;
34
+ otelLogger;
35
+ userId;
36
+ anonymousId;
37
+ traits = {};
38
+ constructor(options = {}) {
39
+ const token = options.token || process.env.INTROSPECTION_TOKEN || "";
40
+ const serviceName = options.serviceName ||
41
+ process.env.INTROSPECTION_SERVICE_NAME ||
42
+ "introspection-client";
43
+ const advanced = options.advanced || {};
44
+ const baseUrl = advanced.baseUrl ||
45
+ process.env.INTROSPECTION_BASE_URL ||
46
+ "https://otel.introspection.dev";
47
+ if (!token) {
48
+ sdkLogger.warn("IntrospectionClient: No token provided. Events will not be sent.");
49
+ }
50
+ // Construct endpoint URL for logs
51
+ let endpoint;
52
+ if (baseUrl.endsWith("/v1/logs")) {
53
+ endpoint = baseUrl;
54
+ }
55
+ else {
56
+ endpoint = `${baseUrl.replace(/\/$/, "")}/v1/logs`;
57
+ }
58
+ // Build headers
59
+ const headers = {
60
+ Authorization: `Bearer ${token}`,
61
+ };
62
+ if (advanced.additionalHeaders) {
63
+ Object.assign(headers, advanced.additionalHeaders);
64
+ }
65
+ // Create OTLP log exporter
66
+ const exporter = new OTLPLogExporter({
67
+ url: endpoint,
68
+ headers,
69
+ });
70
+ // Create batch processor
71
+ const processor = new BatchLogRecordProcessor(exporter, {
72
+ maxQueueSize: advanced.maxBatchSize ?? 100,
73
+ scheduledDelayMillis: advanced.flushInterval ?? 5000,
74
+ });
75
+ // Get default resource and merge with our service name
76
+ const baseResource = defaultResource();
77
+ const introspectionResource = resourceFromAttributes({
78
+ [ATTR_SERVICE_NAME]: serviceName,
79
+ });
80
+ const resource = baseResource.merge(introspectionResource);
81
+ // Create logger provider
82
+ this.loggerProvider = new LoggerProvider({
83
+ resource,
84
+ processors: [processor],
85
+ });
86
+ this.otelLogger = this.loggerProvider.getLogger("@introspection-sdk/introspection-node", VERSION);
87
+ sdkLogger.info(`IntrospectionClient initialized: service=${serviceName}, endpoint=${endpoint}`);
88
+ }
89
+ /** Get gen_ai context from baggage */
90
+ getGenAiFromContext() {
91
+ const baggage = propagation.getBaggage(context.active());
92
+ return {
93
+ conversationId: baggage?.getEntry("gen_ai.conversation.id")?.value,
94
+ previousResponseId: baggage?.getEntry("gen_ai.request.previous_response_id")?.value,
95
+ agentName: baggage?.getEntry("gen_ai.agent.name")?.value,
96
+ agentId: baggage?.getEntry("gen_ai.agent.id")?.value,
97
+ };
98
+ }
99
+ /** Get identity from baggage or client instance */
100
+ getIdentityFromContext() {
101
+ const baggage = propagation.getBaggage(context.active());
102
+ return {
103
+ userId: baggage?.getEntry("identity.user_id")?.value || this.userId,
104
+ anonymousId: baggage?.getEntry("identity.anonymous_id")?.value || this.anonymousId,
105
+ };
106
+ }
107
+ /**
108
+ * Get current timestamp as OpenTelemetry HrTime.
109
+ * Uses process.hrtime.bigint() for nanosecond precision in Node.js.
110
+ */
111
+ getTimestamp() {
112
+ const hrTimeNs = process.hrtime.bigint();
113
+ const epochNs = BigInt(Date.now()) * BigInt(1_000_000);
114
+ const offsetNs = hrTimeNs - process.hrtime.bigint() + epochNs;
115
+ const seconds = Number(offsetNs / BigInt(1_000_000_000));
116
+ const nanos = Number(offsetNs % BigInt(1_000_000_000));
117
+ return [seconds, nanos];
118
+ }
119
+ /**
120
+ * Build log record attributes.
121
+ */
122
+ buildAttributes(eventName, options = {}) {
123
+ const { properties, traits, conversationId, previousResponseId, eventId } = options;
124
+ // Core fields
125
+ const attributes = {
126
+ "event.name": eventName,
127
+ "event.id": eventId || generateEventId(),
128
+ };
129
+ // Identity
130
+ const identity = this.getIdentityFromContext();
131
+ if (identity.userId) {
132
+ attributes["identity.user.id"] = identity.userId;
133
+ }
134
+ if (identity.anonymousId) {
135
+ attributes["identity.anonymous.id"] = identity.anonymousId;
136
+ }
137
+ // Gen AI context (explicit params override baggage)
138
+ const genAi = this.getGenAiFromContext();
139
+ const finalConversationId = conversationId || genAi.conversationId;
140
+ const finalPreviousResponseId = previousResponseId || genAi.previousResponseId;
141
+ if (finalConversationId) {
142
+ attributes["gen_ai.conversation.id"] = finalConversationId;
143
+ }
144
+ if (finalPreviousResponseId) {
145
+ attributes["gen_ai.request.previous_response_id"] =
146
+ finalPreviousResponseId;
147
+ }
148
+ if (genAi.agentName) {
149
+ attributes["gen_ai.agent.name"] = genAi.agentName;
150
+ }
151
+ if (genAi.agentId) {
152
+ attributes["gen_ai.agent.id"] = genAi.agentId;
153
+ }
154
+ // Flatten properties with "properties." prefix
155
+ if (properties) {
156
+ for (const [key, value] of Object.entries(properties)) {
157
+ if (value != null) {
158
+ const attrKey = `properties.${key}`;
159
+ if (typeof value === "object") {
160
+ attributes[attrKey] = JSON.stringify(value);
161
+ }
162
+ else if (typeof value === "string" ||
163
+ typeof value === "number" ||
164
+ typeof value === "boolean") {
165
+ attributes[attrKey] = value;
166
+ }
167
+ }
168
+ }
169
+ }
170
+ // Flatten traits with "context.traits." prefix
171
+ if (traits) {
172
+ for (const [key, value] of Object.entries(traits)) {
173
+ if (value != null) {
174
+ const attrKey = `context.traits.${key}`;
175
+ if (typeof value === "object") {
176
+ attributes[attrKey] = JSON.stringify(value);
177
+ }
178
+ else if (typeof value === "string" ||
179
+ typeof value === "number" ||
180
+ typeof value === "boolean") {
181
+ attributes[attrKey] = value;
182
+ }
183
+ }
184
+ }
185
+ }
186
+ return attributes;
187
+ }
188
+ /**
189
+ * Track a custom event.
190
+ *
191
+ * @param eventName - A descriptive name for the event (e.g. `"page_view"`).
192
+ * @param properties - Arbitrary key/value pairs attached to the event.
193
+ * @param options - Optional overrides such as a custom `eventId`.
194
+ */
195
+ track(eventName, properties, options) {
196
+ const attributes = this.buildAttributes(eventName, {
197
+ properties,
198
+ eventId: options?.eventId,
199
+ });
200
+ this.otelLogger.emit({
201
+ timestamp: this.getTimestamp(),
202
+ context: context.active(),
203
+ severityNumber: SeverityNumber.INFO,
204
+ severityText: "INFO",
205
+ attributes,
206
+ });
207
+ sdkLogger.debug(`Tracked: ${eventName}`);
208
+ }
209
+ /**
210
+ * Track feedback on a message or response.
211
+ *
212
+ * @param name - Feedback type (e.g. `"thumbs_up"`, `"thumbs_down"`).
213
+ * @param options - Additional feedback context such as comments or conversation ID.
214
+ */
215
+ feedback(name, options = {}) {
216
+ const { comments, conversationId, previousResponseId, eventId, ...extra } = options;
217
+ const properties = { name, ...extra };
218
+ if (comments) {
219
+ properties.comments = comments;
220
+ }
221
+ const attributes = this.buildAttributes("introspection.feedback", {
222
+ properties,
223
+ conversationId: conversationId,
224
+ previousResponseId: previousResponseId,
225
+ eventId: eventId,
226
+ });
227
+ this.otelLogger.emit({
228
+ timestamp: this.getTimestamp(),
229
+ context: context.active(),
230
+ severityNumber: SeverityNumber.INFO,
231
+ severityText: "INFO",
232
+ attributes,
233
+ });
234
+ sdkLogger.debug(`Feedback: ${name}`);
235
+ }
236
+ /**
237
+ * Identify a user and associate traits with them.
238
+ *
239
+ * @param userId - Unique identifier for the user.
240
+ * @param traits - Key/value user properties (e.g. `{ email: "…" }`).
241
+ * @param anonymousId - Optional anonymous identifier to link pre-auth sessions.
242
+ * @param eventId - Optional custom event ID override.
243
+ */
244
+ identify(userId, traits, anonymousId, eventId) {
245
+ this.userId = userId;
246
+ if (anonymousId) {
247
+ this.anonymousId = anonymousId;
248
+ }
249
+ if (traits) {
250
+ this.traits = { ...this.traits, ...traits };
251
+ }
252
+ const attributes = this.buildAttributes("identify", { traits, eventId });
253
+ this.otelLogger.emit({
254
+ timestamp: this.getTimestamp(),
255
+ context: context.active(),
256
+ severityNumber: SeverityNumber.INFO,
257
+ severityText: "INFO",
258
+ attributes,
259
+ });
260
+ sdkLogger.debug(`Identified: ${userId}`);
261
+ }
262
+ /**
263
+ * Create an OTel context with the given baggage values set.
264
+ *
265
+ * @param values - Key/value pairs to store in OTel baggage.
266
+ * @returns A new context containing the baggage entries.
267
+ */
268
+ createBaggageContext(values) {
269
+ const ctx = context.active();
270
+ let bag = propagation.getBaggage(ctx) || propagation.createBaggage();
271
+ for (const [key, value] of Object.entries(values)) {
272
+ bag = bag.setEntry(key, { value });
273
+ }
274
+ return propagation.setBaggage(ctx, bag);
275
+ }
276
+ /**
277
+ * Set multiple baggage values and run a callback within that context.
278
+ *
279
+ * @param values - Key/value pairs to store in OTel baggage.
280
+ * @param callback - Function to execute with the baggage context active.
281
+ * @returns The value returned by `callback`.
282
+ */
283
+ async withBaggage(values, callback) {
284
+ const ctx = this.createBaggageContext(values);
285
+ return await context.with(ctx, callback);
286
+ }
287
+ /**
288
+ * Set `gen_ai.agent.name` (and optionally `gen_ai.agent.id`) baggage and run
289
+ * a callback within that context.
290
+ *
291
+ * @param agentName - The agent name to propagate.
292
+ * @param agentId - Optional agent identifier.
293
+ * @param callback - Function to execute with the agent baggage active.
294
+ * @returns The value returned by `callback`.
295
+ */
296
+ async withAgent(agentName, agentId, callback) {
297
+ const values = { "gen_ai.agent.name": agentName };
298
+ if (agentId) {
299
+ values["gen_ai.agent.id"] = agentId;
300
+ }
301
+ return await context.with(this.createBaggageContext(values), callback);
302
+ }
303
+ /**
304
+ * Set conversation-related baggage (`gen_ai.conversation.id`,
305
+ * `gen_ai.request.previous_response_id`) and run a callback within that
306
+ * context.
307
+ *
308
+ * @param conversationId - The conversation identifier.
309
+ * @param previousResponseId - The ID of the previous response in the conversation.
310
+ * @param callback - Function to execute with the conversation baggage active.
311
+ * @returns The value returned by `callback`.
312
+ */
313
+ async withConversation(conversationId, previousResponseId, callback) {
314
+ const values = {};
315
+ if (conversationId) {
316
+ values["gen_ai.conversation.id"] = conversationId;
317
+ }
318
+ if (previousResponseId) {
319
+ values["gen_ai.request.previous_response_id"] = previousResponseId;
320
+ }
321
+ return await context.with(this.createBaggageContext(values), callback);
322
+ }
323
+ /**
324
+ * Set the user ID on this client instance.
325
+ *
326
+ * @param userId - Unique identifier for the user.
327
+ */
328
+ setUserId(userId) {
329
+ this.userId = userId;
330
+ }
331
+ /**
332
+ * Set the anonymous ID on this client instance.
333
+ *
334
+ * @param anonymousId - Anonymous identifier used before authentication.
335
+ */
336
+ setAnonymousId(anonymousId) {
337
+ this.anonymousId = anonymousId;
338
+ }
339
+ /**
340
+ * Set `identity.user_id` baggage and run a callback within that context.
341
+ *
342
+ * @param userId - The user identifier to propagate.
343
+ * @param callback - Function to execute with the user-ID baggage active.
344
+ * @returns The value returned by `callback`.
345
+ */
346
+ async withUserId(userId, callback) {
347
+ return await context.with(this.createBaggageContext({ "identity.user_id": userId }), callback);
348
+ }
349
+ /**
350
+ * Set `identity.anonymous_id` baggage and run a callback within that context.
351
+ *
352
+ * @param anonymousId - The anonymous identifier to propagate.
353
+ * @param callback - Function to execute with the anonymous-ID baggage active.
354
+ * @returns The value returned by `callback`.
355
+ */
356
+ async withAnonymousId(anonymousId, callback) {
357
+ return await context.with(this.createBaggageContext({ "identity.anonymous_id": anonymousId }), callback);
358
+ }
359
+ /**
360
+ * Get the current anonymous ID stored on this client instance.
361
+ *
362
+ * @returns The anonymous ID, or `undefined` if not set.
363
+ */
364
+ getAnonymousId() {
365
+ return this.anonymousId;
366
+ }
367
+ /**
368
+ * Reset all client-side state (user ID, anonymous ID, and traits).
369
+ */
370
+ reset() {
371
+ this.userId = undefined;
372
+ this.anonymousId = undefined;
373
+ this.traits = {};
374
+ sdkLogger.debug("Client state reset");
375
+ }
376
+ /**
377
+ * Flush all queued log records to the backend.
378
+ *
379
+ * @returns A promise that resolves once the flush completes.
380
+ */
381
+ async flush() {
382
+ await this.loggerProvider.forceFlush();
383
+ sdkLogger.debug("Flushed pending log records");
384
+ }
385
+ /**
386
+ * Shut down the client, flushing all pending log records.
387
+ *
388
+ * @returns A promise that resolves once shutdown is complete.
389
+ */
390
+ async shutdown() {
391
+ await this.loggerProvider.shutdown();
392
+ sdkLogger.debug("IntrospectionClient shutdown complete");
393
+ }
394
+ }
@@ -0,0 +1,65 @@
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
+ import type { InputMessage, OutputMessage, SystemInstruction, ToolDefinition } from "../types/genai.js";
11
+ /**
12
+ * Convert AI SDK messages (ModelMessage[]) to gen_ai InputMessage[].
13
+ * System messages are excluded — they're handled separately via
14
+ * {@link extractSystemInstructions}.
15
+ *
16
+ * @param messages - Array of AI SDK ModelMessage objects.
17
+ * @returns Array of gen_ai InputMessage objects (without system messages).
18
+ */
19
+ export declare function convertMessagesToInputMessages(messages: readonly unknown[]): InputMessage[];
20
+ /**
21
+ * Extract gen_ai system instructions from AI SDK's system field and messages.
22
+ *
23
+ * The AI SDK provides system prompts in two places:
24
+ * - The `system` parameter (string, SystemModelMessage, or array)
25
+ * - Messages with role "system"
26
+ *
27
+ * @param system - The AI SDK `system` field.
28
+ * @param messages - The messages array (may contain system-role messages).
29
+ * @returns System instructions in gen_ai format, or undefined if none.
30
+ */
31
+ export declare function extractSystemInstructions(system: unknown, messages: readonly unknown[]): SystemInstruction[] | undefined;
32
+ /**
33
+ * Build gen_ai output messages from AI SDK step result data.
34
+ *
35
+ * Combines text, reasoning, and tool call outputs into a single assistant
36
+ * OutputMessage, mirroring the order used by vercel.ts (reasoning → text → tool calls).
37
+ *
38
+ * @param options - Step result fields.
39
+ * @returns Array of gen_ai OutputMessage objects.
40
+ */
41
+ export declare function buildOutputMessages(options: {
42
+ text?: string;
43
+ reasoningText?: string;
44
+ reasoning?: readonly {
45
+ text?: string;
46
+ }[];
47
+ toolCalls?: readonly {
48
+ toolCallId: string;
49
+ toolName: string;
50
+ input: unknown;
51
+ }[];
52
+ finishReason?: string;
53
+ }): OutputMessage[];
54
+ /**
55
+ * Convert AI SDK ToolSet to gen_ai ToolDefinition[].
56
+ *
57
+ * The AI SDK represents tools as `Record<string, Tool>` where each Tool has
58
+ * description and parameters (usually a Zod schema). We extract what we can
59
+ * without importing Zod.
60
+ *
61
+ * @param tools - AI SDK ToolSet (Record<string, Tool>).
62
+ * @returns Array of gen_ai ToolDefinition objects, or undefined if no tools.
63
+ */
64
+ export declare function convertToolsToToolDefinitions(tools: unknown): ToolDefinition[] | undefined;
65
+ //# sourceMappingURL=ai-sdk.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-sdk.d.ts","sourceRoot":"","sources":["../../src/converters/ai-sdk.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,YAAY,EACZ,aAAa,EACb,iBAAiB,EACjB,cAAc,EAEf,MAAM,mBAAmB,CAAC;AAuB3B;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,QAAQ,EAAE,SAAS,OAAO,EAAE,GAC3B,YAAY,EAAE,CAQhB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,yBAAyB,CACvC,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,SAAS,OAAO,EAAE,GAC3B,iBAAiB,EAAE,GAAG,SAAS,CAoCjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,SAAS;QAAE,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACzC,SAAS,CAAC,EAAE,SAAS;QACnB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,EAAE,MAAM,CAAC;QACjB,KAAK,EAAE,OAAO,CAAC;KAChB,EAAE,CAAC;IACJ,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,GAAG,aAAa,EAAE,CAuClB;AAED;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,OAAO,GACb,cAAc,EAAE,GAAG,SAAS,CAgB9B"}
@@ -0,0 +1,238 @@
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
+ // Public conversion functions
12
+ // ---------------------------------------------------------------------------
13
+ /**
14
+ * Convert AI SDK messages (ModelMessage[]) to gen_ai InputMessage[].
15
+ * System messages are excluded — they're handled separately via
16
+ * {@link extractSystemInstructions}.
17
+ *
18
+ * @param messages - Array of AI SDK ModelMessage objects.
19
+ * @returns Array of gen_ai InputMessage objects (without system messages).
20
+ */
21
+ export function convertMessagesToInputMessages(messages) {
22
+ const result = [];
23
+ for (const msg of messages) {
24
+ const m = msg;
25
+ if (!m || !m.role || m.role === "system")
26
+ continue;
27
+ result.push(convertOneMessage(m));
28
+ }
29
+ return result;
30
+ }
31
+ /**
32
+ * Extract gen_ai system instructions from AI SDK's system field and messages.
33
+ *
34
+ * The AI SDK provides system prompts in two places:
35
+ * - The `system` parameter (string, SystemModelMessage, or array)
36
+ * - Messages with role "system"
37
+ *
38
+ * @param system - The AI SDK `system` field.
39
+ * @param messages - The messages array (may contain system-role messages).
40
+ * @returns System instructions in gen_ai format, or undefined if none.
41
+ */
42
+ export function extractSystemInstructions(system, messages) {
43
+ const instructions = [];
44
+ // From the `system` field
45
+ if (typeof system === "string" && system) {
46
+ instructions.push({ type: "text", content: system });
47
+ }
48
+ else if (system && typeof system === "object") {
49
+ if (Array.isArray(system)) {
50
+ for (const s of system) {
51
+ const text = extractTextFromSystemMessage(s);
52
+ if (text)
53
+ instructions.push({ type: "text", content: text });
54
+ }
55
+ }
56
+ else {
57
+ const text = extractTextFromSystemMessage(system);
58
+ if (text)
59
+ instructions.push({ type: "text", content: text });
60
+ }
61
+ }
62
+ // From system-role messages
63
+ for (const msg of messages) {
64
+ const m = msg;
65
+ if (m?.role === "system") {
66
+ const content = m.content;
67
+ if (typeof content === "string") {
68
+ instructions.push({ type: "text", content });
69
+ }
70
+ else if (Array.isArray(content)) {
71
+ const text = content
72
+ .filter((p) => p.type === "text")
73
+ .map((p) => p.text || "")
74
+ .join("");
75
+ if (text)
76
+ instructions.push({ type: "text", content: text });
77
+ }
78
+ }
79
+ }
80
+ return instructions.length > 0 ? instructions : undefined;
81
+ }
82
+ /**
83
+ * Build gen_ai output messages from AI SDK step result data.
84
+ *
85
+ * Combines text, reasoning, and tool call outputs into a single assistant
86
+ * OutputMessage, mirroring the order used by vercel.ts (reasoning → text → tool calls).
87
+ *
88
+ * @param options - Step result fields.
89
+ * @returns Array of gen_ai OutputMessage objects.
90
+ */
91
+ export function buildOutputMessages(options) {
92
+ const parts = [];
93
+ // Reasoning first (mirrors vercel.ts order)
94
+ if (options.reasoningText) {
95
+ parts.push({ type: "thinking", content: options.reasoningText });
96
+ }
97
+ else if (options.reasoning?.length) {
98
+ for (const r of options.reasoning) {
99
+ if (r.text)
100
+ parts.push({ type: "thinking", content: r.text });
101
+ }
102
+ }
103
+ // Text content
104
+ if (options.text) {
105
+ parts.push({ type: "text", content: options.text });
106
+ }
107
+ // Tool calls
108
+ if (options.toolCalls?.length) {
109
+ for (const tc of options.toolCalls) {
110
+ parts.push({
111
+ type: "tool_call",
112
+ name: tc.toolName,
113
+ id: tc.toolCallId,
114
+ arguments: typeof tc.input === "string" ? tc.input : JSON.stringify(tc.input),
115
+ });
116
+ }
117
+ }
118
+ if (parts.length === 0)
119
+ return [];
120
+ return [
121
+ {
122
+ role: "assistant",
123
+ parts,
124
+ finish_reason: options.finishReason,
125
+ },
126
+ ];
127
+ }
128
+ /**
129
+ * Convert AI SDK ToolSet to gen_ai ToolDefinition[].
130
+ *
131
+ * The AI SDK represents tools as `Record<string, Tool>` where each Tool has
132
+ * description and parameters (usually a Zod schema). We extract what we can
133
+ * without importing Zod.
134
+ *
135
+ * @param tools - AI SDK ToolSet (Record<string, Tool>).
136
+ * @returns Array of gen_ai ToolDefinition objects, or undefined if no tools.
137
+ */
138
+ export function convertToolsToToolDefinitions(tools) {
139
+ if (!tools || typeof tools !== "object")
140
+ return undefined;
141
+ const defs = [];
142
+ for (const [name, tool] of Object.entries(tools)) {
143
+ if (!tool || typeof tool !== "object")
144
+ continue;
145
+ const t = tool;
146
+ defs.push({
147
+ name,
148
+ description: typeof t.description === "string" ? t.description : undefined,
149
+ parameters: extractToolParameters(t.parameters),
150
+ });
151
+ }
152
+ return defs.length > 0 ? defs : undefined;
153
+ }
154
+ // ---------------------------------------------------------------------------
155
+ // Internal helpers
156
+ // ---------------------------------------------------------------------------
157
+ function convertOneMessage(msg) {
158
+ const role = (msg.role || "user");
159
+ const content = msg.content;
160
+ const parts = [];
161
+ if (typeof content === "string") {
162
+ parts.push({ type: "text", content });
163
+ }
164
+ else if (Array.isArray(content)) {
165
+ for (const part of content) {
166
+ const converted = convertContentPart(part);
167
+ if (converted)
168
+ parts.push(converted);
169
+ }
170
+ }
171
+ return { role, parts };
172
+ }
173
+ function convertContentPart(part) {
174
+ switch (part.type) {
175
+ case "text":
176
+ return { type: "text", content: part.text || "" };
177
+ case "tool-call": {
178
+ const args = part.args ?? part.input;
179
+ return {
180
+ type: "tool_call",
181
+ name: part.toolName || "",
182
+ id: part.toolCallId || "",
183
+ arguments: typeof args === "string" ? args : JSON.stringify(args ?? {}),
184
+ };
185
+ }
186
+ case "tool-result": {
187
+ const rawResult = part.result ?? part.output;
188
+ let response;
189
+ if (typeof rawResult === "string") {
190
+ response = rawResult;
191
+ }
192
+ else if (rawResult &&
193
+ typeof rawResult === "object" &&
194
+ "value" in rawResult) {
195
+ response = JSON.stringify(rawResult.value);
196
+ }
197
+ else {
198
+ response = JSON.stringify(rawResult);
199
+ }
200
+ return {
201
+ type: "tool_call_response",
202
+ id: part.toolCallId || "",
203
+ response,
204
+ };
205
+ }
206
+ case "reasoning":
207
+ return { type: "thinking", content: part.text || "" };
208
+ default:
209
+ // Unknown part type — serialize as text fallback
210
+ return { type: "text", content: JSON.stringify(part) };
211
+ }
212
+ }
213
+ function extractTextFromSystemMessage(msg) {
214
+ if (typeof msg === "string")
215
+ return msg;
216
+ if (msg && typeof msg === "object") {
217
+ const m = msg;
218
+ if (typeof m.content === "string")
219
+ return m.content;
220
+ if (typeof m.text === "string")
221
+ return m.text;
222
+ }
223
+ return null;
224
+ }
225
+ function extractToolParameters(parameters) {
226
+ if (!parameters || typeof parameters !== "object")
227
+ return undefined;
228
+ const p = parameters;
229
+ // jsonSchema() wrapper — has a jsonSchema property
230
+ if (p.jsonSchema && typeof p.jsonSchema === "object") {
231
+ return p.jsonSchema;
232
+ }
233
+ // Plain JSON Schema object (has 'type' and 'properties')
234
+ if (p.type === "object" && p.properties) {
235
+ return p;
236
+ }
237
+ return undefined;
238
+ }