@introspection-sdk/introspection-node 0.4.2 → 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.
Files changed (219) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/client.d.ts +42 -132
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.js +67 -366
  5. package/dist/client.js.map +1 -0
  6. package/dist/converters/claude.d.ts +4 -0
  7. package/dist/converters/claude.d.ts.map +1 -1
  8. package/dist/converters/claude.js +7 -0
  9. package/dist/converters/claude.js.map +1 -0
  10. package/dist/converters/gemini.d.ts +90 -0
  11. package/dist/converters/gemini.d.ts.map +1 -0
  12. package/dist/converters/gemini.js +171 -0
  13. package/dist/converters/gemini.js.map +1 -0
  14. package/dist/converters/index.d.ts +1 -1
  15. package/dist/converters/index.d.ts.map +1 -1
  16. package/dist/converters/index.js +2 -1
  17. package/dist/converters/index.js.map +1 -0
  18. package/dist/converters/openai.js +1 -0
  19. package/dist/converters/openai.js.map +1 -0
  20. package/dist/converters/openinference.js +1 -0
  21. package/dist/converters/openinference.js.map +1 -0
  22. package/dist/converters/vercel.d.ts.map +1 -1
  23. package/dist/converters/vercel.js +31 -3
  24. package/dist/converters/vercel.js.map +1 -0
  25. package/dist/http.d.ts +36 -0
  26. package/dist/http.d.ts.map +1 -0
  27. package/dist/http.js +145 -0
  28. package/dist/http.js.map +1 -0
  29. package/dist/index.d.ts +24 -27
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +22 -32
  32. package/dist/index.js.map +1 -0
  33. package/dist/{anthropic.d.ts → otel/anthropic.d.ts} +23 -13
  34. package/dist/otel/anthropic.d.ts.map +1 -0
  35. package/dist/{anthropic.js → otel/anthropic.js} +132 -40
  36. package/dist/otel/anthropic.js.map +1 -0
  37. package/dist/{claude-hooks.d.ts → otel/claude-hooks.d.ts} +19 -1
  38. package/dist/otel/claude-hooks.d.ts.map +1 -0
  39. package/dist/{claude-hooks.js → otel/claude-hooks.js} +94 -20
  40. package/dist/otel/claude-hooks.js.map +1 -0
  41. package/dist/otel/claude-wrapper.d.ts.map +1 -0
  42. package/dist/{claude-wrapper.js → otel/claude-wrapper.js} +2 -1
  43. package/dist/otel/claude-wrapper.js.map +1 -0
  44. package/dist/otel/gemini.d.ts +56 -0
  45. package/dist/otel/gemini.d.ts.map +1 -0
  46. package/dist/otel/gemini.js +348 -0
  47. package/dist/otel/gemini.js.map +1 -0
  48. package/dist/otel/index.d.ts +32 -0
  49. package/dist/otel/index.d.ts.map +1 -0
  50. package/dist/otel/index.js +41 -0
  51. package/dist/otel/index.js.map +1 -0
  52. package/dist/otel/init.d.ts +126 -0
  53. package/dist/otel/init.d.ts.map +1 -0
  54. package/dist/otel/init.js +259 -0
  55. package/dist/otel/init.js.map +1 -0
  56. package/dist/otel/integrations/anthropic.d.ts +10 -0
  57. package/dist/otel/integrations/anthropic.d.ts.map +1 -0
  58. package/dist/otel/integrations/anthropic.js +22 -0
  59. package/dist/otel/integrations/anthropic.js.map +1 -0
  60. package/dist/otel/integrations/base.d.ts +83 -0
  61. package/dist/otel/integrations/base.d.ts.map +1 -0
  62. package/dist/otel/integrations/base.js +23 -0
  63. package/dist/otel/integrations/base.js.map +1 -0
  64. package/dist/otel/integrations/claude-agent.d.ts +13 -0
  65. package/dist/otel/integrations/claude-agent.d.ts.map +1 -0
  66. package/dist/otel/integrations/claude-agent.js +27 -0
  67. package/dist/otel/integrations/claude-agent.js.map +1 -0
  68. package/dist/otel/integrations/gemini.d.ts +11 -0
  69. package/dist/otel/integrations/gemini.d.ts.map +1 -0
  70. package/dist/otel/integrations/gemini.js +21 -0
  71. package/dist/otel/integrations/gemini.js.map +1 -0
  72. package/dist/otel/integrations/index.d.ts +39 -0
  73. package/dist/otel/integrations/index.d.ts.map +1 -0
  74. package/dist/otel/integrations/index.js +131 -0
  75. package/dist/otel/integrations/index.js.map +1 -0
  76. package/dist/otel/integrations/langchain.d.ts +16 -0
  77. package/dist/otel/integrations/langchain.d.ts.map +1 -0
  78. package/dist/otel/integrations/langchain.js +41 -0
  79. package/dist/otel/integrations/langchain.js.map +1 -0
  80. package/dist/otel/integrations/mastra.d.ts +14 -0
  81. package/dist/otel/integrations/mastra.d.ts.map +1 -0
  82. package/dist/otel/integrations/mastra.js +27 -0
  83. package/dist/otel/integrations/mastra.js.map +1 -0
  84. package/dist/otel/integrations/openai-agents.d.ts +11 -0
  85. package/dist/otel/integrations/openai-agents.d.ts.map +1 -0
  86. package/dist/otel/integrations/openai-agents.js +25 -0
  87. package/dist/otel/integrations/openai-agents.js.map +1 -0
  88. package/dist/otel/integrations/pi.d.ts +12 -0
  89. package/dist/otel/integrations/pi.d.ts.map +1 -0
  90. package/dist/otel/integrations/pi.js +20 -0
  91. package/dist/otel/integrations/pi.js.map +1 -0
  92. package/dist/otel/integrations/vercel.d.ts +15 -0
  93. package/dist/otel/integrations/vercel.d.ts.map +1 -0
  94. package/dist/otel/integrations/vercel.js +20 -0
  95. package/dist/otel/integrations/vercel.js.map +1 -0
  96. package/dist/{langchain-handler.d.ts → otel/langchain-handler.d.ts} +10 -1
  97. package/dist/otel/langchain-handler.d.ts.map +1 -0
  98. package/dist/{langchain-handler.js → otel/langchain-handler.js} +43 -6
  99. package/dist/otel/langchain-handler.js.map +1 -0
  100. package/dist/otel/logs.d.ts +82 -0
  101. package/dist/otel/logs.d.ts.map +1 -0
  102. package/dist/otel/logs.js +271 -0
  103. package/dist/otel/logs.js.map +1 -0
  104. package/dist/{mastra-exporter.d.ts → otel/mastra-exporter.d.ts} +3 -1
  105. package/dist/otel/mastra-exporter.d.ts.map +1 -0
  106. package/dist/{mastra-exporter.js → otel/mastra-exporter.js} +71 -17
  107. package/dist/otel/mastra-exporter.js.map +1 -0
  108. package/dist/otel/pi.d.ts +59 -0
  109. package/dist/otel/pi.d.ts.map +1 -0
  110. package/dist/otel/pi.js +67 -0
  111. package/dist/otel/pi.js.map +1 -0
  112. package/dist/otel/register.d.ts +8 -0
  113. package/dist/otel/register.d.ts.map +1 -0
  114. package/dist/otel/register.js +48 -0
  115. package/dist/otel/register.js.map +1 -0
  116. package/dist/otel/setup.d.ts +52 -0
  117. package/dist/otel/setup.d.ts.map +1 -0
  118. package/dist/otel/setup.js +146 -0
  119. package/dist/otel/setup.js.map +1 -0
  120. package/dist/{span-processor.d.ts → otel/span-processor.d.ts} +1 -1
  121. package/dist/otel/span-processor.d.ts.map +1 -0
  122. package/dist/{span-processor.js → otel/span-processor.js} +11 -5
  123. package/dist/otel/span-processor.js.map +1 -0
  124. package/dist/{tracing-processor.d.ts → otel/tracing-processor.d.ts} +4 -1
  125. package/dist/otel/tracing-processor.d.ts.map +1 -0
  126. package/dist/{tracing-processor.js → otel/tracing-processor.js} +64 -14
  127. package/dist/otel/tracing-processor.js.map +1 -0
  128. package/dist/resources/experiments.d.ts +31 -0
  129. package/dist/resources/experiments.d.ts.map +1 -0
  130. package/dist/resources/experiments.js +133 -0
  131. package/dist/resources/experiments.js.map +1 -0
  132. package/dist/resources/recipes.d.ts +22 -0
  133. package/dist/resources/recipes.d.ts.map +1 -0
  134. package/dist/resources/recipes.js +61 -0
  135. package/dist/resources/recipes.js.map +1 -0
  136. package/dist/resources/runtimes.d.ts +76 -0
  137. package/dist/resources/runtimes.d.ts.map +1 -0
  138. package/dist/resources/runtimes.js +179 -0
  139. package/dist/resources/runtimes.js.map +1 -0
  140. package/dist/runner-resources/files.d.ts +35 -0
  141. package/dist/runner-resources/files.d.ts.map +1 -0
  142. package/dist/runner-resources/files.js +131 -0
  143. package/dist/runner-resources/files.js.map +1 -0
  144. package/dist/runner-resources/tasks.d.ts +39 -0
  145. package/dist/runner-resources/tasks.d.ts.map +1 -0
  146. package/dist/runner-resources/tasks.js +129 -0
  147. package/dist/runner-resources/tasks.js.map +1 -0
  148. package/dist/runner.d.ts +80 -0
  149. package/dist/runner.d.ts.map +1 -0
  150. package/dist/runner.js +159 -0
  151. package/dist/runner.js.map +1 -0
  152. package/dist/streaming.d.ts +8 -0
  153. package/dist/streaming.d.ts.map +1 -0
  154. package/dist/streaming.js +67 -0
  155. package/dist/streaming.js.map +1 -0
  156. package/dist/types.js +1 -0
  157. package/dist/types.js.map +1 -0
  158. package/dist/utils.d.ts +10 -0
  159. package/dist/utils.d.ts.map +1 -1
  160. package/dist/utils.js +13 -4
  161. package/dist/utils.js.map +1 -0
  162. package/dist/version.js +1 -0
  163. package/dist/version.js.map +1 -0
  164. package/package.json +91 -22
  165. package/src/client.ts +76 -473
  166. package/src/converters/claude.ts +10 -0
  167. package/src/converters/gemini.ts +243 -0
  168. package/src/converters/index.ts +13 -7
  169. package/src/converters/vercel.ts +41 -3
  170. package/src/http.ts +163 -0
  171. package/src/index.ts +110 -75
  172. package/src/{anthropic.ts → otel/anthropic.ts} +206 -54
  173. package/src/{claude-hooks.ts → otel/claude-hooks.ts} +119 -20
  174. package/src/{claude-wrapper.ts → otel/claude-wrapper.ts} +1 -1
  175. package/src/otel/gemini.ts +491 -0
  176. package/src/otel/index.ts +121 -0
  177. package/src/otel/init.ts +411 -0
  178. package/src/otel/integrations/anthropic.ts +26 -0
  179. package/src/otel/integrations/base.ts +95 -0
  180. package/src/otel/integrations/claude-agent.ts +32 -0
  181. package/src/otel/integrations/gemini.ts +25 -0
  182. package/src/otel/integrations/index.ts +151 -0
  183. package/src/otel/integrations/langchain.ts +45 -0
  184. package/src/otel/integrations/mastra.ts +30 -0
  185. package/src/otel/integrations/openai-agents.ts +31 -0
  186. package/src/otel/integrations/pi.ts +24 -0
  187. package/src/otel/integrations/vercel.ts +24 -0
  188. package/src/{langchain-handler.ts → otel/langchain-handler.ts} +53 -6
  189. package/src/otel/logs.ts +394 -0
  190. package/src/{mastra-exporter.ts → otel/mastra-exporter.ts} +83 -22
  191. package/src/otel/pi.ts +88 -0
  192. package/src/otel/register.ts +50 -0
  193. package/src/otel/setup.ts +215 -0
  194. package/src/{span-processor.ts → otel/span-processor.ts} +11 -6
  195. package/src/{tracing-processor.ts → otel/tracing-processor.ts} +94 -19
  196. package/src/resources/experiments.ts +170 -0
  197. package/src/resources/recipes.ts +73 -0
  198. package/src/resources/runtimes.ts +231 -0
  199. package/src/runner-resources/files.ts +163 -0
  200. package/src/runner-resources/tasks.ts +156 -0
  201. package/src/runner.ts +194 -0
  202. package/src/streaming.ts +57 -0
  203. package/src/utils.ts +12 -5
  204. package/dist/ai-sdk-integration.d.ts +0 -123
  205. package/dist/ai-sdk-integration.d.ts.map +0 -1
  206. package/dist/ai-sdk-integration.js +0 -475
  207. package/dist/anthropic.d.ts.map +0 -1
  208. package/dist/claude-hooks.d.ts.map +0 -1
  209. package/dist/claude-wrapper.d.ts.map +0 -1
  210. package/dist/converters/ai-sdk.d.ts +0 -65
  211. package/dist/converters/ai-sdk.d.ts.map +0 -1
  212. package/dist/converters/ai-sdk.js +0 -261
  213. package/dist/langchain-handler.d.ts.map +0 -1
  214. package/dist/mastra-exporter.d.ts.map +0 -1
  215. package/dist/span-processor.d.ts.map +0 -1
  216. package/dist/tracing-processor.d.ts.map +0 -1
  217. package/src/ai-sdk-integration.ts +0 -691
  218. package/src/converters/ai-sdk.ts +0 -323
  219. /package/dist/{claude-wrapper.d.ts → otel/claude-wrapper.d.ts} +0 -0
