@cloudbase/agent-observability 1.0.1-alpha.10

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.
@@ -0,0 +1,417 @@
1
+ import { Span, TimeInput } from "@opentelemetry/api";
2
+
3
+ import { createObservationAttributes, createTraceAttributes } from "./attributes.js";
4
+ import { getTracer } from "./tracerProvider.js";
5
+ import {
6
+ BaseSpanAttributes,
7
+ LLMAttributes,
8
+ TraceAttributes,
9
+ ObservationType,
10
+ } from "../types.js";
11
+ import type {
12
+ ToolAttributes,
13
+ AgentAttributes,
14
+ ChainAttributes,
15
+ RetrieverAttributes,
16
+ RerankerAttributes,
17
+ EvaluatorAttributes,
18
+ GuardrailAttributes,
19
+ EmbeddingAttributes,
20
+ ObservationAttributes,
21
+ } from "../types.js";
22
+
23
+ /**
24
+ * Union type representing any observation wrapper.
25
+ *
26
+ * @public
27
+ */
28
+ export type Observation =
29
+ | ObservationSpan
30
+ | ObservationLLM
31
+ | ObservationEmbedding
32
+ | ObservationAgent
33
+ | ObservationTool
34
+ | ObservationChain
35
+ | ObservationRetriever
36
+ | ObservationReranker
37
+ | ObservationEvaluator
38
+ | ObservationGuardrail;
39
+
40
+ /**
41
+ * Parameters for creating an observation wrapper.
42
+ *
43
+ * @internal
44
+ */
45
+ type ObservationParams = {
46
+ otelSpan: Span;
47
+ type: ObservationType;
48
+ attributes?: BaseSpanAttributes | LLMAttributes;
49
+ };
50
+
51
+ /**
52
+ * Base class for all observation wrappers.
53
+ *
54
+ * Provides common functionality for all observation types including:
55
+ * - OpenTelemetry span integration
56
+ * - Unique identification (span ID, trace ID)
57
+ * - Lifecycle management (update, end)
58
+ * - Trace context management
59
+ * - Child observation creation
60
+ *
61
+ * @internal
62
+ */
63
+ abstract class BaseObservation {
64
+ /** The underlying OpenTelemetry span */
65
+ public readonly otelSpan: Span;
66
+ /** The observation type */
67
+ public readonly type: ObservationType;
68
+ /** The span ID from the OpenTelemetry span context */
69
+ public id: string;
70
+ /** The trace ID from the OpenTelemetry span context */
71
+ public traceId: string;
72
+
73
+ constructor(params: ObservationParams) {
74
+ this.otelSpan = params.otelSpan;
75
+ this.id = params.otelSpan.spanContext().spanId;
76
+ this.traceId = params.otelSpan.spanContext().traceId;
77
+ this.type = params.type;
78
+
79
+ if (params.attributes) {
80
+ this.otelSpan.setAttributes(
81
+ createObservationAttributes(params.type, params.attributes),
82
+ );
83
+ }
84
+ }
85
+
86
+ /** Gets the AG-Kit OpenTelemetry tracer instance */
87
+ protected get tracer() {
88
+ return getTracer();
89
+ }
90
+
91
+ /**
92
+ * Ends the observation, marking it as complete.
93
+ *
94
+ * @param endTime - Optional end time, defaults to current time
95
+ */
96
+ public end(endTime?: TimeInput) {
97
+ this.otelSpan.end(endTime);
98
+ }
99
+
100
+ /**
101
+ * Updates the OTEL span attributes.
102
+ *
103
+ * @param attributes - Attributes to update
104
+ * @internal
105
+ */
106
+ updateOtelSpanAttributes(attributes: ObservationAttributes) {
107
+ this.otelSpan.setAttributes(
108
+ createObservationAttributes(this.type, attributes),
109
+ );
110
+ }
111
+
112
+ /**
113
+ * Updates the parent trace with new attributes.
114
+ *
115
+ * @param attributes - Trace attributes to set
116
+ * @returns This observation for method chaining
117
+ */
118
+ public updateTrace(attributes: TraceAttributes) {
119
+ this.otelSpan.setAttributes(createTraceAttributes(attributes));
120
+ return this;
121
+ }
122
+
123
+ /**
124
+ * Creates a new child observation within this observation's context.
125
+ *
126
+ * @param name - Name for the child observation
127
+ * @param attributes - Type-specific attributes
128
+ * @param options - Configuration including observation type
129
+ * @returns Child observation instance
130
+ */
131
+ public startObservation(
132
+ name: string,
133
+ attributes: LLMAttributes,
134
+ options: { asType: "llm" },
135
+ ): ObservationLLM;
136
+ public startObservation(
137
+ name: string,
138
+ attributes: EmbeddingAttributes,
139
+ options: { asType: "embedding" },
140
+ ): ObservationEmbedding;
141
+ public startObservation(
142
+ name: string,
143
+ attributes: AgentAttributes,
144
+ options: { asType: "agent" },
145
+ ): ObservationAgent;
146
+ public startObservation(
147
+ name: string,
148
+ attributes: ToolAttributes,
149
+ options: { asType: "tool" },
150
+ ): ObservationTool;
151
+ public startObservation(
152
+ name: string,
153
+ attributes: ChainAttributes,
154
+ options: { asType: "chain" },
155
+ ): ObservationChain;
156
+ public startObservation(
157
+ name: string,
158
+ attributes: RetrieverAttributes,
159
+ options: { asType: "retriever" },
160
+ ): ObservationRetriever;
161
+ public startObservation(
162
+ name: string,
163
+ attributes: RerankerAttributes,
164
+ options: { asType: "reranker" },
165
+ ): ObservationReranker;
166
+ public startObservation(
167
+ name: string,
168
+ attributes: EvaluatorAttributes,
169
+ options: { asType: "evaluator" },
170
+ ): ObservationEvaluator;
171
+ public startObservation(
172
+ name: string,
173
+ attributes: GuardrailAttributes,
174
+ options: { asType: "guardrail" },
175
+ ): ObservationGuardrail;
176
+ public startObservation(
177
+ name: string,
178
+ attributes?: BaseSpanAttributes,
179
+ options?: { asType?: "span" },
180
+ ): ObservationSpan;
181
+ public startObservation(
182
+ name: string,
183
+ attributes?:
184
+ | BaseSpanAttributes
185
+ | LLMAttributes
186
+ | ToolAttributes
187
+ | AgentAttributes
188
+ | ChainAttributes
189
+ | RetrieverAttributes
190
+ | RerankerAttributes
191
+ | EvaluatorAttributes
192
+ | GuardrailAttributes
193
+ | EmbeddingAttributes,
194
+ options?: { asType?: ObservationType },
195
+ ): Observation {
196
+ // Import here to avoid circular dependency
197
+ const { startObservation: startObs } = require("../index.js");
198
+ const { asType = "span" } = options || {};
199
+
200
+ return startObs(name, attributes, {
201
+ asType: asType as "span",
202
+ parentSpanContext: this.otelSpan.spanContext(),
203
+ });
204
+ }
205
+ }
206
+
207
+ // Type-specific observation classes
208
+
209
+ type ObservationSpanParams = {
210
+ otelSpan: Span;
211
+ attributes?: BaseSpanAttributes;
212
+ };
213
+
214
+ /**
215
+ * General-purpose observation for tracking operations.
216
+ *
217
+ * @public
218
+ */
219
+ export class ObservationSpan extends BaseObservation {
220
+ constructor(params: ObservationSpanParams) {
221
+ super({ ...params, type: "span" });
222
+ }
223
+
224
+ public update(attributes: BaseSpanAttributes): ObservationSpan {
225
+ super.updateOtelSpanAttributes(attributes);
226
+ return this;
227
+ }
228
+ }
229
+
230
+ type ObservationLLMParams = {
231
+ otelSpan: Span;
232
+ attributes?: LLMAttributes;
233
+ };
234
+
235
+ /**
236
+ * LLM observation for tracking language model calls.
237
+ *
238
+ * @public
239
+ */
240
+ export class ObservationLLM extends BaseObservation {
241
+ constructor(params: ObservationLLMParams) {
242
+ super({ ...params, type: "llm" });
243
+ }
244
+
245
+ public update(attributes: LLMAttributes): ObservationLLM {
246
+ super.updateOtelSpanAttributes(attributes);
247
+ return this;
248
+ }
249
+ }
250
+
251
+ type ObservationEmbeddingParams = {
252
+ otelSpan: Span;
253
+ attributes?: EmbeddingAttributes;
254
+ };
255
+
256
+ /**
257
+ * Embedding observation for tracking embedding operations.
258
+ *
259
+ * @public
260
+ */
261
+ export class ObservationEmbedding extends BaseObservation {
262
+ constructor(params: ObservationEmbeddingParams) {
263
+ super({ ...params, type: "embedding" });
264
+ }
265
+
266
+ public update(attributes: EmbeddingAttributes): ObservationEmbedding {
267
+ super.updateOtelSpanAttributes(attributes);
268
+ return this;
269
+ }
270
+ }
271
+
272
+ type ObservationAgentParams = {
273
+ otelSpan: Span;
274
+ attributes?: AgentAttributes;
275
+ };
276
+
277
+ /**
278
+ * Agent observation for tracking AI agent workflows.
279
+ *
280
+ * @public
281
+ */
282
+ export class ObservationAgent extends BaseObservation {
283
+ constructor(params: ObservationAgentParams) {
284
+ super({ ...params, type: "agent" });
285
+ }
286
+
287
+ public update(attributes: AgentAttributes): ObservationAgent {
288
+ super.updateOtelSpanAttributes(attributes);
289
+ return this;
290
+ }
291
+ }
292
+
293
+ type ObservationToolParams = {
294
+ otelSpan: Span;
295
+ attributes?: ToolAttributes;
296
+ };
297
+
298
+ /**
299
+ * Tool observation for tracking tool calls.
300
+ *
301
+ * @public
302
+ */
303
+ export class ObservationTool extends BaseObservation {
304
+ constructor(params: ObservationToolParams) {
305
+ super({ ...params, type: "tool" });
306
+ }
307
+
308
+ public update(attributes: ToolAttributes): ObservationTool {
309
+ super.updateOtelSpanAttributes(attributes);
310
+ return this;
311
+ }
312
+ }
313
+
314
+ type ObservationChainParams = {
315
+ otelSpan: Span;
316
+ attributes?: ChainAttributes;
317
+ };
318
+
319
+ /**
320
+ * Chain observation for tracking multi-step workflows.
321
+ *
322
+ * @public
323
+ */
324
+ export class ObservationChain extends BaseObservation {
325
+ constructor(params: ObservationChainParams) {
326
+ super({ ...params, type: "chain" });
327
+ }
328
+
329
+ public update(attributes: ChainAttributes): ObservationChain {
330
+ super.updateOtelSpanAttributes(attributes);
331
+ return this;
332
+ }
333
+ }
334
+
335
+ type ObservationRetrieverParams = {
336
+ otelSpan: Span;
337
+ attributes?: RetrieverAttributes;
338
+ };
339
+
340
+ /**
341
+ * Retriever observation for tracking document retrieval.
342
+ *
343
+ * @public
344
+ */
345
+ export class ObservationRetriever extends BaseObservation {
346
+ constructor(params: ObservationRetrieverParams) {
347
+ super({ ...params, type: "retriever" });
348
+ }
349
+
350
+ public update(attributes: RetrieverAttributes): ObservationRetriever {
351
+ super.updateOtelSpanAttributes(attributes);
352
+ return this;
353
+ }
354
+ }
355
+
356
+ type ObservationRerankerParams = {
357
+ otelSpan: Span;
358
+ attributes?: RerankerAttributes;
359
+ };
360
+
361
+ /**
362
+ * Reranker observation for tracking reranking operations.
363
+ *
364
+ * @public
365
+ */
366
+ export class ObservationReranker extends BaseObservation {
367
+ constructor(params: ObservationRerankerParams) {
368
+ super({ ...params, type: "reranker" });
369
+ }
370
+
371
+ public update(attributes: RerankerAttributes): ObservationReranker {
372
+ super.updateOtelSpanAttributes(attributes);
373
+ return this;
374
+ }
375
+ }
376
+
377
+ type ObservationEvaluatorParams = {
378
+ otelSpan: Span;
379
+ attributes?: EvaluatorAttributes;
380
+ };
381
+
382
+ /**
383
+ * Evaluator observation for tracking evaluation operations.
384
+ *
385
+ * @public
386
+ */
387
+ export class ObservationEvaluator extends BaseObservation {
388
+ constructor(params: ObservationEvaluatorParams) {
389
+ super({ ...params, type: "evaluator" });
390
+ }
391
+
392
+ public update(attributes: EvaluatorAttributes): ObservationEvaluator {
393
+ super.updateOtelSpanAttributes(attributes);
394
+ return this;
395
+ }
396
+ }
397
+
398
+ type ObservationGuardrailParams = {
399
+ otelSpan: Span;
400
+ attributes?: GuardrailAttributes;
401
+ };
402
+
403
+ /**
404
+ * Guardrail observation for tracking safety checks.
405
+ *
406
+ * @public
407
+ */
408
+ export class ObservationGuardrail extends BaseObservation {
409
+ constructor(params: ObservationGuardrailParams) {
410
+ super({ ...params, type: "guardrail" });
411
+ }
412
+
413
+ public update(attributes: GuardrailAttributes): ObservationGuardrail {
414
+ super.updateOtelSpanAttributes(attributes);
415
+ return this;
416
+ }
417
+ }
@@ -0,0 +1,136 @@
1
+ import { TracerProvider, trace, context } from "@opentelemetry/api";
2
+
3
+ const OBSERVABILITY_GLOBAL_SYMBOL = Symbol.for("observability");
4
+
5
+ type ObservabilityGlobalState = {
6
+ isolatedTracerProvider: TracerProvider | null;
7
+ };
8
+
9
+ function createState(): ObservabilityGlobalState {
10
+ return {
11
+ isolatedTracerProvider: null,
12
+ };
13
+ }
14
+
15
+ interface GlobalThis {
16
+ [OBSERVABILITY_GLOBAL_SYMBOL]?: ObservabilityGlobalState;
17
+ }
18
+
19
+ /**
20
+ * Gets the global state for tracing observability.
21
+ *
22
+ * @returns The global state object
23
+ * @internal
24
+ */
25
+ function getObservabilityGlobalState(): ObservabilityGlobalState {
26
+ const initialState = createState();
27
+
28
+ try {
29
+ const g = globalThis as typeof globalThis & GlobalThis;
30
+
31
+ if (typeof g !== "object" || g === null) {
32
+ console.warn(
33
+ "[Observability] globalThis is not available, using fallback state",
34
+ );
35
+ return initialState;
36
+ }
37
+
38
+ if (!g[OBSERVABILITY_GLOBAL_SYMBOL]) {
39
+ Object.defineProperty(g, OBSERVABILITY_GLOBAL_SYMBOL, {
40
+ value: initialState,
41
+ writable: false,
42
+ configurable: false,
43
+ enumerable: false,
44
+ });
45
+ }
46
+
47
+ return g[OBSERVABILITY_GLOBAL_SYMBOL]!;
48
+ } catch (err) {
49
+ console.error(
50
+ `[Observability] Failed to access global state: ${err instanceof Error ? err.message : String(err)}`,
51
+ );
52
+ return initialState;
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Sets an isolated TracerProvider for tracing tracing operations.
58
+ *
59
+ * This allows tracing to use its own TracerProvider instance, separate from
60
+ * the global OpenTelemetry TracerProvider.
61
+ *
62
+ * Note: While this isolates span processing and export, it does NOT provide
63
+ * complete trace isolation. OpenTelemetry context (trace IDs, parent spans)
64
+ * is still shared between the global and isolated providers.
65
+ *
66
+ * @param provider - The TracerProvider instance to use, or null to clear
67
+ *
68
+ * @example
69
+ * ```typescript
70
+ * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
71
+ * import { setTracerProvider } from './observability';
72
+ *
73
+ * const provider = new NodeTracerProvider();
74
+ * setTracerProvider(provider);
75
+ * ```
76
+ *
77
+ * @public
78
+ */
79
+ export function setTracerProvider(provider: TracerProvider | null) {
80
+ getObservabilityGlobalState().isolatedTracerProvider = provider;
81
+ }
82
+
83
+ /**
84
+ * Gets the TracerProvider for tracing tracing operations.
85
+ *
86
+ * Returns the isolated TracerProvider if one has been set via setTracerProvider(),
87
+ * otherwise falls back to the global OpenTelemetry TracerProvider.
88
+ *
89
+ * @returns The TracerProvider instance to use for tracing tracing
90
+ *
91
+ * @example
92
+ * ```typescript
93
+ * import { getTracerProvider } from './observability';
94
+ *
95
+ * const provider = getTracerProvider();
96
+ * const tracer = provider.getTracer('my-tracer', '1.0.0');
97
+ * ```
98
+ *
99
+ * @public
100
+ */
101
+ export function getTracerProvider(): TracerProvider {
102
+ const { isolatedTracerProvider } = getObservabilityGlobalState();
103
+
104
+ if (isolatedTracerProvider) return isolatedTracerProvider;
105
+
106
+ return trace.getTracerProvider();
107
+ }
108
+
109
+ /**
110
+ * Gets the OpenTelemetry tracer instance for tracing.
111
+ *
112
+ * Returns a tracer specifically configured for tracing with the correct
113
+ * tracer name and version.
114
+ *
115
+ * @returns The tracing OpenTelemetry tracer instance
116
+ *
117
+ * @example
118
+ * ```typescript
119
+ * import { getTracer } from './observability';
120
+ *
121
+ * const tracer = getTracer();
122
+ * const span = tracer.startSpan('my-operation');
123
+ * ```
124
+ *
125
+ * @public
126
+ */
127
+ export function getTracer() {
128
+ return getTracerProvider().getTracer(
129
+ OBSERVABILITY_SDK_NAME,
130
+ OBSERVABILITY_SDK_VERSION
131
+ );
132
+ }
133
+
134
+ // SDK version - could be read from package.json in production
135
+ const OBSERVABILITY_SDK_NAME = "ag-kit-observability";
136
+ const OBSERVABILITY_SDK_VERSION = "0.1.0";