@introspection-sdk/introspection-node 0.4.3 → 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 (122) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/otel/anthropic.d.ts +14 -12
  3. package/dist/otel/anthropic.d.ts.map +1 -1
  4. package/dist/otel/anthropic.js +89 -47
  5. package/dist/otel/anthropic.js.map +1 -1
  6. package/dist/otel/gemini.d.ts +15 -7
  7. package/dist/otel/gemini.d.ts.map +1 -1
  8. package/dist/otel/gemini.js +88 -37
  9. package/dist/otel/gemini.js.map +1 -1
  10. package/dist/otel/index.d.ts +5 -0
  11. package/dist/otel/index.d.ts.map +1 -1
  12. package/dist/otel/index.js +8 -0
  13. package/dist/otel/index.js.map +1 -1
  14. package/dist/otel/init.d.ts +126 -0
  15. package/dist/otel/init.d.ts.map +1 -0
  16. package/dist/otel/init.js +259 -0
  17. package/dist/otel/init.js.map +1 -0
  18. package/dist/otel/integrations/anthropic.d.ts +10 -0
  19. package/dist/otel/integrations/anthropic.d.ts.map +1 -0
  20. package/dist/otel/integrations/anthropic.js +22 -0
  21. package/dist/otel/integrations/anthropic.js.map +1 -0
  22. package/dist/otel/integrations/base.d.ts +83 -0
  23. package/dist/otel/integrations/base.d.ts.map +1 -0
  24. package/dist/otel/integrations/base.js +23 -0
  25. package/dist/otel/integrations/base.js.map +1 -0
  26. package/dist/otel/integrations/claude-agent.d.ts +13 -0
  27. package/dist/otel/integrations/claude-agent.d.ts.map +1 -0
  28. package/dist/otel/integrations/claude-agent.js +27 -0
  29. package/dist/otel/integrations/claude-agent.js.map +1 -0
  30. package/dist/otel/integrations/gemini.d.ts +11 -0
  31. package/dist/otel/integrations/gemini.d.ts.map +1 -0
  32. package/dist/otel/integrations/gemini.js +21 -0
  33. package/dist/otel/integrations/gemini.js.map +1 -0
  34. package/dist/otel/integrations/index.d.ts +39 -0
  35. package/dist/otel/integrations/index.d.ts.map +1 -0
  36. package/dist/otel/integrations/index.js +131 -0
  37. package/dist/otel/integrations/index.js.map +1 -0
  38. package/dist/otel/integrations/langchain.d.ts +16 -0
  39. package/dist/otel/integrations/langchain.d.ts.map +1 -0
  40. package/dist/otel/integrations/langchain.js +41 -0
  41. package/dist/otel/integrations/langchain.js.map +1 -0
  42. package/dist/otel/integrations/mastra.d.ts +14 -0
  43. package/dist/otel/integrations/mastra.d.ts.map +1 -0
  44. package/dist/otel/integrations/mastra.js +27 -0
  45. package/dist/otel/integrations/mastra.js.map +1 -0
  46. package/dist/otel/integrations/openai-agents.d.ts +11 -0
  47. package/dist/otel/integrations/openai-agents.d.ts.map +1 -0
  48. package/dist/otel/integrations/openai-agents.js +25 -0
  49. package/dist/otel/integrations/openai-agents.js.map +1 -0
  50. package/dist/otel/integrations/pi.d.ts +12 -0
  51. package/dist/otel/integrations/pi.d.ts.map +1 -0
  52. package/dist/otel/integrations/pi.js +20 -0
  53. package/dist/otel/integrations/pi.js.map +1 -0
  54. package/dist/otel/integrations/vercel.d.ts +15 -0
  55. package/dist/otel/integrations/vercel.d.ts.map +1 -0
  56. package/dist/otel/integrations/vercel.js +20 -0
  57. package/dist/otel/integrations/vercel.js.map +1 -0
  58. package/dist/otel/register.d.ts +8 -0
  59. package/dist/otel/register.d.ts.map +1 -0
  60. package/dist/otel/register.js +48 -0
  61. package/dist/otel/register.js.map +1 -0
  62. package/dist/otel/setup.d.ts +29 -0
  63. package/dist/otel/setup.d.ts.map +1 -1
  64. package/dist/otel/setup.js +22 -3
  65. package/dist/otel/setup.js.map +1 -1
  66. package/dist/utils.d.ts +10 -0
  67. package/dist/utils.d.ts.map +1 -1
  68. package/dist/utils.js +12 -4
  69. package/dist/utils.js.map +1 -1
  70. package/package.json +17 -5
  71. package/src/otel/anthropic.ts +128 -63
  72. package/src/otel/gemini.ts +139 -45
  73. package/src/otel/index.ts +41 -0
  74. package/src/otel/init.ts +411 -0
  75. package/src/otel/integrations/anthropic.ts +26 -0
  76. package/src/otel/integrations/base.ts +95 -0
  77. package/src/otel/integrations/claude-agent.ts +32 -0
  78. package/src/otel/integrations/gemini.ts +25 -0
  79. package/src/otel/integrations/index.ts +151 -0
  80. package/src/otel/integrations/langchain.ts +45 -0
  81. package/src/otel/integrations/mastra.ts +30 -0
  82. package/src/otel/integrations/openai-agents.ts +31 -0
  83. package/src/otel/integrations/pi.ts +24 -0
  84. package/src/otel/integrations/vercel.ts +24 -0
  85. package/src/otel/register.ts +50 -0
  86. package/src/otel/setup.ts +44 -3
  87. package/src/utils.ts +12 -5
  88. package/.turbo/turbo-check-types.log +0 -4
  89. package/.turbo/turbo-lint.log +0 -5
  90. package/dist/ai-sdk-integration.d.ts +0 -123
  91. package/dist/ai-sdk-integration.d.ts.map +0 -1
  92. package/dist/ai-sdk-integration.js +0 -475
  93. package/dist/anthropic.d.ts +0 -54
  94. package/dist/anthropic.d.ts.map +0 -1
  95. package/dist/anthropic.js +0 -375
  96. package/dist/claude-hooks.d.ts +0 -328
  97. package/dist/claude-hooks.d.ts.map +0 -1
  98. package/dist/claude-hooks.js +0 -571
  99. package/dist/claude-wrapper.d.ts +0 -93
  100. package/dist/claude-wrapper.d.ts.map +0 -1
  101. package/dist/claude-wrapper.js +0 -154
  102. package/dist/converters/ai-sdk.d.ts +0 -65
  103. package/dist/converters/ai-sdk.d.ts.map +0 -1
  104. package/dist/converters/ai-sdk.js +0 -261
  105. package/dist/converters/openclaw.d.ts +0 -16
  106. package/dist/converters/openclaw.d.ts.map +0 -1
  107. package/dist/converters/openclaw.js +0 -49
  108. package/dist/langchain-handler.d.ts +0 -103
  109. package/dist/langchain-handler.d.ts.map +0 -1
  110. package/dist/langchain-handler.js +0 -518
  111. package/dist/mastra-exporter.d.ts +0 -85
  112. package/dist/mastra-exporter.d.ts.map +0 -1
  113. package/dist/mastra-exporter.js +0 -471
  114. package/dist/span-processor.d.ts +0 -62
  115. package/dist/span-processor.d.ts.map +0 -1
  116. package/dist/span-processor.js +0 -313
  117. package/dist/tracing-processor.d.ts +0 -136
  118. package/dist/tracing-processor.d.ts.map +0 -1
  119. package/dist/tracing-processor.js +0 -368
  120. package/dist/types/genai.d.ts +0 -165
  121. package/dist/types/genai.d.ts.map +0 -1
  122. package/dist/types/genai.js +0 -82
