@introspection-sdk/introspection-node 0.20.0 → 0.21.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 (211) hide show
  1. package/README.md +28 -8
  2. package/dist/auth.d.ts +1 -1
  3. package/dist/client.d.ts +4 -3
  4. package/dist/client.d.ts.map +1 -1
  5. package/dist/client.js +16 -7
  6. package/dist/client.js.map +1 -1
  7. package/dist/dev-target.js +1 -1
  8. package/dist/index.d.ts +3 -7
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +2 -6
  11. package/dist/index.js.map +1 -1
  12. package/dist/otel/index.d.ts +1 -14
  13. package/dist/otel/index.d.ts.map +1 -1
  14. package/dist/otel/index.js +10 -18
  15. package/dist/otel/index.js.map +1 -1
  16. package/dist/otel/init.d.ts +19 -21
  17. package/dist/otel/init.d.ts.map +1 -1
  18. package/dist/otel/init.js +62 -48
  19. package/dist/otel/init.js.map +1 -1
  20. package/dist/otel/integrations/base.d.ts +17 -37
  21. package/dist/otel/integrations/base.d.ts.map +1 -1
  22. package/dist/otel/integrations/base.js +6 -13
  23. package/dist/otel/integrations/base.js.map +1 -1
  24. package/dist/otel/integrations/index.d.ts +3 -4
  25. package/dist/otel/integrations/index.d.ts.map +1 -1
  26. package/dist/otel/integrations/index.js +6 -25
  27. package/dist/otel/integrations/index.js.map +1 -1
  28. package/dist/otel/integrations/pi.d.ts.map +1 -1
  29. package/dist/otel/integrations/pi.js +9 -1
  30. package/dist/otel/integrations/pi.js.map +1 -1
  31. package/dist/otel/logs.d.ts +41 -8
  32. package/dist/otel/logs.d.ts.map +1 -1
  33. package/dist/otel/logs.js +95 -66
  34. package/dist/otel/logs.js.map +1 -1
  35. package/dist/otel/pi.d.ts +18 -5
  36. package/dist/otel/pi.d.ts.map +1 -1
  37. package/dist/otel/pi.js +38 -9
  38. package/dist/otel/pi.js.map +1 -1
  39. package/dist/otel/register.d.ts +2 -2
  40. package/dist/otel/register.d.ts.map +1 -1
  41. package/dist/otel/register.js +7 -9
  42. package/dist/otel/register.js.map +1 -1
  43. package/dist/otel/setup.d.ts +2 -2
  44. package/dist/otel/setup.d.ts.map +1 -1
  45. package/dist/otel/setup.js +16 -6
  46. package/dist/otel/setup.js.map +1 -1
  47. package/dist/otel/span-processor.d.ts +18 -5
  48. package/dist/otel/span-processor.d.ts.map +1 -1
  49. package/dist/otel/span-processor.js +50 -65
  50. package/dist/otel/span-processor.js.map +1 -1
  51. package/dist/resources/experiments.d.ts +10 -5
  52. package/dist/resources/experiments.d.ts.map +1 -1
  53. package/dist/resources/experiments.js +9 -25
  54. package/dist/resources/experiments.js.map +1 -1
  55. package/dist/resources/recipes.d.ts +6 -6
  56. package/dist/resources/recipes.d.ts.map +1 -1
  57. package/dist/resources/recipes.js +5 -23
  58. package/dist/resources/recipes.js.map +1 -1
  59. package/dist/resources/runtimes.d.ts +2 -2
  60. package/dist/resources/runtimes.d.ts.map +1 -1
  61. package/dist/resources/runtimes.js +2 -2
  62. package/dist/resources/runtimes.js.map +1 -1
  63. package/dist/runner.d.ts +8 -4
  64. package/dist/runner.d.ts.map +1 -1
  65. package/dist/runner.js +26 -18
  66. package/dist/runner.js.map +1 -1
  67. package/dist/types.d.ts +1 -1
  68. package/dist/types.d.ts.map +1 -1
  69. package/dist/types.js +1 -1
  70. package/dist/types.js.map +1 -1
  71. package/dist/utils.d.ts +45 -0
  72. package/dist/utils.d.ts.map +1 -1
  73. package/dist/utils.js +61 -2
  74. package/dist/utils.js.map +1 -1
  75. package/dist/version.d.ts +1 -1
  76. package/dist/version.d.ts.map +1 -1
  77. package/dist/version.js +1 -1
  78. package/dist/version.js.map +1 -1
  79. package/package.json +18 -60
  80. package/.turbo/turbo-build.log +0 -4
  81. package/dist/converters/claude.d.ts +0 -150
  82. package/dist/converters/claude.d.ts.map +0 -1
  83. package/dist/converters/claude.js +0 -211
  84. package/dist/converters/claude.js.map +0 -1
  85. package/dist/converters/gemini.d.ts +0 -90
  86. package/dist/converters/gemini.d.ts.map +0 -1
  87. package/dist/converters/gemini.js +0 -171
  88. package/dist/converters/gemini.js.map +0 -1
  89. package/dist/converters/index.d.ts +0 -9
  90. package/dist/converters/index.d.ts.map +0 -1
  91. package/dist/converters/index.js +0 -9
  92. package/dist/converters/index.js.map +0 -1
  93. package/dist/converters/openai.d.ts +0 -51
  94. package/dist/converters/openai.d.ts.map +0 -1
  95. package/dist/converters/openai.js +0 -269
  96. package/dist/converters/openai.js.map +0 -1
  97. package/dist/converters/openinference.d.ts +0 -107
  98. package/dist/converters/openinference.d.ts.map +0 -1
  99. package/dist/converters/openinference.js +0 -553
  100. package/dist/converters/openinference.js.map +0 -1
  101. package/dist/converters/vercel.d.ts +0 -21
  102. package/dist/converters/vercel.d.ts.map +0 -1
  103. package/dist/converters/vercel.js +0 -236
  104. package/dist/converters/vercel.js.map +0 -1
  105. package/dist/otel/anthropic.d.ts +0 -63
  106. package/dist/otel/anthropic.d.ts.map +0 -1
  107. package/dist/otel/anthropic.js +0 -475
  108. package/dist/otel/anthropic.js.map +0 -1
  109. package/dist/otel/claude-hooks.d.ts +0 -346
  110. package/dist/otel/claude-hooks.d.ts.map +0 -1
  111. package/dist/otel/claude-hooks.js +0 -647
  112. package/dist/otel/claude-hooks.js.map +0 -1
  113. package/dist/otel/claude-wrapper.d.ts +0 -93
  114. package/dist/otel/claude-wrapper.d.ts.map +0 -1
  115. package/dist/otel/claude-wrapper.js +0 -155
  116. package/dist/otel/claude-wrapper.js.map +0 -1
  117. package/dist/otel/gemini.d.ts +0 -56
  118. package/dist/otel/gemini.d.ts.map +0 -1
  119. package/dist/otel/gemini.js +0 -350
  120. package/dist/otel/gemini.js.map +0 -1
  121. package/dist/otel/integrations/anthropic.d.ts +0 -10
  122. package/dist/otel/integrations/anthropic.d.ts.map +0 -1
  123. package/dist/otel/integrations/anthropic.js +0 -26
  124. package/dist/otel/integrations/anthropic.js.map +0 -1
  125. package/dist/otel/integrations/claude-agent.d.ts +0 -12
  126. package/dist/otel/integrations/claude-agent.d.ts.map +0 -1
  127. package/dist/otel/integrations/claude-agent.js +0 -28
  128. package/dist/otel/integrations/claude-agent.js.map +0 -1
  129. package/dist/otel/integrations/gemini.d.ts +0 -11
  130. package/dist/otel/integrations/gemini.d.ts.map +0 -1
  131. package/dist/otel/integrations/gemini.js +0 -23
  132. package/dist/otel/integrations/gemini.js.map +0 -1
  133. package/dist/otel/integrations/langchain.d.ts +0 -15
  134. package/dist/otel/integrations/langchain.d.ts.map +0 -1
  135. package/dist/otel/integrations/langchain.js +0 -43
  136. package/dist/otel/integrations/langchain.js.map +0 -1
  137. package/dist/otel/integrations/mastra.d.ts +0 -14
  138. package/dist/otel/integrations/mastra.d.ts.map +0 -1
  139. package/dist/otel/integrations/mastra.js +0 -31
  140. package/dist/otel/integrations/mastra.js.map +0 -1
  141. package/dist/otel/integrations/openai-agents.d.ts +0 -11
  142. package/dist/otel/integrations/openai-agents.d.ts.map +0 -1
  143. package/dist/otel/integrations/openai-agents.js +0 -30
  144. package/dist/otel/integrations/openai-agents.js.map +0 -1
  145. package/dist/otel/integrations/vercel.d.ts +0 -14
  146. package/dist/otel/integrations/vercel.d.ts.map +0 -1
  147. package/dist/otel/integrations/vercel.js +0 -20
  148. package/dist/otel/integrations/vercel.js.map +0 -1
  149. package/dist/otel/langchain-handler.d.ts +0 -112
  150. package/dist/otel/langchain-handler.d.ts.map +0 -1
  151. package/dist/otel/langchain-handler.js +0 -561
  152. package/dist/otel/langchain-handler.js.map +0 -1
  153. package/dist/otel/mastra-exporter.d.ts +0 -87
  154. package/dist/otel/mastra-exporter.d.ts.map +0 -1
  155. package/dist/otel/mastra-exporter.js +0 -530
  156. package/dist/otel/mastra-exporter.js.map +0 -1
  157. package/dist/otel/openai.d.ts +0 -20
  158. package/dist/otel/openai.d.ts.map +0 -1
  159. package/dist/otel/openai.js +0 -55
  160. package/dist/otel/openai.js.map +0 -1
  161. package/dist/otel/tracing-processor.d.ts +0 -139
  162. package/dist/otel/tracing-processor.d.ts.map +0 -1
  163. package/dist/otel/tracing-processor.js +0 -434
  164. package/dist/otel/tracing-processor.js.map +0 -1
  165. package/eslint.config.mjs +0 -4
  166. package/src/auth.ts +0 -236
  167. package/src/client.ts +0 -163
  168. package/src/converters/claude.ts +0 -343
  169. package/src/converters/gemini.ts +0 -243
  170. package/src/converters/index.ts +0 -50
  171. package/src/converters/openai.ts +0 -300
  172. package/src/converters/openinference.ts +0 -665
  173. package/src/converters/vercel.ts +0 -302
  174. package/src/dev-target.ts +0 -79
  175. package/src/http.ts +0 -43
  176. package/src/index.ts +0 -277
  177. package/src/otel/anthropic.ts +0 -619
  178. package/src/otel/claude-hooks.ts +0 -1116
  179. package/src/otel/claude-wrapper.ts +0 -239
  180. package/src/otel/gemini.ts +0 -483
  181. package/src/otel/index.ts +0 -128
  182. package/src/otel/init.ts +0 -411
  183. package/src/otel/integrations/anthropic.ts +0 -36
  184. package/src/otel/integrations/base.ts +0 -158
  185. package/src/otel/integrations/claude-agent.ts +0 -36
  186. package/src/otel/integrations/gemini.ts +0 -30
  187. package/src/otel/integrations/index.ts +0 -152
  188. package/src/otel/integrations/langchain.ts +0 -60
  189. package/src/otel/integrations/mastra.ts +0 -48
  190. package/src/otel/integrations/openai-agents.ts +0 -47
  191. package/src/otel/integrations/pi.ts +0 -28
  192. package/src/otel/integrations/vercel.ts +0 -26
  193. package/src/otel/langchain-handler.ts +0 -804
  194. package/src/otel/logs.ts +0 -395
  195. package/src/otel/mastra-exporter.ts +0 -714
  196. package/src/otel/openai.ts +0 -93
  197. package/src/otel/pi.ts +0 -94
  198. package/src/otel/register.ts +0 -50
  199. package/src/otel/setup.ts +0 -215
  200. package/src/otel/span-processor.ts +0 -512
  201. package/src/otel/tracing-processor.ts +0 -637
  202. package/src/pagination.ts +0 -10
  203. package/src/proxy-from-env.d.ts +0 -7
  204. package/src/resources/experiments.ts +0 -176
  205. package/src/resources/recipes.ts +0 -74
  206. package/src/resources/runtimes.ts +0 -31
  207. package/src/runner.ts +0 -213
  208. package/src/types.ts +0 -13
  209. package/src/utils.ts +0 -95
  210. package/src/version.ts +0 -2
  211. package/tsconfig.json +0 -8