package/src/index.ts CHANGED
@@ -1,36 +1,18 @@
1
- import { IntrospectionSpanProcessor } from "./span-processor.js";
2
- import type { IntrospectionSpanProcessorOptions } from "./span-processor.js";
3
-
4
1
  /**
5
- * Factory that creates an {@link IntrospectionSpanProcessor}.
6
- *
7
- * Provided as the default export for convenient use with logfire's
8
- * `additionalSpanProcessors` option.
2
+ * Root entry point — REST-only surface.
9
3
  *
10
- * @param options - Optional processor configuration (token, serviceName, etc.).
11
- * @returns A ready-to-use {@link IntrospectionSpanProcessor}.
4
+ * Exports `IntrospectionClient` (REST), Runner / RuntimeHandle /
5
+ * ExperimentHandle, REST wire types, and HTTP helpers. None of these
6
+ * touch the OpenTelemetry SDK.
12
7
  *
13
- * @example
14
- * ```ts
15
- * import introspectionSpanProcessor from "@introspection-sdk/introspection-node";
16
- *
17
- * logfire.configure({ additionalSpanProcessors: [introspectionSpanProcessor()] });
18
- * ```
8
+ * For the OTel surface (logs, span processors, instrumentors) import
9
+ * from `@introspection-sdk/introspection-node/otel`.
19
10
  */
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
11
 
