@introspection-sdk/introspection-node 0.19.1 → 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 +4 -7
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +3 -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 -266
  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,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
- }
@@ -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
- }