@@ -1,483 +0,0 @@
1
- /**
2
- * Lightweight Google Gemini (`@google/genai`) instrumentor for Introspection SDK.
3
- *
4
- * Captures the full Gemini response including per-part `thoughtSignature`
5
- * payloads (Gemini 3.x extended thinking) as gen_ai `reasoning` parts with
6
- * `signature` set — mirroring how the {@link AnthropicInstrumentor} captures
7
- * Anthropic thinking blocks.
8
- *
9
- * Supports both non-streaming (`client.models.generateContent`) and streaming
10
- * (`client.models.generateContentStream`) calls.
11
- *
12
- * @example
13
- * ```ts
14
- * import { GoogleGenAI } from "@google/genai";
15
- * import { GeminiInstrumentor } from "@introspection-sdk/introspection-node";
16
- *
17
- * const client = new GoogleGenAI({ apiKey: process.env.GEMINI_API_KEY });
18
- * new GeminiInstrumentor().instrument({ tracerProvider: provider, client });
19
- * // All client.models.generateContent / generateContentStream calls are now traced
20
- * ```
21
- *
22
- * @see https://ai.google.dev/gemini-api/docs/thought-signatures
23
- */
24
-
25
- import { trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
26
- import type { Tracer, Span, TracerProvider } from "@opentelemetry/api";
27
-
28
- import type { InputMessage, OutputMessage } from "@introspection-sdk/types";
29
- import { providerCostAttributes } from "@introspection-sdk/types";
30
- import {
31
- convertGeminiContentsToInputMessages,
32
- convertGeminiCandidatesToOutputMessages,
33
- convertGeminiSystemInstructionToSemconv,
34
- convertGeminiToolsToToolDefinitions,
35
- GEMINI_PROVIDER_NAME,
36
- type GeminiCandidate,
37
- type GeminiContent,
38
- type GeminiPart,
39
- type GeminiTool,
40
- } from "../converters/gemini.js";
41
-
42
- // ---------------------------------------------------------------------------
43
- // Span helpers
44
- // ---------------------------------------------------------------------------
45
-
46
- function serializeMessages(msgs: InputMessage[] | OutputMessage[]): string {
47
- return JSON.stringify(
48
- msgs.map((m) => {
49
- const obj: Record<string, unknown> = { role: m.role, parts: m.parts };
50
- if ("finish_reason" in m && m.finish_reason) {
51
- obj.finish_reason = m.finish_reason;
52
- }
53
- return obj;
54
- }),
55
- );
56
- }
57
-
58
- function startSpan(
59
- tracer: Tracer,
60
- model: string,
61
- conversationId?: string,
62
- ): Span {
63
- return tracer.startSpan("chat", {
64
- kind: SpanKind.CLIENT,
65
- attributes: {
66
- "gen_ai.provider.name": GEMINI_PROVIDER_NAME,
67
- "gen_ai.operation.name": "chat",
68
- "gen_ai.request.model": model,
69
- "openinference.span.kind": "LLM",
70
- ...(conversationId
71
- ? { "gen_ai.conversation.id": conversationId }
72
- : undefined),
73
- },
74
- });
75
- }
76
-
77
- function setRequestAttrs(span: Span, kwargs: Record<string, unknown>): void {
78
- const contents = kwargs.contents as
79
- string | GeminiContent[] | GeminiContent | undefined;
80
- const inputMsgs = convertGeminiContentsToInputMessages(contents);
81
- if (inputMsgs.length > 0) {
82
- span.setAttribute("gen_ai.input.messages", serializeMessages(inputMsgs));
83
- }
84
-
85
- const config = (kwargs.config as Record<string, unknown> | undefined) || {};
86
-
87
- const systemInstruction = config.systemInstruction as
88
- string | GeminiContent | undefined;
89
- const systemInstructions =
90
- convertGeminiSystemInstructionToSemconv(systemInstruction);
91
- if (systemInstructions && systemInstructions.length > 0) {
92
- span.setAttribute(
93
- "gen_ai.system_instructions",
94
- JSON.stringify(systemInstructions),
95
- );
96
- }
97
-
98
- const tools = config.tools as GeminiTool[] | undefined;
99
- const toolDefs = convertGeminiToolsToToolDefinitions(tools);
100
- if (toolDefs && toolDefs.length > 0) {
101
- span.setAttribute("gen_ai.tool.definitions", JSON.stringify(toolDefs));
102
- }
103
- }
104
-
105
- function setUsageAttrs(
106
- span: Span,
107
- usage: Record<string, unknown> | undefined,
108
- ): void {
109
- if (!usage) return;
110
- const promptTokens = usage.promptTokenCount as number | undefined;
111
- const candidateTokens = usage.candidatesTokenCount as number | undefined;
112
- const cachedTokens = usage.cachedContentTokenCount as number | undefined;
113
- const thoughtTokens = usage.thoughtsTokenCount as number | undefined;
114
- if (typeof promptTokens === "number") {
115
- span.setAttribute("gen_ai.usage.input_tokens", promptTokens);
116
- }
117
- if (typeof candidateTokens === "number") {
118
- // Gemini reports candidatesTokenCount separately from thoughtsTokenCount;
119
- // sum them so output_tokens reflects everything billed as model output.
120
- const total = candidateTokens + (thoughtTokens ?? 0);
121
- span.setAttribute("gen_ai.usage.output_tokens", total);
122
- } else if (typeof thoughtTokens === "number") {
123
- span.setAttribute("gen_ai.usage.output_tokens", thoughtTokens);
124
- }
125
- if (typeof cachedTokens === "number") {
126
- span.setAttribute("gen_ai.usage.cache_read.input_tokens", cachedTokens);
127
- }
128
- // Provider-reported cost (e.g. OpenRouter usage.cost) when present.
129
- span.setAttributes(providerCostAttributes(usage));
130
- }
131
-
132
- function setResponseAttrs(span: Span, response: Record<string, unknown>): void {
133
- const candidates = response.candidates as GeminiCandidate[] | undefined;
134
- const outputMsgs = convertGeminiCandidatesToOutputMessages(candidates);
135
- if (outputMsgs.length > 0) {
136
- span.setAttribute("gen_ai.output.messages", serializeMessages(outputMsgs));
137
- const finishReasons = outputMsgs
138
- .map((m) => m.finish_reason)
139
- .filter((r): r is string => typeof r === "string");
140
- if (finishReasons.length > 0) {
141
- span.setAttribute("gen_ai.response.finish_reasons", finishReasons);
142
- }
143
- }
144
-
145
- const responseId = response.responseId as string | undefined;
146
- if (responseId) span.setAttribute("gen_ai.response.id", responseId);
147
-
148
- const modelVersion = response.modelVersion as string | undefined;
149
- if (modelVersion) span.setAttribute("gen_ai.response.model", modelVersion);
150
-
151
- setUsageAttrs(span, response.usageMetadata as Record<string, unknown>);
152
-
153
- span.setStatus({ code: SpanStatusCode.OK });
154
- }
155
-
156
- // ---------------------------------------------------------------------------
157
- // Streaming aggregator
158
- // ---------------------------------------------------------------------------
159
-
160
- interface StreamChunk {
161
- candidates?: GeminiCandidate[];
162
- usageMetadata?: Record<string, unknown>;
163
- responseId?: string;
164
- modelVersion?: string;
165
- }
166
-
167
- /** True if a streamed part is just incremental text with no metadata to preserve. */
168
- function isPlainTextPart(part: GeminiPart): boolean {
169
- return (
170
- typeof part.text === "string" &&
171
- part.thought !== true &&
172
- !part.thoughtSignature &&
173
- !part.functionCall &&
174
- !part.functionResponse
175
- );
176
- }
177
-
178
- /**
179
- * Async-iterable wrapper that aggregates streamed `GenerateContentResponse`
180
- * chunks so the underlying span captures the complete final message — including
181
- * the final per-part `thoughtSignature` values that only arrive on the last
182
- * chunk of a streamed turn.
183
- */
184
- class TracedStream implements AsyncIterable<StreamChunk> {
185
- private aggregated: Map<number, GeminiCandidate> = new Map();
186
- private usage: Record<string, unknown> | undefined;
187
- private responseId: string | undefined;
188
- private modelVersion: string | undefined;
189
- private finalized = false;
190
-
191
- constructor(
192
- private readonly inner: AsyncIterable<StreamChunk>,
193
- private readonly span: Span,
194
- ) {}
195
-
196
- async *[Symbol.asyncIterator](): AsyncIterableIterator<StreamChunk> {
197
- try {
198
- for await (const chunk of this.inner) {
199
- this.processChunk(chunk);
200
- yield chunk;
201
- }
202
- } catch (err) {
203
- this.span.setStatus({
204
- code: SpanStatusCode.ERROR,
205
- message: String(err),
206
- });
207
- this.span.end();
208
- this.finalized = true;
209
- throw err;
210
- } finally {
211
- this.finalize();
212
- }
213
- }
214
-
215
- private processChunk(chunk: StreamChunk): void {
216
- if (chunk.responseId) this.responseId = chunk.responseId;
217
- if (chunk.modelVersion) this.modelVersion = chunk.modelVersion;
218
- if (chunk.usageMetadata) this.usage = chunk.usageMetadata;
219
- for (const cand of chunk.candidates ?? []) {
220
- const idx = cand.index ?? 0;
221
- const existing = this.aggregated.get(idx);
222
- if (!existing) {
223
- this.aggregated.set(idx, {
224
- index: idx,
225
- finishReason: cand.finishReason,
226
- content: {
227
- role: cand.content?.role,
228
- parts: [...(cand.content?.parts ?? [])],
229
- },
230
- });
231
- continue;
232
- }
233
- if (cand.finishReason) existing.finishReason = cand.finishReason;
234
- if (cand.content?.role && !existing.content?.role) {
235
- existing.content!.role = cand.content.role;
236
- }
237
- this.mergeParts(
238
- existing.content!.parts as GeminiPart[],
239
- cand.content?.parts ?? [],
240
- );
241
- }
242
- }
243
-
244
- /**
245
- * Merge streamed parts into the aggregated parts list.
246
- *
247
- * Gemini streams text incrementally as separate `text`-only parts; thought
248
- * signatures and function calls arrive as their own parts. Consecutive
249
- * plain-text parts are coalesced into a single accumulating text part so the
250
- * final thought signature on a block survives on its trailing part.
251
- */
252
- private mergeParts(target: GeminiPart[], incoming: GeminiPart[]): void {
253
- for (const part of incoming) {
254
- const last = target[target.length - 1];
255
- if (isPlainTextPart(part) && last && isPlainTextPart(last)) {
256
- last.text = (last.text || "") + (part.text || "");
257
- continue;
258
- }
259
- target.push({ ...part });
260
- }
261
- }
262
-
263
- private finalize(): void {
264
- if (this.finalized) return;
265
- this.finalized = true;
266
- setResponseAttrs(this.span, {
267
- candidates: Array.from(this.aggregated.values()),
268
- usageMetadata: this.usage,
269
- responseId: this.responseId,
270
- modelVersion: this.modelVersion,
271
- });
272
- this.span.end();
273
- }
274
- }
275
-
276
- // ---------------------------------------------------------------------------
277
- // Public API
278
- // ---------------------------------------------------------------------------
279
-
280
- // The instrumentor patches `client.models.generateContent[Stream]` in place.
281
- // We accept the client as `any` (matching AnthropicInstrumentor) so the typed
282
- // SDK shape from `@google/genai` is compatible without consumers needing
283
- // to cast at the call site.
284
- interface GeminiModelsApi {
285
- generateContent: (...args: unknown[]) => Promise<unknown>;
286
- generateContentStream?: (...args: unknown[]) => Promise<unknown>;
287
- }
288
-
289
- /**
290
- * Auto-instrumentor that wraps a `@google/genai` client instance to add
291
- * Introspection tracing with full thought-signature capture.
292
- *
293
- * Captures all content parts including thought summaries and signed
294
- * `thoughtSignature` payloads required for multi-turn replay.
295
- */
296
- /**
297
- * Build the traced replacement for a non-streaming `generateContent`-style
298
- * method. Shared by {@link GeminiInstrumentor.instrument} (patches a client's
299
- * public `models.generateContent`) and {@link GeminiInstrumentor.instrumentClass}
300
- * (patches `Models.prototype.generateContentInternal` so every client is
301
- * auto-traced). Preserves the call-site `this`.
302
- */
303
- function buildTracedGenerate(
304
- origGenerate: (...args: unknown[]) => Promise<unknown>,
305
- tracer: Tracer,
306
- conversationId: string,
307
- ): (...args: unknown[]) => Promise<unknown> {
308
- return async function tracedGenerate(this: unknown, ...args: unknown[]) {
309
- const kwargs = (args[0] as Record<string, unknown>) || {};
310
- const span = startSpan(
311
- tracer,
312
- (kwargs.model as string) || "unknown",
313
- conversationId,
314
- );
315
- setRequestAttrs(span, kwargs);
316
- try {
317
- const response = (await origGenerate.apply(this, args)) as Record<
318
- string,
319
- unknown
320
- >;
321
- setResponseAttrs(span, response);
322
- return response;
323
- } catch (err) {
324
- span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
325
- throw err;
326
- } finally {
327
- span.end();
328
- }
329
- };
330
- }
331
-
332
- /** Build the traced replacement for a streaming `generateContentStream`-style method. */
333
- function buildTracedGenerateStream(
334
- origStream: (...args: unknown[]) => Promise<unknown>,
335
- tracer: Tracer,
336
- conversationId: string,
337
- ): (...args: unknown[]) => Promise<unknown> {
338
- return async function tracedGenerateStream(
339
- this: unknown,
340
- ...args: unknown[]
341
- ) {
342
- const kwargs = (args[0] as Record<string, unknown>) || {};
343
- const span = startSpan(
344
- tracer,
345
- (kwargs.model as string) || "unknown",
346
- conversationId,
347
- );
348
- setRequestAttrs(span, kwargs);
349
- try {
350
- const stream = (await origStream.apply(
351
- this,
352
- args,
353
- )) as AsyncIterable<StreamChunk>;
354
- return new TracedStream(stream, span);
355
- } catch (err) {
356
- span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
357
- span.end();
358
- throw err;
359
- }
360
- };
361
- }
362
-
363
- /**
364
- * Marker placed on a patched `Models.prototype` method so repeated
365
- * `instrumentClass()` calls (e.g. a second `introspection.init()`) are no-ops.
366
- */
367
- const PATCHED = Symbol.for("introspection.gemini.patched");
368
-
369
- export class GeminiInstrumentor {
370
- private tracer: Tracer | null = null;
371
- private restores: Array<() => void> = [];
372
- private conversationId: string | undefined;
373
-
374
- instrument(opts: {
375
- tracerProvider?: TracerProvider;
376
- /** A `@google/genai` `GoogleGenAI` client instance. */
377
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
378
- client: any;
379
- /**
380
- * Conversation ID shared across all spans produced by this instrumentor.
381
- * Defaults to a random UUID generated at `instrument()` time.
382
- */
383
- conversationId?: string;
384
- }): void {
385
- const { client } = opts;
386
- if (!client?.models?.generateContent) {
387
- throw new Error(
388
- "Invalid client — pass a GoogleGenAI client instance (new GoogleGenAI({...}))",
389
- );
390
- }
391
-
392
- this.conversationId = opts.conversationId ?? crypto.randomUUID();
393
-
394
- const provider = opts.tracerProvider ?? trace.getTracerProvider();
395
- this.tracer = provider.getTracer("introspection-gemini");
396
- const tracer = this.tracer;
397
- const conversationId = this.conversationId;
398
-
399
- const api = client.models as GeminiModelsApi;
400
- const origGenerate = api.generateContent.bind(api);
401
- const origGenerateStream = api.generateContentStream?.bind(api);
402
- this.restores.push(() => {
403
- api.generateContent = origGenerate;
404
- if (origGenerateStream) api.generateContentStream = origGenerateStream;
405
- });
406
-
407
- api.generateContent = buildTracedGenerate(
408
- origGenerate,
409
- tracer,
410
- conversationId,
411
- ) as GeminiModelsApi["generateContent"];
412
-
413
- if (origGenerateStream) {
414
- api.generateContentStream = buildTracedGenerateStream(
415
- origGenerateStream,
416
- tracer,
417
- conversationId,
418
- ) as GeminiModelsApi["generateContentStream"];
419
- }
420
- }
421
-
422
- /**
423
- * Patch `Models.prototype.generateContentInternal` / `…StreamInternal` so
424
- * **every** `GoogleGenAI` client is traced — the one-liner path used by
425
- * `introspection.init()`. The public `generateContent` /
426
- * `generateContentStream` are per-instance bound class fields that delegate
427
- * to these prototype methods, so patching the prototype covers all clients.
428
- * Idempotent: a second call is a no-op.
429
- *
430
- * @param opts.genai The `@google/genai` module (must expose `Models`).
431
- */
432
- instrumentClass(opts: {
433
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
434
- genai: any;
435
- tracerProvider?: TracerProvider;
436
- conversationId?: string;
437
- }): void {
438
- const proto = opts.genai?.Models?.prototype;
439
- if (typeof proto?.generateContentInternal !== "function") {
440
- throw new Error(
441
- "Invalid @google/genai module — expected `Models.prototype.generateContentInternal`",
442
- );
443
- }
444
- if (proto.generateContentInternal[PATCHED]) return;
445
-
446
- this.conversationId = opts.conversationId ?? crypto.randomUUID();
447
- const provider = opts.tracerProvider ?? trace.getTracerProvider();
448
- this.tracer = provider.getTracer("introspection-gemini");
449
- const conversationId = this.conversationId;
450
-
451
- const origGenerate = proto.generateContentInternal;
452
- const patchedGenerate = buildTracedGenerate(
453
- origGenerate,
454
- this.tracer,
455
- conversationId,
456
- ) as ((...args: unknown[]) => Promise<unknown>) & { [PATCHED]?: boolean };
457
- patchedGenerate[PATCHED] = true;
458
- proto.generateContentInternal = patchedGenerate;
459
- this.restores.push(() => {
460
- proto.generateContentInternal = origGenerate;
461
- });
462
-
463
- if (typeof proto.generateContentStreamInternal === "function") {
464
- const origStream = proto.generateContentStreamInternal;
465
- const patchedStream = buildTracedGenerateStream(
466
- origStream,
467
- this.tracer,
468
- conversationId,
469
- ) as ((...args: unknown[]) => Promise<unknown>) & { [PATCHED]?: boolean };
470
- patchedStream[PATCHED] = true;
471
- proto.generateContentStreamInternal = patchedStream;
472
- this.restores.push(() => {
473
- proto.generateContentStreamInternal = origStream;
474
- });
475
- }
476
- }
477
-
478
- uninstrument(): void {
479
- for (const restore of this.restores) restore();
480
- this.restores = [];
481
- this.tracer = null;
482
- }
483
- }
package/src/otel/index.ts DELETED
@@ -1,128 +0,0 @@
1
- /**
2
- * OpenTelemetry surface for the Introspection Node SDK.
3
- *
4
- * Subpath entry point: `@introspection-sdk/introspection-node/otel`.
5
- *
6
- * Importing from here pulls in OTel SDK packages (peer deps). The
7
- * REST-only `IntrospectionClient` is available from the package root
8
- * and does not require these to be installed.
9
- */
10
-
11
- // IntrospectionLogs — OTel logs exporter with track/feedback/identify
12
- // and baggage context helpers (extracted from the old IntrospectionClient).
13
- export { IntrospectionLogs } from "./logs.js";
14
- export type { IntrospectionLogsOptions } from "./logs.js";
15
-
16
- // One-call tracing bootstrap (NodeTracerProvider + AsyncLocalStorage +
17
- // W3C baggage propagator + IntrospectionSpanProcessor).
18
- export { setupTracing } from "./setup.js";
19
- export type { SetupTracingOptions, ConflictBehavior } from "./setup.js";
20
-
21
- // Span processor — attach to your own TracerProvider.
22
- export { IntrospectionSpanProcessor } from "./span-processor.js";
23
- export type { IntrospectionSpanProcessorOptions } from "./span-processor.js";
24
-
25
- // OpenAI embeddings wrapper (metadata-only; never records inputs or vectors).
26
- export { tracedEmbeddingsCreate } from "./openai.js";
27
- export type {
28
- OpenAIEmbeddingsClient,
29
- TracedEmbeddingsOptions,
30
- } from "./openai.js";
31
-
32
- // OpenAI Agents SDK tracing processor.
33
- export { IntrospectionTracingProcessor } from "./tracing-processor.js";
34
- export type {
35
- IntrospectionTracingProcessorOptions,
36
- TracingProcessorAdvancedOptions,
37
- } from "./tracing-processor.js";
38
-
39
- // Claude Agent SDK hooks.
40
- export { IntrospectionClaudeHooks } from "./claude-hooks.js";
41
- export type {
42
- IntrospectionClaudeHooksOptions,
43
- ClaudeHooksAdvancedOptions,
44
- ClaudeHooksConfig,
45
- ClaudeHookCallbackMatcher,
46
- ClaudeHookEvent,
47
- ClaudeHookInput,
48
- ClaudeHookOutput,
49
- ClaudeHookCallback,
50
- ClaudeUsage,
51
- ClaudeModelUsage,
52
- ClaudeResultMessage,
53
- ClaudeAssistantMessage,
54
- ClaudeSDKMessage,
55
- } from "./claude-hooks.js";
56
-
57
- // Claude Agent SDK wrapper.
58
- export { withIntrospection } from "./claude-wrapper.js";
59
- export type {
60
- WithIntrospectionOptions,
61
- InstrumentedClaudeAgentSDK,
62
- ClaudeAgentSDKModule,
63
- } from "./claude-wrapper.js";
64
-
65
- // Anthropic SDK instrumentor.
66
- export {
67
- AnthropicInstrumentor,
68
- tracedMessagesCreate,
69
- REDACTED_THINKING_CONTENT,
70
- } from "./anthropic.js";
71
-
72
- // Pi Agent SDK instrumentor.
73
- export { IntrospectionPiInstrumentor } from "./pi.js";
74
- export type {
75
- IntrospectionPiInstrumentorOptions,
76
- AgentMeta as PiAgentMeta,
77
- } from "./pi.js";
78
-
79
- // Gemini SDK instrumentor.
80
- export { GeminiInstrumentor } from "./gemini.js";
81
-
82
- // OpenInference span exporter (drops Arize/OpenInference attrs onto a
83
- // downstream OTel SpanExporter).
84
- export {
85
- addOpenInferenceAttributes,
86
- OpenInferenceSpanExporter,
87
- } from "../converters/openinference.js";
88
-
89
- // One-liner bootstrap (`introspection.init()`) + analytics proxies and the
90
- // per-framework handle accessors it binds. Auto-detects installed frameworks
91
- // and wires them into one shared provider.
92
- export {
93
- init,
94
- shutdown,
95
- track,
96
- feedback,
97
- identify,
98
- conversation,
99
- withAgent,
100
- withConversation,
101
- withUserId,
102
- withAnonymousId,
103
- newConversationId,
104
- getClient,
105
- getTracerProvider,
106
- getLangchainHandler,
107
- getMastraExporter,
108
- instrumentPi,
109
- instrumentClaudeAgent,
110
- _resetForTests,
111
- } from "./init.js";
112
- export type { InitOptions } from "./init.js";
113
-
114
- // Low-level OTel registration helper (also used by `init`).
115
- export { registerOTelGlobals } from "./setup.js";
116
-
117
- // Integration registry — for custom integrations passed to `init`.
118
- export {
119
- DidNotEnable,
120
- discoverIntegrations,
121
- setupIntegrations,
122
- resetInstalledForTests,
123
- } from "./integrations/index.js";
124
- export type {
125
- Integration,
126
- IntegrationSetupContext,
127
- IntegrationHandles,
128
- } from "./integrations/index.js";