@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.
- package/README.md +28 -8
- package/dist/auth.d.ts +1 -1
- package/dist/client.d.ts +4 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.js +16 -7
- package/dist/client.js.map +1 -1
- package/dist/dev-target.js +1 -1
- package/dist/index.d.ts +3 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -6
- package/dist/index.js.map +1 -1
- package/dist/otel/index.d.ts +1 -14
- package/dist/otel/index.d.ts.map +1 -1
- package/dist/otel/index.js +10 -18
- package/dist/otel/index.js.map +1 -1
- package/dist/otel/init.d.ts +19 -21
- package/dist/otel/init.d.ts.map +1 -1
- package/dist/otel/init.js +62 -48
- package/dist/otel/init.js.map +1 -1
- package/dist/otel/integrations/base.d.ts +17 -37
- package/dist/otel/integrations/base.d.ts.map +1 -1
- package/dist/otel/integrations/base.js +6 -13
- package/dist/otel/integrations/base.js.map +1 -1
- package/dist/otel/integrations/index.d.ts +3 -4
- package/dist/otel/integrations/index.d.ts.map +1 -1
- package/dist/otel/integrations/index.js +6 -25
- package/dist/otel/integrations/index.js.map +1 -1
- package/dist/otel/integrations/pi.d.ts.map +1 -1
- package/dist/otel/integrations/pi.js +9 -1
- package/dist/otel/integrations/pi.js.map +1 -1
- package/dist/otel/logs.d.ts +41 -8
- package/dist/otel/logs.d.ts.map +1 -1
- package/dist/otel/logs.js +95 -66
- package/dist/otel/logs.js.map +1 -1
- package/dist/otel/pi.d.ts +18 -5
- package/dist/otel/pi.d.ts.map +1 -1
- package/dist/otel/pi.js +38 -9
- package/dist/otel/pi.js.map +1 -1
- package/dist/otel/register.d.ts +2 -2
- package/dist/otel/register.d.ts.map +1 -1
- package/dist/otel/register.js +7 -9
- package/dist/otel/register.js.map +1 -1
- package/dist/otel/setup.d.ts +2 -2
- package/dist/otel/setup.d.ts.map +1 -1
- package/dist/otel/setup.js +16 -6
- package/dist/otel/setup.js.map +1 -1
- package/dist/otel/span-processor.d.ts +18 -5
- package/dist/otel/span-processor.d.ts.map +1 -1
- package/dist/otel/span-processor.js +50 -65
- package/dist/otel/span-processor.js.map +1 -1
- package/dist/resources/experiments.d.ts +10 -5
- package/dist/resources/experiments.d.ts.map +1 -1
- package/dist/resources/experiments.js +9 -25
- package/dist/resources/experiments.js.map +1 -1
- package/dist/resources/recipes.d.ts +6 -6
- package/dist/resources/recipes.d.ts.map +1 -1
- package/dist/resources/recipes.js +5 -23
- package/dist/resources/recipes.js.map +1 -1
- package/dist/resources/runtimes.d.ts +2 -2
- package/dist/resources/runtimes.d.ts.map +1 -1
- package/dist/resources/runtimes.js +2 -2
- package/dist/resources/runtimes.js.map +1 -1
- package/dist/runner.d.ts +8 -4
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +26 -18
- package/dist/runner.js.map +1 -1
- package/dist/types.d.ts +1 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +1 -1
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +45 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +61 -2
- package/dist/utils.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.d.ts.map +1 -1
- package/dist/version.js +1 -1
- package/dist/version.js.map +1 -1
- package/package.json +18 -60
- package/.turbo/turbo-build.log +0 -4
- package/dist/converters/claude.d.ts +0 -150
- package/dist/converters/claude.d.ts.map +0 -1
- package/dist/converters/claude.js +0 -211
- package/dist/converters/claude.js.map +0 -1
- package/dist/converters/gemini.d.ts +0 -90
- package/dist/converters/gemini.d.ts.map +0 -1
- package/dist/converters/gemini.js +0 -171
- package/dist/converters/gemini.js.map +0 -1
- package/dist/converters/index.d.ts +0 -9
- package/dist/converters/index.d.ts.map +0 -1
- package/dist/converters/index.js +0 -9
- package/dist/converters/index.js.map +0 -1
- package/dist/converters/openai.d.ts +0 -51
- package/dist/converters/openai.d.ts.map +0 -1
- package/dist/converters/openai.js +0 -269
- package/dist/converters/openai.js.map +0 -1
- package/dist/converters/openinference.d.ts +0 -107
- package/dist/converters/openinference.d.ts.map +0 -1
- package/dist/converters/openinference.js +0 -553
- package/dist/converters/openinference.js.map +0 -1
- package/dist/converters/vercel.d.ts +0 -21
- package/dist/converters/vercel.d.ts.map +0 -1
- package/dist/converters/vercel.js +0 -236
- package/dist/converters/vercel.js.map +0 -1
- package/dist/otel/anthropic.d.ts +0 -63
- package/dist/otel/anthropic.d.ts.map +0 -1
- package/dist/otel/anthropic.js +0 -475
- package/dist/otel/anthropic.js.map +0 -1
- package/dist/otel/claude-hooks.d.ts +0 -346
- package/dist/otel/claude-hooks.d.ts.map +0 -1
- package/dist/otel/claude-hooks.js +0 -647
- package/dist/otel/claude-hooks.js.map +0 -1
- package/dist/otel/claude-wrapper.d.ts +0 -93
- package/dist/otel/claude-wrapper.d.ts.map +0 -1
- package/dist/otel/claude-wrapper.js +0 -155
- package/dist/otel/claude-wrapper.js.map +0 -1
- package/dist/otel/gemini.d.ts +0 -56
- package/dist/otel/gemini.d.ts.map +0 -1
- package/dist/otel/gemini.js +0 -350
- package/dist/otel/gemini.js.map +0 -1
- package/dist/otel/integrations/anthropic.d.ts +0 -10
- package/dist/otel/integrations/anthropic.d.ts.map +0 -1
- package/dist/otel/integrations/anthropic.js +0 -26
- package/dist/otel/integrations/anthropic.js.map +0 -1
- package/dist/otel/integrations/claude-agent.d.ts +0 -12
- package/dist/otel/integrations/claude-agent.d.ts.map +0 -1
- package/dist/otel/integrations/claude-agent.js +0 -28
- package/dist/otel/integrations/claude-agent.js.map +0 -1
- package/dist/otel/integrations/gemini.d.ts +0 -11
- package/dist/otel/integrations/gemini.d.ts.map +0 -1
- package/dist/otel/integrations/gemini.js +0 -23
- package/dist/otel/integrations/gemini.js.map +0 -1
- package/dist/otel/integrations/langchain.d.ts +0 -15
- package/dist/otel/integrations/langchain.d.ts.map +0 -1
- package/dist/otel/integrations/langchain.js +0 -43
- package/dist/otel/integrations/langchain.js.map +0 -1
- package/dist/otel/integrations/mastra.d.ts +0 -14
- package/dist/otel/integrations/mastra.d.ts.map +0 -1
- package/dist/otel/integrations/mastra.js +0 -31
- package/dist/otel/integrations/mastra.js.map +0 -1
- package/dist/otel/integrations/openai-agents.d.ts +0 -11
- package/dist/otel/integrations/openai-agents.d.ts.map +0 -1
- package/dist/otel/integrations/openai-agents.js +0 -30
- package/dist/otel/integrations/openai-agents.js.map +0 -1
- package/dist/otel/integrations/vercel.d.ts +0 -14
- package/dist/otel/integrations/vercel.d.ts.map +0 -1
- package/dist/otel/integrations/vercel.js +0 -20
- package/dist/otel/integrations/vercel.js.map +0 -1
- package/dist/otel/langchain-handler.d.ts +0 -112
- package/dist/otel/langchain-handler.d.ts.map +0 -1
- package/dist/otel/langchain-handler.js +0 -561
- package/dist/otel/langchain-handler.js.map +0 -1
- package/dist/otel/mastra-exporter.d.ts +0 -87
- package/dist/otel/mastra-exporter.d.ts.map +0 -1
- package/dist/otel/mastra-exporter.js +0 -530
- package/dist/otel/mastra-exporter.js.map +0 -1
- package/dist/otel/openai.d.ts +0 -20
- package/dist/otel/openai.d.ts.map +0 -1
- package/dist/otel/openai.js +0 -55
- package/dist/otel/openai.js.map +0 -1
- package/dist/otel/tracing-processor.d.ts +0 -139
- package/dist/otel/tracing-processor.d.ts.map +0 -1
- package/dist/otel/tracing-processor.js +0 -434
- package/dist/otel/tracing-processor.js.map +0 -1
- package/eslint.config.mjs +0 -4
- package/src/auth.ts +0 -236
- package/src/client.ts +0 -163
- package/src/converters/claude.ts +0 -343
- package/src/converters/gemini.ts +0 -243
- package/src/converters/index.ts +0 -50
- package/src/converters/openai.ts +0 -300
- package/src/converters/openinference.ts +0 -665
- package/src/converters/vercel.ts +0 -302
- package/src/dev-target.ts +0 -79
- package/src/http.ts +0 -43
- package/src/index.ts +0 -277
- package/src/otel/anthropic.ts +0 -619
- package/src/otel/claude-hooks.ts +0 -1116
- package/src/otel/claude-wrapper.ts +0 -239
- package/src/otel/gemini.ts +0 -483
- package/src/otel/index.ts +0 -128
- package/src/otel/init.ts +0 -411
- package/src/otel/integrations/anthropic.ts +0 -36
- package/src/otel/integrations/base.ts +0 -158
- package/src/otel/integrations/claude-agent.ts +0 -36
- package/src/otel/integrations/gemini.ts +0 -30
- package/src/otel/integrations/index.ts +0 -152
- package/src/otel/integrations/langchain.ts +0 -60
- package/src/otel/integrations/mastra.ts +0 -48
- package/src/otel/integrations/openai-agents.ts +0 -47
- package/src/otel/integrations/pi.ts +0 -28
- package/src/otel/integrations/vercel.ts +0 -26
- package/src/otel/langchain-handler.ts +0 -804
- package/src/otel/logs.ts +0 -395
- package/src/otel/mastra-exporter.ts +0 -714
- package/src/otel/openai.ts +0 -93
- package/src/otel/pi.ts +0 -94
- package/src/otel/register.ts +0 -50
- package/src/otel/setup.ts +0 -215
- package/src/otel/span-processor.ts +0 -512
- package/src/otel/tracing-processor.ts +0 -637
- package/src/pagination.ts +0 -10
- package/src/proxy-from-env.d.ts +0 -7
- package/src/resources/experiments.ts +0 -176
- package/src/resources/recipes.ts +0 -74
- package/src/resources/runtimes.ts +0 -31
- package/src/runner.ts +0 -213
- package/src/types.ts +0 -13
- package/src/utils.ts +0 -95
- package/src/version.ts +0 -2
- package/tsconfig.json +0 -8
package/src/otel/openai.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/** Privacy-preserving OpenAI embeddings instrumentation. */
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
context,
|
|
5
|
-
SpanKind,
|
|
6
|
-
SpanStatusCode,
|
|
7
|
-
trace,
|
|
8
|
-
type Span,
|
|
9
|
-
type Tracer,
|
|
10
|
-
} from "@opentelemetry/api";
|
|
11
|
-
import type {
|
|
12
|
-
CreateEmbeddingResponse,
|
|
13
|
-
EmbeddingCreateParams,
|
|
14
|
-
} from "openai/resources/embeddings";
|
|
15
|
-
|
|
16
|
-
/** The narrow OpenAI client surface required by the embedding wrapper. */
|
|
17
|
-
export interface OpenAIEmbeddingsClient {
|
|
18
|
-
embeddings: {
|
|
19
|
-
create(params: EmbeddingCreateParams): Promise<CreateEmbeddingResponse>;
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export interface TracedEmbeddingsOptions {
|
|
24
|
-
/** GenAI provider name. Defaults to `openai`. */
|
|
25
|
-
providerName?: string;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function setResponseAttributes(
|
|
29
|
-
span: Span,
|
|
30
|
-
response: CreateEmbeddingResponse,
|
|
31
|
-
): void {
|
|
32
|
-
span.setAttribute("gen_ai.response.model", response.model);
|
|
33
|
-
span.setAttribute("gen_ai.usage.input_tokens", response.usage.prompt_tokens);
|
|
34
|
-
|
|
35
|
-
const firstEmbedding = response.data[0]?.embedding;
|
|
36
|
-
if (Array.isArray(firstEmbedding)) {
|
|
37
|
-
span.setAttribute(
|
|
38
|
-
"gen_ai.embeddings.dimension.count",
|
|
39
|
-
firstEmbedding.length,
|
|
40
|
-
);
|
|
41
|
-
}
|
|
42
|
-
span.setStatus({ code: SpanStatusCode.OK });
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Call `client.embeddings.create` and emit a metadata-only GenAI span.
|
|
47
|
-
*
|
|
48
|
-
* Inputs and embedding vectors are deliberately never attached to the span.
|
|
49
|
-
*/
|
|
50
|
-
export async function tracedEmbeddingsCreate(
|
|
51
|
-
tracer: Tracer,
|
|
52
|
-
client: OpenAIEmbeddingsClient,
|
|
53
|
-
params: EmbeddingCreateParams,
|
|
54
|
-
options: TracedEmbeddingsOptions = {},
|
|
55
|
-
): Promise<CreateEmbeddingResponse> {
|
|
56
|
-
const providerName = options.providerName ?? "openai";
|
|
57
|
-
const span = tracer.startSpan(`embeddings ${params.model}`, {
|
|
58
|
-
kind: SpanKind.CLIENT,
|
|
59
|
-
attributes: {
|
|
60
|
-
"gen_ai.system": providerName,
|
|
61
|
-
"gen_ai.provider.name": providerName,
|
|
62
|
-
"gen_ai.operation.name": "embeddings",
|
|
63
|
-
"gen_ai.request.model": params.model,
|
|
64
|
-
"openinference.span.kind": "EMBEDDING",
|
|
65
|
-
...(params.dimensions !== undefined
|
|
66
|
-
? { "gen_ai.embeddings.dimension.count": params.dimensions }
|
|
67
|
-
: undefined),
|
|
68
|
-
...(params.encoding_format
|
|
69
|
-
? { "gen_ai.request.encoding_formats": [params.encoding_format] }
|
|
70
|
-
: undefined),
|
|
71
|
-
},
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
return await context.with(
|
|
76
|
-
trace.setSpan(context.active(), span),
|
|
77
|
-
async () => {
|
|
78
|
-
const response = await client.embeddings.create(params);
|
|
79
|
-
setResponseAttributes(span, response);
|
|
80
|
-
return response;
|
|
81
|
-
},
|
|
82
|
-
);
|
|
83
|
-
} catch (error) {
|
|
84
|
-
span.setStatus({
|
|
85
|
-
code: SpanStatusCode.ERROR,
|
|
86
|
-
message: error instanceof Error ? error.message : String(error),
|
|
87
|
-
});
|
|
88
|
-
if (error instanceof Error) span.recordException(error);
|
|
89
|
-
throw error;
|
|
90
|
-
} finally {
|
|
91
|
-
span.end();
|
|
92
|
-
}
|
|
93
|
-
}
|
package/src/otel/pi.ts
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* High-level pi Agent SDK integration for Introspection.
|
|
3
|
-
*
|
|
4
|
-
* Wraps {@link @introspection-sdk/introspection-pi} behind a zero-config
|
|
5
|
-
* interface so users don't need to manage OpenTelemetry tracers directly.
|
|
6
|
-
*
|
|
7
|
-
* @example
|
|
8
|
-
* ```ts
|
|
9
|
-
* import { IntrospectionPiInstrumentor, setupTracing } from "@introspection-sdk/introspection-node";
|
|
10
|
-
* import { Agent } from "@earendil-works/pi-agent-core";
|
|
11
|
-
*
|
|
12
|
-
* setupTracing({ serviceName: "my-app" });
|
|
13
|
-
* const instrumentor = new IntrospectionPiInstrumentor();
|
|
14
|
-
*
|
|
15
|
-
* const agent = new Agent({ ... });
|
|
16
|
-
* instrumentor.instrument(agent, {
|
|
17
|
-
* conversationId: "conv-123",
|
|
18
|
-
* agentId: "weather-agent",
|
|
19
|
-
* agentName: "Weather",
|
|
20
|
-
* });
|
|
21
|
-
*
|
|
22
|
-
* await agent.prompt("What's the weather in Tokyo?");
|
|
23
|
-
* instrumentor.stop();
|
|
24
|
-
* ```
|
|
25
|
-
*/
|
|
26
|
-
|
|
27
|
-
import { trace } from "@opentelemetry/api";
|
|
28
|
-
import type { Agent, StreamFn } from "@earendil-works/pi-agent-core";
|
|
29
|
-
import {
|
|
30
|
-
instrumentStream,
|
|
31
|
-
instrumentAgent,
|
|
32
|
-
type AgentMeta,
|
|
33
|
-
type AgentInstrumentation,
|
|
34
|
-
} from "@introspection-sdk/introspection-pi";
|
|
35
|
-
import { VERSION } from "../version.js";
|
|
36
|
-
|
|
37
|
-
export type { AgentMeta } from "@introspection-sdk/introspection-pi";
|
|
38
|
-
|
|
39
|
-
export interface IntrospectionPiInstrumentorOptions {
|
|
40
|
-
/** Tracer name used for all spans produced by this instrumentor. */
|
|
41
|
-
tracerName?: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Zero-config pi Agent SDK integration for Introspection.
|
|
46
|
-
*
|
|
47
|
-
* Uses the global OTel tracer provider (registered by {@link setupTracing}).
|
|
48
|
-
* Call {@link instrument} once per {@link Agent} instance, then {@link stop}
|
|
49
|
-
* to unsubscribe all tool instrumentation and finalize open spans.
|
|
50
|
-
*/
|
|
51
|
-
export class IntrospectionPiInstrumentor {
|
|
52
|
-
private _tracer: ReturnType<typeof trace.getTracer>;
|
|
53
|
-
private _instrumentations: AgentInstrumentation[] = [];
|
|
54
|
-
|
|
55
|
-
constructor(opts: IntrospectionPiInstrumentorOptions = {}) {
|
|
56
|
-
this._tracer = trace.getTracer(
|
|
57
|
-
opts.tracerName ?? "introspection-pi",
|
|
58
|
-
VERSION,
|
|
59
|
-
);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
/**
|
|
63
|
-
* Instrument a pi {@link Agent}:
|
|
64
|
-
* - Wraps the agent stream function to emit a `chat ${provider}` span per
|
|
65
|
-
* LLM call.
|
|
66
|
-
* - Subscribes to the agent loop to emit an `execute_tool ${name}` span per
|
|
67
|
-
* tool execution.
|
|
68
|
-
*
|
|
69
|
-
* @param agent - The pi Agent instance to instrument.
|
|
70
|
-
* @param meta - Identity metadata stamped on every span produced by this agent.
|
|
71
|
-
*/
|
|
72
|
-
instrument(agent: Agent, meta: AgentMeta): void {
|
|
73
|
-
const key = "streamFunction" in agent ? "streamFunction" : "streamFn";
|
|
74
|
-
const instrumentable = agent as unknown as Record<
|
|
75
|
-
"streamFunction" | "streamFn",
|
|
76
|
-
StreamFn
|
|
77
|
-
>;
|
|
78
|
-
instrumentable[key] = instrumentStream(instrumentable[key], {
|
|
79
|
-
tracer: this._tracer,
|
|
80
|
-
meta,
|
|
81
|
-
});
|
|
82
|
-
this._instrumentations.push(
|
|
83
|
-
instrumentAgent(agent, { tracer: this._tracer, meta }),
|
|
84
|
-
);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Unsubscribe all active tool instrumentations and finalize any open spans.
|
|
89
|
-
*/
|
|
90
|
-
stop(): void {
|
|
91
|
-
for (const inst of this._instrumentations) inst.stop();
|
|
92
|
-
this._instrumentations = [];
|
|
93
|
-
}
|
|
94
|
-
}
|
package/src/otel/register.ts
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Zero-code preload entry point for Introspection tracing.
|
|
3
|
-
*
|
|
4
|
-
* Usage (ESM):
|
|
5
|
-
*
|
|
6
|
-
* ```bash
|
|
7
|
-
* node --import @introspection-sdk/introspection-node/otel/register app.js
|
|
8
|
-
* ```
|
|
9
|
-
*
|
|
10
|
-
* Runs `introspection.init()` from the environment **before** the app's module
|
|
11
|
-
* graph executes, so no `init()` call is needed in application source. Because
|
|
12
|
-
* the Anthropic / Gemini one-liner works by prototype-patching the SDK classes
|
|
13
|
-
* (see `AnthropicInstrumentor.instrumentClass`), every client constructed after
|
|
14
|
-
* this preload is traced with zero code changes.
|
|
15
|
-
*
|
|
16
|
-
* Configuration is read from the usual environment variables
|
|
17
|
-
* (`INTROSPECTION_TOKEN`, `INTROSPECTION_SERVICE_NAME`,
|
|
18
|
-
* `INTROSPECTION_BASE_OTEL_URL`). If no token is configured the preload logs a
|
|
19
|
-
* warning and does nothing, so it never crashes the host application.
|
|
20
|
-
*
|
|
21
|
-
* This is the counterpart to the private SDK's `register.ts`, adapted to the
|
|
22
|
-
* `src/otel` surface: it drives the existing `init()` rather than registering an
|
|
23
|
-
* import-in-the-middle loader hook (the prototype-patch path does not need one).
|
|
24
|
-
*/
|
|
25
|
-
import { logger } from "../utils.js";
|
|
26
|
-
import { init } from "./init.js";
|
|
27
|
-
|
|
28
|
-
/**
|
|
29
|
-
* Run `init()` if a token is configured. Exported (and awaited at module load)
|
|
30
|
-
* so tests can invoke it deterministically; the module-level `await` below
|
|
31
|
-
* guarantees the preload finishes — including framework auto-discovery and the
|
|
32
|
-
* prototype patch — before Node evaluates the application entry point.
|
|
33
|
-
*/
|
|
34
|
-
export async function registerFromEnv(): Promise<void> {
|
|
35
|
-
if (!process.env.INTROSPECTION_TOKEN) {
|
|
36
|
-
logger.warn(
|
|
37
|
-
"[introspection] register: INTROSPECTION_TOKEN not set; tracing not initialised.",
|
|
38
|
-
);
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
try {
|
|
42
|
-
await init();
|
|
43
|
-
logger.debug("[introspection] register: tracing initialised from preload.");
|
|
44
|
-
} catch (err) {
|
|
45
|
-
// Never let a preload failure take down the host application.
|
|
46
|
-
logger.warn(`[introspection] register: init failed: ${String(err)}`);
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
await registerFromEnv();
|
package/src/otel/setup.ts
DELETED
|
@@ -1,215 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* One-call tracing bootstrap for Introspection.
|
|
3
|
-
*
|
|
4
|
-
* Wires up the three things every host process needs for baggage-based
|
|
5
|
-
* agent / conversation propagation to work:
|
|
6
|
-
*
|
|
7
|
-
* 1. `AsyncLocalStorageContextManager` — without this, `context.with()` is a
|
|
8
|
-
* no-op (the default `NOOP_CONTEXT_MANAGER` silently drops the context),
|
|
9
|
-
* which makes `IntrospectionClient.withAgent()`, `.withConversation()`,
|
|
10
|
-
* etc. dead code.
|
|
11
|
-
* 2. `W3CBaggagePropagator` — so baggage is carried across HTTP boundaries
|
|
12
|
-
* via the `baggage` header (alongside W3C trace-context).
|
|
13
|
-
* 3. A registered `NodeTracerProvider` with an `IntrospectionSpanProcessor`
|
|
14
|
-
* on it — so spans are exported to Introspection and baggage values
|
|
15
|
-
* (conversation / agent IDs) are merged into span attributes at
|
|
16
|
-
* `onEnd()` time.
|
|
17
|
-
*
|
|
18
|
-
* Call this once at process start (before any instrumentors run). It is
|
|
19
|
-
* idempotent across re-invocations — the cached provider is returned and
|
|
20
|
-
* the global registrations are not touched a second time.
|
|
21
|
-
*
|
|
22
|
-
* **Loud-by-default registration.** OTel's `setGlobalContextManager` /
|
|
23
|
-
* `setGlobalPropagator` silently refuse to replace an existing
|
|
24
|
-
* registration. If anything (another lib, a stale call, a test fixture)
|
|
25
|
-
* has already installed a context manager that does NOT propagate W3C
|
|
26
|
-
* baggage, our `withAgent` / `withConversation` calls become silent
|
|
27
|
-
* no-ops and users get spans with no identity attached — exactly the
|
|
28
|
-
* footgun this SDK exists to prevent.
|
|
29
|
-
*
|
|
30
|
-
* `setupTracing` defends against that:
|
|
31
|
-
* - If no manager / propagator is registered: install ours.
|
|
32
|
-
* - If one is registered: WARN (default) or THROW (strict mode) so the
|
|
33
|
-
* host author sees it, rather than discovering missing baggage in
|
|
34
|
-
* production telemetry weeks later.
|
|
35
|
-
*
|
|
36
|
-
* Pass `{ onConflict: "throw" }` (or set `INTROSPECTION_STRICT_TRACING=1`
|
|
37
|
-
* in the env) for the strict variant. Pass `{ onConflict: "replace" }`
|
|
38
|
-
* to force replacement when you know the prior registration is wrong
|
|
39
|
-
* (e.g. test setup, or migrating from another tracer).
|
|
40
|
-
*
|
|
41
|
-
* @example
|
|
42
|
-
* ```ts
|
|
43
|
-
* import { setupTracing, IntrospectionClient } from "@introspection-sdk/introspection-node";
|
|
44
|
-
*
|
|
45
|
-
* setupTracing();
|
|
46
|
-
*
|
|
47
|
-
* const introspect = new IntrospectionClient();
|
|
48
|
-
*
|
|
49
|
-
* await introspect.withAgent("researcher", "researcher-1", () =>
|
|
50
|
-
* introspect.withConversation(conversationId, undefined, () =>
|
|
51
|
-
* client.messages.create({ ... }),
|
|
52
|
-
* ),
|
|
53
|
-
* );
|
|
54
|
-
* ```
|
|
55
|
-
*/
|
|
56
|
-
import { context, propagation } from "@opentelemetry/api";
|
|
57
|
-
import { AsyncLocalStorageContextManager } from "@opentelemetry/context-async-hooks";
|
|
58
|
-
import {
|
|
59
|
-
CompositePropagator,
|
|
60
|
-
W3CBaggagePropagator,
|
|
61
|
-
W3CTraceContextPropagator,
|
|
62
|
-
} from "@opentelemetry/core";
|
|
63
|
-
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
64
|
-
import type { SpanProcessor } from "@opentelemetry/sdk-trace-base";
|
|
65
|
-
|
|
66
|
-
import { logger } from "../utils.js";
|
|
67
|
-
import {
|
|
68
|
-
IntrospectionSpanProcessor,
|
|
69
|
-
type IntrospectionSpanProcessorOptions,
|
|
70
|
-
} from "./span-processor.js";
|
|
71
|
-
|
|
72
|
-
let registeredProvider: NodeTracerProvider | undefined;
|
|
73
|
-
|
|
74
|
-
export type ConflictBehavior = "warn" | "throw" | "replace";
|
|
75
|
-
|
|
76
|
-
export interface SetupTracingOptions extends IntrospectionSpanProcessorOptions {
|
|
77
|
-
/**
|
|
78
|
-
* What to do if a context manager or propagator is already registered.
|
|
79
|
-
*
|
|
80
|
-
* - `"warn"` (default) — log a warning and continue with the existing
|
|
81
|
-
* registration. Spans still flow but baggage may not propagate as
|
|
82
|
-
* expected. Set `INTROSPECTION_STRICT_TRACING=1` to upgrade to throw.
|
|
83
|
-
* - `"throw"` — throw an error. Use in environments where a silent
|
|
84
|
-
* baggage drop is unacceptable (production telemetry pipelines).
|
|
85
|
-
* - `"replace"` — force-register over the existing manager / propagator.
|
|
86
|
-
* Useful in tests or when migrating from another tracer; can break
|
|
87
|
-
* other libraries that relied on the previous registration.
|
|
88
|
-
*/
|
|
89
|
-
onConflict?: ConflictBehavior;
|
|
90
|
-
|
|
91
|
-
/**
|
|
92
|
-
* Extra span processors attached to the same `NodeTracerProvider`, so a
|
|
93
|
-
* single set of spans fans out to other backends (Langfuse, Arize, …).
|
|
94
|
-
* This is the low-level dual-export hook:
|
|
95
|
-
*
|
|
96
|
-
* ```ts
|
|
97
|
-
* setupTracing({
|
|
98
|
-
* serviceName: "my-app",
|
|
99
|
-
* additionalSpanProcessors: [new BatchSpanProcessor(langfuseExporter)],
|
|
100
|
-
* });
|
|
101
|
-
* ```
|
|
102
|
-
*
|
|
103
|
-
* Note: the {@link IntrospectionSpanProcessor} forwards its *own* converted
|
|
104
|
-
* copy of each span to Introspection — it does not mutate the shared span —
|
|
105
|
-
* so these processors receive the raw span and run independently of it.
|
|
106
|
-
* Order is therefore irrelevant. `introspection.init({ spanProcessors })`
|
|
107
|
-
* is the equivalent for the one-liner surface.
|
|
108
|
-
*/
|
|
109
|
-
additionalSpanProcessors?: SpanProcessor[];
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
function defaultConflictBehavior(): ConflictBehavior {
|
|
113
|
-
return process.env.INTROSPECTION_STRICT_TRACING ? "throw" : "warn";
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
function registerContextManager(behavior: ConflictBehavior): void {
|
|
117
|
-
const ok = context.setGlobalContextManager(
|
|
118
|
-
new AsyncLocalStorageContextManager().enable(),
|
|
119
|
-
);
|
|
120
|
-
if (ok) return;
|
|
121
|
-
// OTel silently refuses replacement — we have to disable+retry to force.
|
|
122
|
-
const msg =
|
|
123
|
-
"setupTracing: another OTel context manager was already registered. " +
|
|
124
|
-
"withAgent / withConversation baggage scopes will not propagate unless " +
|
|
125
|
-
"that manager is an AsyncLocalStorageContextManager honoring W3C baggage.";
|
|
126
|
-
if (behavior === "throw") {
|
|
127
|
-
throw new Error(`[introspection] ${msg}`);
|
|
128
|
-
}
|
|
129
|
-
if (behavior === "replace") {
|
|
130
|
-
context.disable();
|
|
131
|
-
if (
|
|
132
|
-
!context.setGlobalContextManager(
|
|
133
|
-
new AsyncLocalStorageContextManager().enable(),
|
|
134
|
-
)
|
|
135
|
-
) {
|
|
136
|
-
throw new Error(
|
|
137
|
-
"[introspection] setupTracing: failed to replace context manager " +
|
|
138
|
-
"even after disable(); the OTel global registry is in a bad state.",
|
|
139
|
-
);
|
|
140
|
-
}
|
|
141
|
-
return;
|
|
142
|
-
}
|
|
143
|
-
logger.warn(msg);
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
function registerPropagator(behavior: ConflictBehavior): void {
|
|
147
|
-
const propagator = new CompositePropagator({
|
|
148
|
-
propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()],
|
|
149
|
-
});
|
|
150
|
-
const ok = propagation.setGlobalPropagator(propagator);
|
|
151
|
-
if (ok) return;
|
|
152
|
-
const msg =
|
|
153
|
-
"setupTracing: another OTel propagator was already registered. " +
|
|
154
|
-
"Cross-process baggage propagation depends on the existing propagator " +
|
|
155
|
-
"carrying the W3C `baggage` header.";
|
|
156
|
-
if (behavior === "throw") {
|
|
157
|
-
throw new Error(`[introspection] ${msg}`);
|
|
158
|
-
}
|
|
159
|
-
if (behavior === "replace") {
|
|
160
|
-
propagation.disable();
|
|
161
|
-
if (!propagation.setGlobalPropagator(propagator)) {
|
|
162
|
-
throw new Error(
|
|
163
|
-
"[introspection] setupTracing: failed to replace propagator even " +
|
|
164
|
-
"after disable(); the OTel global registry is in a bad state.",
|
|
165
|
-
);
|
|
166
|
-
}
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
logger.warn(msg);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
/**
|
|
173
|
-
* Install the OTel context manager + W3C (trace-context + baggage) propagator
|
|
174
|
-
* that Introspection's baggage scopes depend on.
|
|
175
|
-
*
|
|
176
|
-
* Shared by {@link setupTracing} and `introspection.init()`. `behavior`
|
|
177
|
-
* controls what happens when a manager / propagator is already registered
|
|
178
|
-
* (see {@link ConflictBehavior}).
|
|
179
|
-
*/
|
|
180
|
-
export function registerOTelGlobals(behavior?: ConflictBehavior): void {
|
|
181
|
-
const resolved = behavior ?? defaultConflictBehavior();
|
|
182
|
-
registerContextManager(resolved);
|
|
183
|
-
registerPropagator(resolved);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
/**
|
|
187
|
-
* Initialise Introspection tracing for a Node.js process.
|
|
188
|
-
*/
|
|
189
|
-
export function setupTracing(
|
|
190
|
-
options?: SetupTracingOptions,
|
|
191
|
-
): NodeTracerProvider {
|
|
192
|
-
if (registeredProvider) {
|
|
193
|
-
return registeredProvider;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
const behavior: ConflictBehavior =
|
|
197
|
-
options?.onConflict ?? defaultConflictBehavior();
|
|
198
|
-
|
|
199
|
-
registerOTelGlobals(behavior);
|
|
200
|
-
|
|
201
|
-
const provider = new NodeTracerProvider({
|
|
202
|
-
// Order is irrelevant: IntrospectionSpanProcessor exports its own converted
|
|
203
|
-
// copy and does not mutate the shared span, so co-located dual-export
|
|
204
|
-
// processors receive the raw span regardless of position. Listed first here
|
|
205
|
-
// (and in init()) purely for a consistent, readable ordering story.
|
|
206
|
-
spanProcessors: [
|
|
207
|
-
new IntrospectionSpanProcessor(options),
|
|
208
|
-
...(options?.additionalSpanProcessors ?? []),
|
|
209
|
-
],
|
|
210
|
-
});
|
|
211
|
-
provider.register();
|
|
212
|
-
|
|
213
|
-
registeredProvider = provider;
|
|
214
|
-
return provider;
|
|
215
|
-
}
|