package/dist/utils.js CHANGED
@@ -1,10 +1,18 @@
1
1
  import { DiagLogLevel } from "@opentelemetry/api";
2
2
  import { HttpsProxyAgent } from "https-proxy-agent";
3
- function getHttpsProxyUrl() {
4
- return process.env.HTTPS_PROXY || undefined;
5
- }
3
+ import { resolveForwardProxyUrl } from "@introspection-sdk/introspection-proxy";
4
+ /**
5
+ * Attach a forward-proxy agent to OTLP exporter options when a proxy is
6
+ * configured in the environment.
7
+ *
8
+ * Note: the OTLP proto exporters use Node's `http`/`https` stack, so this uses
9
+ * `HttpsProxyAgent` (an `http.Agent`) rather than the undici dispatcher from
10
+ * `./proxy.js` — a dispatcher only applies to `fetch`. The proxy *URL* is
11
+ * resolved by the shared {@link resolveForwardProxyUrl} so fetch and OTLP
12
+ * traffic agree on which proxy to use.
13
+ */
6
14
  export function withOtlpHttpsProxy(options) {
7
- const proxyUrl = getHttpsProxyUrl();
15
+ const proxyUrl = resolveForwardProxyUrl();
8
16
  if (!proxyUrl)
9
17
  return options;
10
18
  return {
package/dist/utils.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAc,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAEpD,SAAS,gBAAgB;IACvB,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,IAAI,SAAS,CAAC;AAC9C,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAmB,OAAU;IAC7D,MAAM,QAAQ,GAAG,gBAAgB,EAAE,CAAC;IACpC,IAAI,CAAC,QAAQ;QAAE,OAAO,OAAO,CAAC;IAE9B,OAAO;QACL,GAAG,OAAO;QACV,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,QAAQ,CAAC;KACjD,CAAC;AACT,CAAC;AAED;;;GAGG;AACH,MAAM,mBAAmB;IACf,QAAQ,CAAe;IAE/B;QACE,MAAM,WAAW,GAAG,CAClB,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,MAAM,CAC9C,CAAC,WAAW,EAAE,CAAC;QAChB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;IAEO,aAAa,CAAC,KAAa;QACjC,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,OAAO;gBACV,OAAO,YAAY,CAAC,KAAK,CAAC;YAC5B,KAAK,MAAM;gBACT,OAAO,YAAY,CAAC,IAAI,CAAC;YAC3B,KAAK,MAAM;gBACT,OAAO,YAAY,CAAC,IAAI,CAAC;YAC3B,KAAK,OAAO;gBACV,OAAO,YAAY,CAAC,KAAK,CAAC;YAC5B,KAAK,SAAS;gBACZ,OAAO,YAAY,CAAC,OAAO,CAAC;YAC9B;gBACE,OAAO,YAAY,CAAC,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,KAAmB;QACnC,OAAO,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,GAAG,IAAe;QACtB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,IAAI,CAAC,GAAG,IAAe;QACrB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,IAAI,CAAC,GAAG,IAAe;QACrB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,IAAe;QACtB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,IAAe;QACxB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC"}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAc,MAAM,oBAAoB,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAEhF;;;;;;;;;GASG;AACH,MAAM,UAAU,kBAAkB,CAAmB,OAAU;IAC7D,MAAM,QAAQ,GAAG,sBAAsB,EAAE,CAAC;IAC1C,IAAI,CAAC,QAAQ;QAAE,OAAO,OAAO,CAAC;IAE9B,OAAO;QACL,GAAG,OAAO;QACV,gBAAgB,EAAE,GAAG,EAAE,CAAC,IAAI,eAAe,CAAC,QAAQ,CAAC;KACjD,CAAC;AACT,CAAC;AAED;;;GAGG;AACH,MAAM,mBAAmB;IACf,QAAQ,CAAe;IAE/B;QACE,MAAM,WAAW,GAAG,CAClB,OAAO,CAAC,GAAG,CAAC,uBAAuB,IAAI,MAAM,CAC9C,CAAC,WAAW,EAAE,CAAC;QAChB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IAClD,CAAC;IAEO,aAAa,CAAC,KAAa;QACjC,QAAQ,KAAK,EAAE,CAAC;YACd,KAAK,OAAO;gBACV,OAAO,YAAY,CAAC,KAAK,CAAC;YAC5B,KAAK,MAAM;gBACT,OAAO,YAAY,CAAC,IAAI,CAAC;YAC3B,KAAK,MAAM;gBACT,OAAO,YAAY,CAAC,IAAI,CAAC;YAC3B,KAAK,OAAO;gBACV,OAAO,YAAY,CAAC,KAAK,CAAC;YAC5B,KAAK,SAAS;gBACZ,OAAO,YAAY,CAAC,OAAO,CAAC;YAC9B;gBACE,OAAO,YAAY,CAAC,IAAI,CAAC;QAC7B,CAAC;IACH,CAAC;IAEO,SAAS,CAAC,KAAmB;QACnC,OAAO,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC;IAChC,CAAC;IAED,KAAK,CAAC,GAAG,IAAe;QACtB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,IAAI,CAAC,GAAG,IAAe;QACrB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,IAAI,CAAC,GAAG,IAAe;QACrB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC;YACtC,OAAO,CAAC,IAAI,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAED,KAAK,CAAC,GAAG,IAAe;QACtB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;YACvC,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;IAED,OAAO,CAAC,GAAG,IAAe;QACxB,IAAI,IAAI,CAAC,SAAS,CAAC,YAAY,CAAC,OAAO,CAAC,EAAE,CAAC;YACzC,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,CAAC;QAChD,CAAC;IACH,CAAC;CACF;AAED,MAAM,CAAC,MAAM,MAAM,GAAG,IAAI,mBAAmB,EAAE,CAAC"}
package/package.json CHANGED
@@ -1,8 +1,11 @@
1
1
  {
2
2
  "name": "@introspection-sdk/introspection-node",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "license": "Apache-2.0",
5
5
  "description": "Introspection observability SDK for Node.js",
6
+ "engines": {
7
+ "node": ">=22"
8
+ },
6
9
  "type": "module",
7
10
  "main": "./dist/index.js",
8
11
  "types": "./dist/index.d.ts",
@@ -15,6 +18,10 @@
15
18
  "types": "./dist/otel/index.d.ts",
16
19
  "default": "./dist/otel/index.js"
17
20
  },
21
+ "./otel/register": {
22
+ "types": "./dist/otel/register.d.ts",
23
+ "default": "./dist/otel/register.js"
24
+ },
18
25
  "./mastra": {
19
26
  "types": "./dist/otel/mastra-exporter.d.ts",
20
27
  "default": "./dist/otel/mastra-exporter.js"
@@ -43,10 +50,14 @@
43
50
  "@mastra/observability": "^1.14.0",
44
51
  "@mariozechner/pi-agent-core": "^0.73.1",
45
52
  "@mariozechner/pi-ai": "^0.73.1",
53
+ "@anthropic-ai/sdk": "^0.100.0",
54
+ "@anthropic-ai/claude-agent-sdk": "^0.3.154",
55
+ "@google/genai": "^2.6.0",
56
+ "ai": "^6.0.192",
46
57
  "openai": "^6.39.1",
47
58
  "typescript": "5.9.3",
48
- "@repo/typescript-config": "0.0.0",
49
- "@repo/eslint-config": "0.0.0"
59
+ "@repo/eslint-config": "0.0.0",
60
+ "@repo/typescript-config": "0.0.0"
50
61
  },
51
62
  "peerDependencies": {
52
63
  "@openai/agents": ">=0.0.1",
@@ -127,8 +138,9 @@
127
138
  "dependencies": {
128
139
  "@opentelemetry/api": "^1.9.1",
129
140
  "https-proxy-agent": "^7.0.6",
130
- "@introspection-sdk/types": "0.4.3",
131
- "@introspection-sdk/introspection-pi": "0.4.3"
141
+ "@introspection-sdk/types": "0.4.4",
142
+ "@introspection-sdk/introspection-pi": "0.4.4",
143
+ "@introspection-sdk/introspection-proxy": "0.1.0"
132
144
  },
133
145
  "scripts": {
134
146
  "lint": "eslint . --max-warnings 0",
@@ -438,10 +438,92 @@ export async function tracedMessagesCreate(
438
438
  * Captures all content blocks including thinking (extended thinking) with
439
439
  * signatures. Supports both non-streaming and streaming calls.
440
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
+
441
522
  export class AnthropicInstrumentor {
442
523
  private tracer: Tracer | null = null;
443
524
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
444
525
  private patchedClients: Array<{ client: any; originalCreate: any }> = [];
526
+ private protoRestores: Array<() => void> = [];
445
527
  private conversationId: string | undefined;
446
528
 
447
529
  instrument(opts: {
@@ -473,73 +555,54 @@ export class AnthropicInstrumentor {
473
555
 
474
556
  const origCreate = client.messages.create.bind(client.messages);
475
557
  this.patchedClients.push({ client, originalCreate: origCreate });
476
- const tracer = this.tracer!;
477
- const defaultConversationId = this.conversationId;
478
558
 
559
+ client.messages.create = buildTracedCreate(
560
+ origCreate,
561
+ this.tracer,
562
+ this.conversationId,
563
+ );
564
+ }
565
+
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: {
479
575
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
480
- client.messages.create = function (...args: any[]) {
481
- const kwargs = args[0] as Record<string, unknown>;
482
- const isStream = kwargs?.stream === true;
483
-
484
- // Baggage on the active context overrides the conversation ID bound
485
- // at instrument() time, so callers can wrap a request in
486
- // IntrospectionClient.withConversation() to scope a single call to a
487
- // distinct conversation without re-instrumenting the client.
488
- const baggage = propagation.getBaggage(otelContext.active());
489
- const baggageConvId = baggage?.getEntry("gen_ai.conversation.id")?.value;
490
- const baggageAgentName = baggage?.getEntry("gen_ai.agent.name")?.value;
491
- const baggageAgentId = baggage?.getEntry("gen_ai.agent.id")?.value;
492
-
493
- const { span } = startSpan(
494
- tracer,
495
- (kwargs?.model as string) || "unknown",
496
- baggageConvId ?? defaultConversationId,
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`",
497
585
  );
498
- if (baggageAgentName) {
499
- span.setAttribute("gen_ai.agent.name", baggageAgentName);
500
- }
501
- if (baggageAgentId) {
502
- span.setAttribute("gen_ai.agent.id", baggageAgentId);
503
- }
504
- setRequestAttrs(span, kwargs || {});
505
-
506
- if (isStream) {
507
- const streamPromise = origCreate(...args) as Promise<
508
- AsyncIterable<StreamEvent>
509
- >;
510
- return streamPromise.then(
511
- (stream: AsyncIterable<StreamEvent>) =>
512
- new TracedStream(stream, span),
513
- (err: Error) => {
514
- span.setStatus({
515
- code: SpanStatusCode.ERROR,
516
- message: String(err),
517
- });
518
- span.end();
519
- throw err;
520
- },
521
- );
522
- }
586
+ }
587
+ if (proto.create[PATCHED]) return;
523
588
 
524
- const resultPromise = origCreate(...args) as Promise<
525
- Record<string, unknown>
526
- >;
527
- return resultPromise.then(
528
- (response: Record<string, unknown>) => {
529
- setResponseAttrs(span, response);
530
- span.end();
531
- return response;
532
- },
533
- (err: Error) => {
534
- span.setStatus({
535
- code: SpanStatusCode.ERROR,
536
- message: String(err),
537
- });
538
- span.end();
539
- throw err;
540
- },
541
- );
542
- };
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
+ });
543
606
  }
544
607
 
545
608
  uninstrument(): void {
@@ -547,6 +610,8 @@ export class AnthropicInstrumentor {
547
610
  client.messages.create = originalCreate;
548
611
  }
549
612
  this.patchedClients = [];
613
+ for (const restore of this.protoRestores) restore();
614
+ this.protoRestores = [];
550
615
  this.tracer = null;
551
616
  }
552
617
  }
@@ -297,6 +297,79 @@ interface GeminiModelsApi {
297
297
  * Captures all content parts including thought summaries and signed
298
298
  * `thoughtSignature` payloads required for multi-turn replay.
299
299
  */
300
+ /**
301
+ * Build the traced replacement for a non-streaming `generateContent`-style
302
+ * method. Shared by {@link GeminiInstrumentor.instrument} (patches a client's
303
+ * public `models.generateContent`) and {@link GeminiInstrumentor.instrumentClass}
304
+ * (patches `Models.prototype.generateContentInternal` so every client is
305
+ * auto-traced). Preserves the call-site `this`.
306
+ */
307
+ function buildTracedGenerate(
308
+ origGenerate: (...args: unknown[]) => Promise<unknown>,
309
+ tracer: Tracer,
310
+ conversationId: string,
311
+ ): (...args: unknown[]) => Promise<unknown> {
312
+ return async function tracedGenerate(this: unknown, ...args: unknown[]) {
313
+ const kwargs = (args[0] as Record<string, unknown>) || {};
314
+ const span = startSpan(
315
+ tracer,
316
+ (kwargs.model as string) || "unknown",
317
+ conversationId,
318
+ );
319
+ setRequestAttrs(span, kwargs);
320
+ try {
321
+ const response = (await origGenerate.apply(this, args)) as Record<
322
+ string,
323
+ unknown
324
+ >;
325
+ setResponseAttrs(span, response);
326
+ return response;
327
+ } catch (err) {
328
+ span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
329
+ throw err;
330
+ } finally {
331
+ span.end();
332
+ }
333
+ };
334
+ }
335
+
336
+ /** Build the traced replacement for a streaming `generateContentStream`-style method. */
337
+ function buildTracedGenerateStream(
338
+ origStream: (...args: unknown[]) => Promise<unknown>,
339
+ tracer: Tracer,
340
+ conversationId: string,
341
+ ): (...args: unknown[]) => Promise<unknown> {
342
+ return async function tracedGenerateStream(
343
+ this: unknown,
344
+ ...args: unknown[]
345
+ ) {
346
+ const kwargs = (args[0] as Record<string, unknown>) || {};
347
+ const span = startSpan(
348
+ tracer,
349
+ (kwargs.model as string) || "unknown",
350
+ conversationId,
351
+ );
352
+ setRequestAttrs(span, kwargs);
353
+ try {
354
+ const stream = (await origStream.apply(
355
+ this,
356
+ args,
357
+ )) as AsyncIterable<StreamChunk>;
358
+ return new TracedStream(stream, span);
359
+ } catch (err) {
360
+ span.setStatus({ code: SpanStatusCode.ERROR, message: String(err) });
361
+ span.end();
362
+ throw err;
363
+ }
364
+ };
365
+ }
366
+
367
+ /**
368
+ * Marker placed on a patched `Models.prototype` method so repeated
369
+ * `instrumentClass()` calls (e.g. a second `introspection.init()`) are no-ops.
370
+ */
371
+ const PATCHED = Symbol.for("introspection.gemini.patched");
372
+
300
373
  export class GeminiInstrumentor {
301
374
  private tracer: Tracer | null = null;
302
375
  private restores: Array<() => void> = [];
@@ -337,55 +410,76 @@ export class GeminiInstrumentor {
337
410
  if (origGenerateStream) api.generateContentStream = origGenerateStream;
338
411
  });
339
412
 
340
- api.generateContent = async (...args: unknown[]) => {
341
- const kwargs = (args[0] as Record<string, unknown>) || {};
342
- const span = startSpan(
413
+ api.generateContent = buildTracedGenerate(
414
+ origGenerate,
415
+ tracer,
416
+ conversationId,
417
+ ) as GeminiModelsApi["generateContent"];
418
+
419
+ if (origGenerateStream) {
420
+ api.generateContentStream = buildTracedGenerateStream(
421
+ origGenerateStream,
343
422
  tracer,
344
- (kwargs.model as string) || "unknown",
345
423
  conversationId,
424
+ ) as GeminiModelsApi["generateContentStream"];
425
+ }
426
+ }
427
+
428
+ /**
429
+ * Patch `Models.prototype.generateContentInternal` / `…StreamInternal` so
430
+ * **every** `GoogleGenAI` client is traced — the one-liner path used by
431
+ * `introspection.init()`. The public `generateContent` /
432
+ * `generateContentStream` are per-instance bound class fields that delegate
433
+ * to these prototype methods, so patching the prototype covers all clients.
434
+ * Idempotent: a second call is a no-op.
435
+ *
436
+ * @param opts.genai The `@google/genai` module (must expose `Models`).
437
+ */
438
+ instrumentClass(opts: {
439
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
440
+ genai: any;
441
+ tracerProvider?: BasicTracerProvider;
442
+ conversationId?: string;
443
+ }): void {
444
+ const proto = opts.genai?.Models?.prototype;
445
+ if (typeof proto?.generateContentInternal !== "function") {
446
+ throw new Error(
447
+ "Invalid @google/genai module — expected `Models.prototype.generateContentInternal`",
346
448
  );
347
- setRequestAttrs(span, kwargs);
348
- try {
349
- const response = (await origGenerate(...args)) as Record<
350
- string,
351
- unknown
352
- >;
353
- setResponseAttrs(span, response);
354
- return response;
355
- } catch (err) {
356
- span.setStatus({
357
- code: SpanStatusCode.ERROR,
358
- message: String(err),
359
- });
360
- throw err;
361
- } finally {
362
- span.end();
363
- }
364
- };
449
+ }
450
+ if (proto.generateContentInternal[PATCHED]) return;
365
451
 
366
- if (origGenerateStream) {
367
- api.generateContentStream = async (...args: unknown[]) => {
368
- const kwargs = (args[0] as Record<string, unknown>) || {};
369
- const span = startSpan(
370
- tracer,
371
- (kwargs.model as string) || "unknown",
372
- conversationId,
373
- );
374
- setRequestAttrs(span, kwargs);
375
- try {
376
- const stream = (await origGenerateStream(
377
- ...args,
378
- )) as AsyncIterable<StreamChunk>;
379
- return new TracedStream(stream, span);
380
- } catch (err) {
381
- span.setStatus({
382
- code: SpanStatusCode.ERROR,
383
- message: String(err),
384
- });
385
- span.end();
386
- throw err;
387
- }
388
- };
452
+ this.conversationId = opts.conversationId ?? crypto.randomUUID();
453
+ const provider = opts.tracerProvider ?? trace.getTracerProvider();
454
+ this.tracer = (provider as BasicTracerProvider).getTracer(
455
+ "introspection-gemini",
456
+ );
457
+ const conversationId = this.conversationId;
458
+
459
+ const origGenerate = proto.generateContentInternal;
460
+ const patchedGenerate = buildTracedGenerate(
461
+ origGenerate,
462
+ this.tracer,
463
+ conversationId,
464
+ ) as ((...args: unknown[]) => Promise<unknown>) & { [PATCHED]?: boolean };
465
+ patchedGenerate[PATCHED] = true;
466
+ proto.generateContentInternal = patchedGenerate;
467
+ this.restores.push(() => {
468
+ proto.generateContentInternal = origGenerate;
469
+ });
470
+
471
+ if (typeof proto.generateContentStreamInternal === "function") {
472
+ const origStream = proto.generateContentStreamInternal;
473
+ const patchedStream = buildTracedGenerateStream(
474
+ origStream,
475
+ this.tracer,
476
+ conversationId,
477
+ ) as ((...args: unknown[]) => Promise<unknown>) & { [PATCHED]?: boolean };
478
+ patchedStream[PATCHED] = true;
479
+ proto.generateContentStreamInternal = patchedStream;
480
+ this.restores.push(() => {
481
+ proto.generateContentStreamInternal = origStream;
482
+ });
389
483
  }
390
484
  }
391
485
 
package/src/otel/index.ts CHANGED
@@ -78,3 +78,44 @@ export {
78
78
  addOpenInferenceAttributes,
79
79
  OpenInferenceSpanExporter,
80
80
  } from "../converters/openinference.js";
81
+
82
+ // One-liner bootstrap (`introspection.init()`) + analytics proxies and the
83
+ // per-framework handle accessors it binds. Auto-detects installed frameworks
84
+ // and wires them into one shared provider.
85
+ export {
86
+ init,
87
+ shutdown,
88
+ track,
89
+ feedback,
90
+ identify,
91
+ conversation,
92
+ withAgent,
93
+ withConversation,
94
+ withUserId,
95
+ withAnonymousId,
96
+ newConversationId,
97
+ getClient,
98
+ getTracerProvider,
99
+ getLangchainHandler,
100
+ getMastraExporter,
101
+ instrumentPi,
102
+ instrumentClaudeAgent,
103
+ _resetForTests,
104
+ } from "./init.js";
105
+ export type { InitOptions } from "./init.js";
106
+
107
+ // Low-level OTel registration helper (also used by `init`).
108
+ export { registerOTelGlobals } from "./setup.js";
109
+
110
+ // Integration registry — for custom integrations passed to `init`.
111
+ export {
112
+ DidNotEnable,
113
+ discoverIntegrations,
114
+ setupIntegrations,
115
+ resetInstalledForTests,
116
+ } from "./integrations/index.js";
117
+ export type {
118
+ Integration,
119
+ IntegrationSetupContext,
120
+ IntegrationHandles,
121
+ } from "./integrations/index.js";