30
- // Client exports
12
+ // REST client.
31
13
  export { IntrospectionClient } from "./client.js";
32
14
 
33
- // Type exports
15
+ // Configuration / event types shared with the OTel surface.
34
16
  export type {
35
17
  AdvancedOptions,
36
18
  IntrospectionClientOptions,
@@ -38,40 +20,98 @@ export type {
38
20
  UserTraits,
39
21
  } from "./types.js";
40
22
 
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";
23
+ // Runner-bound REST namespaces (runner.tasks, runner.files) + HTTP/SSE.
24
+ export { TasksApi, TaskRunsApi, RunHandle } from "./runner-resources/tasks.js";
25
+ export type { StartParams } from "./runner-resources/tasks.js";
26
+ export { FilesApi, FileVersionsApi } from "./runner-resources/files.js";
27
+ export type { FileUploadBody } from "./runner-resources/files.js";
28
+ export { HttpClient } from "./http.js";
29
+ export type { ResolvedApiConfig } from "./http.js";
30
+ export { parseSse } from "./streaming.js";
47
31
 
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";
32
+ // Runner + CP resources.
33
+ export { Runner } from "./runner.js";
34
+ export type { RunnerSource } from "./runner.js";
35
+ export {
36
+ RuntimesApi,
37
+ RuntimeHandle,
38
+ attachRuntimes,
39
+ isUuid,
40
+ } from "./resources/runtimes.js";
41
+ export type { RuntimeHandleFactory } from "./resources/runtimes.js";
42
+ export {
43
+ ExperimentsApi,
44
+ ExperimentHandle,
45
+ attachExperiments,
46
+ } from "./resources/experiments.js";
47
+ export type { ExperimentHandleFactory } from "./resources/experiments.js";
48
+ export { RecipesApi, attachRecipes } from "./resources/recipes.js";
65
49
 
66
- // Claude Agent SDK wrapper exports
67
- export { withIntrospection } from "./claude-wrapper.js";
50
+ // REST API wire types
68
51
  export type {
69
- WithIntrospectionOptions,
70
- InstrumentedClaudeAgentSDK,
71
- ClaudeAgentSDKModule,
72
- } from "./claude-wrapper.js";
52
+ Paginated,
53
+ ListParams,
54
+ Task,
55
+ TaskCreateParams,
56
+ TaskUpdateParams,
57
+ TaskListParams,
58
+ TaskMode,
59
+ TaskStatus,
60
+ TaskPrompt,
61
+ TaskRun,
62
+ TaskRunCreateParams,
63
+ TaskRunResponse,
64
+ TaskCreateResponse,
65
+ TaskCancelResponse,
66
+ AgentInfo,
67
+ File,
68
+ FileType,
69
+ FileListParams,
70
+ FileUpdateParams,
71
+ FileCreateTextParams,
72
+ SseEvent,
73
+ Runtime,
74
+ RuntimeCreate,
75
+ RuntimeUpdate,
76
+ RuntimeListParams,
77
+ Experiment,
78
+ ExperimentCreate,
79
+ ExperimentUpdate,
80
+ ExperimentListParams,
81
+ ExperimentStatus,
82
+ ExperimentEndParams,
83
+ Arm,
84
+ Recipe,
85
+ RecipeCreate,
86
+ RecipeUpdate,
87
+ RecipeListParams,
88
+ RunnerSpec,
89
+ RunnerDeployment,
90
+ RunnerContext,
91
+ RunnerRecipeSummary,
92
+ RunnerIdentity,
93
+ RunRequest,
94
+ RunCaller,
95
+ RunCallerLibrary,
96
+ RunCallerPage,
97
+ RunIdentityInput,
98
+ } from "@introspection-sdk/types";
99
+ export {
100
+ IntrospectionAPIError,
101
+ AuthenticationError,
102
+ InsufficientScopeError,
103
+ RunnerExpiredError,
104
+ NotFoundError,
105
+ ConflictError,
106
+ ValidationError,
107
+ RateLimitError,
108
+ SandboxUnavailableError,
109
+ StreamError,
110
+ NetworkError,
111
+ apiErrorFromResponse,
112
+ } from "@introspection-sdk/types";
73
113
 
