@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.
- package/.turbo/turbo-build.log +1 -1
- package/dist/otel/anthropic.d.ts +14 -12
- package/dist/otel/anthropic.d.ts.map +1 -1
- package/dist/otel/anthropic.js +89 -47
- package/dist/otel/anthropic.js.map +1 -1
- package/dist/otel/gemini.d.ts +15 -7
- package/dist/otel/gemini.d.ts.map +1 -1
- package/dist/otel/gemini.js +88 -37
- package/dist/otel/gemini.js.map +1 -1
- package/dist/otel/index.d.ts +5 -0
- package/dist/otel/index.d.ts.map +1 -1
- package/dist/otel/index.js +8 -0
- package/dist/otel/index.js.map +1 -1
- package/dist/otel/init.d.ts +126 -0
- package/dist/otel/init.d.ts.map +1 -0
- package/dist/otel/init.js +259 -0
- package/dist/otel/init.js.map +1 -0
- package/dist/otel/integrations/anthropic.d.ts +10 -0
- package/dist/otel/integrations/anthropic.d.ts.map +1 -0
- package/dist/otel/integrations/anthropic.js +22 -0
- package/dist/otel/integrations/anthropic.js.map +1 -0
- package/dist/otel/integrations/base.d.ts +83 -0
- package/dist/otel/integrations/base.d.ts.map +1 -0
- package/dist/otel/integrations/base.js +23 -0
- package/dist/otel/integrations/base.js.map +1 -0
- package/dist/otel/integrations/claude-agent.d.ts +13 -0
- package/dist/otel/integrations/claude-agent.d.ts.map +1 -0
- package/dist/otel/integrations/claude-agent.js +27 -0
- package/dist/otel/integrations/claude-agent.js.map +1 -0
- package/dist/otel/integrations/gemini.d.ts +11 -0
- package/dist/otel/integrations/gemini.d.ts.map +1 -0
- package/dist/otel/integrations/gemini.js +21 -0
- package/dist/otel/integrations/gemini.js.map +1 -0
- package/dist/otel/integrations/index.d.ts +39 -0
- package/dist/otel/integrations/index.d.ts.map +1 -0
- package/dist/otel/integrations/index.js +131 -0
- package/dist/otel/integrations/index.js.map +1 -0
- package/dist/otel/integrations/langchain.d.ts +16 -0
- package/dist/otel/integrations/langchain.d.ts.map +1 -0
- package/dist/otel/integrations/langchain.js +41 -0
- package/dist/otel/integrations/langchain.js.map +1 -0
- package/dist/otel/integrations/mastra.d.ts +14 -0
- package/dist/otel/integrations/mastra.d.ts.map +1 -0
- package/dist/otel/integrations/mastra.js +27 -0
- package/dist/otel/integrations/mastra.js.map +1 -0
- package/dist/otel/integrations/openai-agents.d.ts +11 -0
- package/dist/otel/integrations/openai-agents.d.ts.map +1 -0
- package/dist/otel/integrations/openai-agents.js +25 -0
- package/dist/otel/integrations/openai-agents.js.map +1 -0
- package/dist/otel/integrations/pi.d.ts +12 -0
- package/dist/otel/integrations/pi.d.ts.map +1 -0
- package/dist/otel/integrations/pi.js +20 -0
- package/dist/otel/integrations/pi.js.map +1 -0
- package/dist/otel/integrations/vercel.d.ts +15 -0
- package/dist/otel/integrations/vercel.d.ts.map +1 -0
- package/dist/otel/integrations/vercel.js +20 -0
- package/dist/otel/integrations/vercel.js.map +1 -0
- package/dist/otel/register.d.ts +8 -0
- package/dist/otel/register.d.ts.map +1 -0
- package/dist/otel/register.js +48 -0
- package/dist/otel/register.js.map +1 -0
- package/dist/otel/setup.d.ts +29 -0
- package/dist/otel/setup.d.ts.map +1 -1
- package/dist/otel/setup.js +22 -3
- package/dist/otel/setup.js.map +1 -1
- package/dist/utils.d.ts +10 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +12 -4
- package/dist/utils.js.map +1 -1
- package/package.json +17 -5
- package/src/otel/anthropic.ts +128 -63
- package/src/otel/gemini.ts +139 -45
- package/src/otel/index.ts +41 -0
- package/src/otel/init.ts +411 -0
- package/src/otel/integrations/anthropic.ts +26 -0
- package/src/otel/integrations/base.ts +95 -0
- package/src/otel/integrations/claude-agent.ts +32 -0
- package/src/otel/integrations/gemini.ts +25 -0
- package/src/otel/integrations/index.ts +151 -0
- package/src/otel/integrations/langchain.ts +45 -0
- package/src/otel/integrations/mastra.ts +30 -0
- package/src/otel/integrations/openai-agents.ts +31 -0
- package/src/otel/integrations/pi.ts +24 -0
- package/src/otel/integrations/vercel.ts +24 -0
- package/src/otel/register.ts +50 -0
- package/src/otel/setup.ts +44 -3
- package/src/utils.ts +12 -5
- package/.turbo/turbo-check-types.log +0 -4
- package/.turbo/turbo-lint.log +0 -5
- package/dist/ai-sdk-integration.d.ts +0 -123
- package/dist/ai-sdk-integration.d.ts.map +0 -1
- package/dist/ai-sdk-integration.js +0 -475
- package/dist/anthropic.d.ts +0 -54
- package/dist/anthropic.d.ts.map +0 -1
- package/dist/anthropic.js +0 -375
- package/dist/claude-hooks.d.ts +0 -328
- package/dist/claude-hooks.d.ts.map +0 -1
- package/dist/claude-hooks.js +0 -571
- package/dist/claude-wrapper.d.ts +0 -93
- package/dist/claude-wrapper.d.ts.map +0 -1
- package/dist/claude-wrapper.js +0 -154
- package/dist/converters/ai-sdk.d.ts +0 -65
- package/dist/converters/ai-sdk.d.ts.map +0 -1
- package/dist/converters/ai-sdk.js +0 -261
- package/dist/converters/openclaw.d.ts +0 -16
- package/dist/converters/openclaw.d.ts.map +0 -1
- package/dist/converters/openclaw.js +0 -49
- package/dist/langchain-handler.d.ts +0 -103
- package/dist/langchain-handler.d.ts.map +0 -1
- package/dist/langchain-handler.js +0 -518
- package/dist/mastra-exporter.d.ts +0 -85
- package/dist/mastra-exporter.d.ts.map +0 -1
- package/dist/mastra-exporter.js +0 -471
- package/dist/span-processor.d.ts +0 -62
- package/dist/span-processor.d.ts.map +0 -1
- package/dist/span-processor.js +0 -313
- package/dist/tracing-processor.d.ts +0 -136
- package/dist/tracing-processor.d.ts.map +0 -1
- package/dist/tracing-processor.js +0 -368
- package/dist/types/genai.d.ts +0 -165
- package/dist/types/genai.d.ts.map +0 -1
- 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
|
-
|
|
4
|
-
|
|
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 =
|
|
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;
|
|
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
|
+
"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/
|
|
49
|
-
"@repo/
|
|
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.
|
|
131
|
-
"@introspection-sdk/introspection-pi": "0.4.
|
|
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",
|
package/src/otel/anthropic.ts
CHANGED
|
@@ -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
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
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
|
-
|
|
499
|
-
|
|
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
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
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
|
}
|
package/src/otel/gemini.ts
CHANGED
|
@@ -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 =
|
|
341
|
-
|
|
342
|
-
|
|
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
|
-
|
|
348
|
-
|
|
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
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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";
|