74
- // GenAI types exports
114
+ // GenAI types (shared, no OTel SDK imports).
75
115
  export type {
76
116
  GenAiAttributes,
77
117
  InputMessage,
@@ -85,7 +125,7 @@ export type {
85
125
  } from "@introspection-sdk/types";
86
126
  export { toAttributes } from "@introspection-sdk/types";
87
127
 
88
- // OpenAI converter exports
128
+ // OpenAI converter exports (pure functions, no OTel SDK imports).
89
129
  export {
90
130
  convertResponsesInputsToSemconv,
91
131
  convertResponsesOutputsToSemconv,
@@ -100,22 +140,17 @@ export type {
100
140
  Response as OpenAIResponse,
101
141
  } from "./converters/openai.js";
102
142
 
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
143
+ // Gemini converters (pure functions, no OTel SDK imports).
117
144
  export {
118
- AnthropicInstrumentor,
119
- tracedMessagesCreate,
120
- REDACTED_THINKING_CONTENT,
121
- } from "./anthropic.js";
145
+ convertGeminiContentsToInputMessages,
146
+ convertGeminiCandidatesToOutputMessages,
147
+ convertGeminiSystemInstructionToSemconv,
148
+ convertGeminiToolsToToolDefinitions,
149
+ } from "./converters/gemini.js";
150
+ export type {
151
+ GeminiCandidate,
152
+ GeminiContent,
153
+ GeminiFunctionDeclaration,
154
+ GeminiPart,
155
+ GeminiTool,
156
+ } from "./converters/gemini.js";
@@ -24,7 +24,13 @@ import type {
24
24
  ReasoningPart,
25
25
  } from "@introspection-sdk/types";
26
26
 
27
- import { trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
27
+ import {
28
+ trace,
29
+ context as otelContext,
30
+ propagation,
31
+ SpanKind,
32
+ SpanStatusCode,
33
+ } from "@opentelemetry/api";
28
34
  import type { Tracer, Span } from "@opentelemetry/api";
29
35
  import type { BasicTracerProvider } from "@opentelemetry/sdk-trace-base";
30
36
 
@@ -154,7 +160,11 @@ function serializeMessages(msgs: InputMessage[] | OutputMessage[]): string {
154
160
  );
155
161
  }
156
162
 
157
- function startSpan(tracer: Tracer, model: string): { span: Span; token: null } {
163
+ function startSpan(
164
+ tracer: Tracer,
165
+ model: string,
166
+ conversationId?: string,
167
+ ): { span: Span; token: null } {
158
168
  const span = tracer.startSpan("chat", {
159
169
  kind: SpanKind.CLIENT,
160
170
  attributes: {
@@ -163,6 +173,9 @@ function startSpan(tracer: Tracer, model: string): { span: Span; token: null } {
163
173
  "gen_ai.operation.name": "chat",
164
174
  "gen_ai.request.model": model,
165
175
  "openinference.span.kind": "LLM",
176
+ ...(conversationId
177
+ ? { "gen_ai.conversation.id": conversationId }
178
+ : undefined),
166
179
  },
167
180
  });
168
181
  return { span, token: null };
@@ -177,10 +190,26 @@ function setRequestAttrs(span: Span, kwargs: Record<string, unknown>): void {
177
190
 
178
191
  const system = kwargs.system;
179
192
  if (system) {
180
- const sysVal =
181
- typeof system === "string"
182
- ? JSON.stringify([{ type: "text", content: system }])
183
- : JSON.stringify(system);
193
+ let sysVal: string;
194
+ if (typeof system === "string") {
195
+ sysVal = JSON.stringify([{ type: "text", content: system }]);
196
+ } else if (Array.isArray(system)) {
197
+ // Anthropic block format: [{type:"text", text:"...", cache_control:{...}}]
198
+ // Normalize to gen_ai format: [{type:"text", content:"..."}]
199
+ sysVal = JSON.stringify(
200
+ (system as Array<Record<string, unknown>>).map((block) => ({
201
+ type: "text",
202
+ content:
203
+ typeof block.text === "string"
204
+ ? block.text
205
+ : typeof block.content === "string"
206
+ ? block.content
207
+ : "",
208
+ })),
209
+ );
210
+ } else {
211
+ sysVal = JSON.stringify(system);
212
+ }
184
213
  span.setAttribute("gen_ai.system_instructions", sysVal);
185
214
  }
186
215
 
@@ -219,6 +248,16 @@ function setResponseAttrs(span: Span, response: Record<string, unknown>): void {
219
248
  span.setAttribute("gen_ai.usage.input_tokens", usage.input_tokens);
220
249
  if (usage.output_tokens != null)
221
250
  span.setAttribute("gen_ai.usage.output_tokens", usage.output_tokens);
251
+ if (usage.cache_read_input_tokens != null)
252
+ span.setAttribute(
253
+ "gen_ai.usage.cache_read.input_tokens",
254
+ usage.cache_read_input_tokens,
255
+ );
256
+ if (usage.cache_creation_input_tokens != null)
257
+ span.setAttribute(
258
+ "gen_ai.usage.cache_creation.input_tokens",
259
+ usage.cache_creation_input_tokens,
260
+ );
222
261
  }
223
262
 
224
263
  span.setStatus({ code: SpanStatusCode.OK });
@@ -245,6 +284,8 @@ class TracedStream {
245
284
  private responseModel: string | null = null;
246
285
  private inputTokens = 0;
247
286
  private outputTokens = 0;
287
+ private cacheReadInputTokens = 0;
288
+ private cacheCreationInputTokens = 0;
248
289
  private finalized = false;
249
290
 
250
291
  constructor(inner: AsyncIterable<StreamEvent>, span: Span) {
@@ -268,7 +309,11 @@ class TracedStream {
268
309
  this.responseId = (event.message.id as string) || null;
269
310
  this.responseModel = (event.message.model as string) || null;
270
311
  const usage = event.message.usage as Record<string, number> | undefined;
271
- if (usage) this.inputTokens = usage.input_tokens || 0;
312
+ if (usage) {
313
+ this.inputTokens = usage.input_tokens || 0;
314
+ this.cacheReadInputTokens = usage.cache_read_input_tokens || 0;
315
+ this.cacheCreationInputTokens = usage.cache_creation_input_tokens || 0;
316
+ }
272
317
  } else if (event.type === "content_block_start" && event.content_block) {
273
318
  const bt = (event.content_block.type as string) || "";
274
319
  this.currentBlock = { type: bt };
@@ -328,6 +373,16 @@ class TracedStream {
328
373
  this.span.setAttribute("gen_ai.usage.input_tokens", this.inputTokens);
329
374
  if (this.outputTokens)
330
375
  this.span.setAttribute("gen_ai.usage.output_tokens", this.outputTokens);
376
+ if (this.cacheReadInputTokens)
377
+ this.span.setAttribute(
378
+ "gen_ai.usage.cache_read.input_tokens",
379
+ this.cacheReadInputTokens,
380
+ );
381
+ if (this.cacheCreationInputTokens)
382
+ this.span.setAttribute(
383
+ "gen_ai.usage.cache_creation.input_tokens",
384
+ this.cacheCreationInputTokens,
385
+ );
331
386
  this.span.setStatus({ code: SpanStatusCode.OK });
332
387
  this.span.end();
333
388
  }
@@ -345,8 +400,13 @@ export async function tracedMessagesCreate(
345
400
  tracer: Tracer,
346
401
  client: { messages: { create: (...args: unknown[]) => Promise<unknown> } },
347
402
  kwargs: Record<string, unknown>,
403
+ conversationId?: string,
348
404
  ): Promise<unknown> {
349
- const { span } = startSpan(tracer, (kwargs.model as string) || "unknown");
405
+ const { span } = startSpan(
406
+ tracer,
407
+ (kwargs.model as string) || "unknown",
408
+ conversationId,
409
+ );
350
410
  try {
351
411
  setRequestAttrs(span, kwargs);
352
412
  const response = (await client.messages.create(kwargs)) as Record<
@@ -378,16 +438,106 @@ export async function tracedMessagesCreate(
378
438
  * Captures all content blocks including thinking (extended thinking) with
379
439
  * signatures. Supports both non-streaming and streaming calls.
380
440
  */
441
+ /**
442
+ * Build the traced replacement for `messages.create`.
443
+ *
444
+ * Shared by {@link AnthropicInstrumentor.instrument} (patches a single client
445
+ * instance) and {@link AnthropicInstrumentor.instrumentClass} (patches
446
+ * `Anthropic.Messages.prototype.create` so every client is auto-traced). The
447
+ * returned function preserves the call-site `this`, so it works whether it is
448
+ * assigned to an instance property or onto the class prototype.
449
+ */
450
+ function buildTracedCreate(
451
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
452
+ origCreate: (...args: any[]) => any,
453
+ tracer: Tracer,
454
+ defaultConversationId: string,
455
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
456
+ ): (...args: any[]) => any {
457
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
458
+ return function tracedCreate(this: unknown, ...args: any[]) {
459
+ const kwargs = args[0] as Record<string, unknown>;
460
+ const isStream = kwargs?.stream === true;
461
+
462
+ // Baggage on the active context overrides the conversation ID bound at
463
+ // instrument() time, so callers can wrap a request in
464
+ // IntrospectionClient.withConversation() to scope a single call to a
465
+ // distinct conversation without re-instrumenting the client.
466
+ const baggage = propagation.getBaggage(otelContext.active());
467
+ const baggageConvId = baggage?.getEntry("gen_ai.conversation.id")?.value;
468
+ const baggageAgentName = baggage?.getEntry("gen_ai.agent.name")?.value;
469
+ const baggageAgentId = baggage?.getEntry("gen_ai.agent.id")?.value;
470
+
471
+ const { span } = startSpan(
472
+ tracer,
473
+ (kwargs?.model as string) || "unknown",
474
+ baggageConvId ?? defaultConversationId,
475
+ );
476
+ if (baggageAgentName) {
477
+ span.setAttribute("gen_ai.agent.name", baggageAgentName);
478
+ }
479
+ if (baggageAgentId) {
480
+ span.setAttribute("gen_ai.agent.id", baggageAgentId);
481
+ }
482
+ setRequestAttrs(span, kwargs || {});
483
+
484
+ if (isStream) {
485
+ const streamPromise = origCreate.apply(this, args) as Promise<
486
+ AsyncIterable<StreamEvent>
487
+ >;
488
+ return streamPromise.then(
489
+ (stream: AsyncIterable<StreamEvent>) => new TracedStream(stream, span),
490
+ (err: Error) => {
491
+ span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
492
+ span.end();
493
+ throw err;
494
+ },
495
+ );
496
+ }
497
+
498
+ const resultPromise = origCreate.apply(this, args) as Promise<
499
+ Record<string, unknown>
500
+ >;
501
+ return resultPromise.then(
502
+ (response: Record<string, unknown>) => {
503
+ setResponseAttrs(span, response);
504
+ span.end();
505
+ return response;
506
+ },
507
+ (err: Error) => {
508
+ span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
509
+ span.end();
510
+ throw err;
511
+ },
512
+ );
513
+ };
514
+ }
515
+
516
+ /**
517
+ * Marker placed on a patched `Messages.prototype.create` so repeated
518
+ * `instrumentClass()` calls (e.g. a second `introspection.init()`) are no-ops.
519
+ */
520
+ const PATCHED = Symbol.for("introspection.anthropic.patched");
521
+
381
522
  export class AnthropicInstrumentor {
382
523
  private tracer: Tracer | null = null;
383
524
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
384
525
  private patchedClients: Array<{ client: any; originalCreate: any }> = [];
526
+ private protoRestores: Array<() => void> = [];
527
+ private conversationId: string | undefined;
385
528
 
386
529
  instrument(opts: {
387
530
  tracerProvider?: BasicTracerProvider;
388
531
  /** The Anthropic client instance to instrument. */
389
532
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
390
533
  client: any;
534
+ /**
535
+ * Conversation ID shared across all spans produced by this instrumentor.
536
+ * Enables the Introspection processor to reliably link multi-turn API calls
537
+ * into a single conversation without relying on history-hash matching.
538
+ * Defaults to a random UUID generated at instrument() time.
539
+ */
540
+ conversationId?: string;
391
541
  }): void {
392
542
  const { client } = opts;
393
543
  if (!client?.messages?.create) {
@@ -396,6 +546,8 @@ export class AnthropicInstrumentor {
396
546
  );
397
547
  }
398
548
 
549
+ this.conversationId = opts.conversationId ?? crypto.randomUUID();
550
+
399
551
  const provider = opts.tracerProvider ?? trace.getTracerProvider();
400
552
  this.tracer = (provider as BasicTracerProvider).getTracer(
401
553
  "introspection-anthropic",
@@ -403,56 +555,54 @@ export class AnthropicInstrumentor {
403
555
 
404
556
  const origCreate = client.messages.create.bind(client.messages);
405
557
  this.patchedClients.push({ client, originalCreate: origCreate });
406
- const tracer = this.tracer!;
407
558
 
408
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
409
- client.messages.create = function (...args: any[]) {
410
- const kwargs = args[0] as Record<string, unknown>;
411
- const isStream = kwargs?.stream === true;
559
+ client.messages.create = buildTracedCreate(
560
+ origCreate,
561
+ this.tracer,
562
+ this.conversationId,
563
+ );
564
+ }
412
565
 
413
- const { span } = startSpan(
414
- tracer,
415
- (kwargs?.model as string) || "unknown",
566
+ /**
567
+ * Patch `Anthropic.Messages.prototype.create` so **every** client constructed
568
+ * from the given module is traced — the one-liner path used by
569
+ * `introspection.init()`. Idempotent: a second call is a no-op.
570
+ *
571
+ * @param opts.anthropic The `@anthropic-ai/sdk` module's default export
572
+ * (the `Anthropic` class). Its `.Messages.prototype.create` is patched.
573
+ */
574
+ instrumentClass(opts: {
575
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
576
+ anthropic: any;
577
+ tracerProvider?: BasicTracerProvider;
578
+ conversationId?: string;
579
+ }): void {
580
+ const Messages = opts.anthropic?.Messages;
581
+ const proto = Messages?.prototype;
582
+ if (typeof proto?.create !== "function") {
583
+ throw new Error(
584
+ "Invalid Anthropic module — expected `Anthropic.Messages.prototype.create`",
416
585
  );
417
- setRequestAttrs(span, kwargs || {});
418
-
419
- if (isStream) {
420
- const streamPromise = origCreate(...args) as Promise<
421
- AsyncIterable<StreamEvent>
422
- >;
423
- return streamPromise.then(
424
- (stream: AsyncIterable<StreamEvent>) =>
425
- new TracedStream(stream, span),
426
- (err: Error) => {
427
- span.setStatus({
428
- code: SpanStatusCode.ERROR,
429
- message: String(err),
430
- });
431
- span.end();
432
- throw err;
433
- },
434
- );
435
- }
586
+ }
587
+ if (proto.create[PATCHED]) return;
436
588
 
437
- const resultPromise = origCreate(...args) as Promise<
438
- Record<string, unknown>
439
- >;
440
- return resultPromise.then(
441
- (response: Record<string, unknown>) => {
442
- setResponseAttrs(span, response);
443
- span.end();
444
- return response;
445
- },
446
- (err: Error) => {
447
- span.setStatus({
448
- code: SpanStatusCode.ERROR,
449
- message: String(err),
450
- });
451
- span.end();
452
- throw err;
453
- },
454
- );
455
- };
589
+ this.conversationId = opts.conversationId ?? crypto.randomUUID();
590
+ const provider = opts.tracerProvider ?? trace.getTracerProvider();
591
+ this.tracer = (provider as BasicTracerProvider).getTracer(
592
+ "introspection-anthropic",
593
+ );
594
+
595
+ const origCreate = proto.create;
596
+ const patched = buildTracedCreate(
597
+ origCreate,
598
+ this.tracer,
599
+ this.conversationId,
600
+ ) as ((...args: unknown[]) => unknown) & { [PATCHED]?: boolean };
601
+ patched[PATCHED] = true;
602
+ proto.create = patched;
603
+ this.protoRestores.push(() => {
604
+ proto.create = origCreate;
605
+ });
456
606
  }
457
607
 
458
608
  uninstrument(): void {
@@ -460,6 +610,8 @@ export class AnthropicInstrumentor {
460
610
  client.messages.create = originalCreate;
461
611
  }
462
612
  this.patchedClients = [];
613
+ for (const restore of this.protoRestores) restore();
614
+ this.protoRestores = [];
463
615
  this.tracer = null;
464
616
  }
465
617
  }