@cloudbase/agent-observability 1.0.1-alpha.9
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 +231 -0
- package/dist/chunk-NFEGQTCC.mjs +27 -0
- package/dist/chunk-NFEGQTCC.mjs.map +1 -0
- package/dist/chunk-ZGEMAYS4.mjs +716 -0
- package/dist/chunk-ZGEMAYS4.mjs.map +1 -0
- package/dist/esm-PGEDANAI.mjs +1030 -0
- package/dist/esm-PGEDANAI.mjs.map +1 -0
- package/dist/index.d.mts +728 -0
- package/dist/index.d.ts +728 -0
- package/dist/index.js +732 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +52 -0
- package/dist/index.mjs.map +1 -0
- package/dist/langchain.d.mts +108 -0
- package/dist/langchain.d.ts +108 -0
- package/dist/langchain.js +1237 -0
- package/dist/langchain.js.map +1 -0
- package/dist/langchain.mjs +535 -0
- package/dist/langchain.mjs.map +1 -0
- package/dist/server.d.mts +163 -0
- package/dist/server.d.ts +163 -0
- package/dist/server.js +1528 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +175 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +91 -0
- package/src/core/attributes.ts +233 -0
- package/src/core/constants.ts +75 -0
- package/src/core/spanWrapper.ts +417 -0
- package/src/core/tracerProvider.ts +136 -0
- package/src/index.ts +775 -0
- package/src/langchain/CallbackHandler.ts +893 -0
- package/src/langchain/index.ts +7 -0
- package/src/server/config.ts +160 -0
- package/src/server/index.ts +21 -0
- package/src/server/setup.ts +344 -0
- package/src/types.ts +254 -0
package/src/index.ts
ADDED
|
@@ -0,0 +1,775 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observability - OpenTelemetry-based tracing with OpenInference semantic conventions
|
|
3
|
+
*
|
|
4
|
+
* @packageDocumentation
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { trace, context, TimeInput, SpanStatusCode, Span, SpanContext } from "@opentelemetry/api";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
createObservationAttributes,
|
|
11
|
+
createTraceAttributes,
|
|
12
|
+
} from "./core/attributes.js";
|
|
13
|
+
import {
|
|
14
|
+
ObservationSpan,
|
|
15
|
+
ObservationLLM,
|
|
16
|
+
ObservationEmbedding,
|
|
17
|
+
ObservationAgent,
|
|
18
|
+
ObservationTool,
|
|
19
|
+
ObservationChain,
|
|
20
|
+
ObservationRetriever,
|
|
21
|
+
ObservationReranker,
|
|
22
|
+
ObservationEvaluator,
|
|
23
|
+
ObservationGuardrail,
|
|
24
|
+
type Observation,
|
|
25
|
+
} from "./core/spanWrapper.js";
|
|
26
|
+
import { getTracer } from "./core/tracerProvider.js";
|
|
27
|
+
import {
|
|
28
|
+
ObservationType,
|
|
29
|
+
ObservationLevel,
|
|
30
|
+
BaseSpanAttributes,
|
|
31
|
+
LLMAttributes,
|
|
32
|
+
ToolAttributes,
|
|
33
|
+
AgentAttributes,
|
|
34
|
+
ChainAttributes,
|
|
35
|
+
RetrieverAttributes,
|
|
36
|
+
RerankerAttributes,
|
|
37
|
+
EvaluatorAttributes,
|
|
38
|
+
GuardrailAttributes,
|
|
39
|
+
EmbeddingAttributes,
|
|
40
|
+
ObservationAttributes,
|
|
41
|
+
TraceAttributes,
|
|
42
|
+
} from "./types.js";
|
|
43
|
+
|
|
44
|
+
// Export types
|
|
45
|
+
export type {
|
|
46
|
+
ObservationType,
|
|
47
|
+
ObservationLevel,
|
|
48
|
+
BaseSpanAttributes,
|
|
49
|
+
LLMAttributes,
|
|
50
|
+
ToolAttributes,
|
|
51
|
+
AgentAttributes,
|
|
52
|
+
ChainAttributes,
|
|
53
|
+
RetrieverAttributes,
|
|
54
|
+
RerankerAttributes,
|
|
55
|
+
EvaluatorAttributes,
|
|
56
|
+
GuardrailAttributes,
|
|
57
|
+
EmbeddingAttributes,
|
|
58
|
+
ObservationAttributes,
|
|
59
|
+
TraceAttributes,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
// Export observation classes
|
|
63
|
+
export {
|
|
64
|
+
ObservationSpan,
|
|
65
|
+
ObservationLLM,
|
|
66
|
+
ObservationEmbedding,
|
|
67
|
+
ObservationAgent,
|
|
68
|
+
ObservationTool,
|
|
69
|
+
ObservationChain,
|
|
70
|
+
ObservationRetriever,
|
|
71
|
+
ObservationReranker,
|
|
72
|
+
ObservationEvaluator,
|
|
73
|
+
ObservationGuardrail,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Export observation union type
|
|
77
|
+
export type { Observation };
|
|
78
|
+
|
|
79
|
+
// Export core functions
|
|
80
|
+
export {
|
|
81
|
+
createTraceAttributes,
|
|
82
|
+
createObservationAttributes,
|
|
83
|
+
} from "./core/attributes.js";
|
|
84
|
+
export {
|
|
85
|
+
setTracerProvider,
|
|
86
|
+
getTracerProvider,
|
|
87
|
+
getTracer,
|
|
88
|
+
} from "./core/tracerProvider.js";
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Options for starting observations (spans).
|
|
92
|
+
*
|
|
93
|
+
* @public
|
|
94
|
+
*/
|
|
95
|
+
export type StartObservationOptions = {
|
|
96
|
+
/** Custom start time for the observation */
|
|
97
|
+
startTime?: Date;
|
|
98
|
+
/** Parent span context to attach this observation to */
|
|
99
|
+
parentSpanContext?: SpanContext;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Options for startObservation function.
|
|
104
|
+
*
|
|
105
|
+
* @public
|
|
106
|
+
*/
|
|
107
|
+
export type StartObservationOpts = StartObservationOptions & {
|
|
108
|
+
/** Type of observation to create. Defaults to 'span' */
|
|
109
|
+
asType?: ObservationType;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Creates an OpenTelemetry span with the AG-Kit tracer.
|
|
114
|
+
*
|
|
115
|
+
* @param params - Parameters for span creation
|
|
116
|
+
* @returns The created OpenTelemetry span
|
|
117
|
+
* @internal
|
|
118
|
+
*/
|
|
119
|
+
function createOtelSpan(params: {
|
|
120
|
+
name: string;
|
|
121
|
+
startTime?: TimeInput;
|
|
122
|
+
parentSpanContext?: SpanContext;
|
|
123
|
+
}): Span {
|
|
124
|
+
return getTracer().startSpan(
|
|
125
|
+
params.name,
|
|
126
|
+
{ startTime: params.startTime },
|
|
127
|
+
createParentContext(params.parentSpanContext),
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Creates a parent context from a span context.
|
|
133
|
+
*
|
|
134
|
+
* @param parentSpanContext - The span context to use as parent
|
|
135
|
+
* @returns The created context or undefined if no parent provided
|
|
136
|
+
* @internal
|
|
137
|
+
*/
|
|
138
|
+
function createParentContext(
|
|
139
|
+
parentSpanContext?: SpanContext,
|
|
140
|
+
): ReturnType<typeof trace.setSpanContext> | undefined {
|
|
141
|
+
if (!parentSpanContext) return;
|
|
142
|
+
return trace.setSpanContext(context.active(), parentSpanContext);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Function overloads for proper type inference
|
|
146
|
+
// Generic overload for dynamic asType (returns Observation union)
|
|
147
|
+
export function startObservation(
|
|
148
|
+
name: string,
|
|
149
|
+
attributes?: BaseSpanAttributes,
|
|
150
|
+
options?: StartObservationOpts & { asType?: ObservationType },
|
|
151
|
+
): Observation;
|
|
152
|
+
|
|
153
|
+
// Type-specific overloads for precise type inference
|
|
154
|
+
export function startObservation(
|
|
155
|
+
name: string,
|
|
156
|
+
attributes: LLMAttributes,
|
|
157
|
+
options: StartObservationOpts & { asType: "llm" },
|
|
158
|
+
): ObservationLLM;
|
|
159
|
+
export function startObservation(
|
|
160
|
+
name: string,
|
|
161
|
+
attributes: EmbeddingAttributes,
|
|
162
|
+
options: StartObservationOpts & { asType: "embedding" },
|
|
163
|
+
): ObservationEmbedding;
|
|
164
|
+
export function startObservation(
|
|
165
|
+
name: string,
|
|
166
|
+
attributes: AgentAttributes,
|
|
167
|
+
options: StartObservationOpts & { asType: "agent" },
|
|
168
|
+
): ObservationAgent;
|
|
169
|
+
export function startObservation(
|
|
170
|
+
name: string,
|
|
171
|
+
attributes: ToolAttributes,
|
|
172
|
+
options: StartObservationOpts & { asType: "tool" },
|
|
173
|
+
): ObservationTool;
|
|
174
|
+
export function startObservation(
|
|
175
|
+
name: string,
|
|
176
|
+
attributes: ChainAttributes,
|
|
177
|
+
options: StartObservationOpts & { asType: "chain" },
|
|
178
|
+
): ObservationChain;
|
|
179
|
+
export function startObservation(
|
|
180
|
+
name: string,
|
|
181
|
+
attributes: RetrieverAttributes,
|
|
182
|
+
options: StartObservationOpts & { asType: "retriever" },
|
|
183
|
+
): ObservationRetriever;
|
|
184
|
+
export function startObservation(
|
|
185
|
+
name: string,
|
|
186
|
+
attributes: RerankerAttributes,
|
|
187
|
+
options: StartObservationOpts & { asType: "reranker" },
|
|
188
|
+
): ObservationReranker;
|
|
189
|
+
export function startObservation(
|
|
190
|
+
name: string,
|
|
191
|
+
attributes: EvaluatorAttributes,
|
|
192
|
+
options: StartObservationOpts & { asType: "evaluator" },
|
|
193
|
+
): ObservationEvaluator;
|
|
194
|
+
export function startObservation(
|
|
195
|
+
name: string,
|
|
196
|
+
attributes: GuardrailAttributes,
|
|
197
|
+
options: StartObservationOpts & { asType: "guardrail" },
|
|
198
|
+
): ObservationGuardrail;
|
|
199
|
+
export function startObservation(
|
|
200
|
+
name: string,
|
|
201
|
+
attributes?: BaseSpanAttributes,
|
|
202
|
+
options?: StartObservationOpts & { asType?: "span" },
|
|
203
|
+
): ObservationSpan;
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Creates and starts a new AG-Kit observation.
|
|
207
|
+
*
|
|
208
|
+
* Supports multiple observation types with full TypeScript type safety:
|
|
209
|
+
* - **span**: General-purpose operations (default)
|
|
210
|
+
* - **llm**: LLM calls and AI model interactions
|
|
211
|
+
* - **embedding**: Text embedding and vector operations
|
|
212
|
+
* - **agent**: AI agent workflows
|
|
213
|
+
* - **tool**: Individual tool calls
|
|
214
|
+
* - **chain**: Multi-step processes
|
|
215
|
+
* - **retriever**: Document retrieval
|
|
216
|
+
* - **reranker**: Result reranking
|
|
217
|
+
* - **evaluator**: Quality assessment
|
|
218
|
+
* - **guardrail**: Safety checks
|
|
219
|
+
*
|
|
220
|
+
* @param name - Descriptive name for the observation
|
|
221
|
+
* @param attributes - Type-specific attributes
|
|
222
|
+
* @param options - Configuration options
|
|
223
|
+
* @returns Strongly-typed observation object
|
|
224
|
+
*
|
|
225
|
+
* @example
|
|
226
|
+
* ```typescript
|
|
227
|
+
* import { startObservation } from './observability';
|
|
228
|
+
*
|
|
229
|
+
* // LLM observation
|
|
230
|
+
* const llm = startObservation('openai-gpt-4', {
|
|
231
|
+
* input: [{ role: 'user', content: 'Hello' }],
|
|
232
|
+
* model: 'gpt-4',
|
|
233
|
+
* modelParameters: { temperature: 0.7 }
|
|
234
|
+
* }, { asType: 'llm' });
|
|
235
|
+
*
|
|
236
|
+
* // Tool observation
|
|
237
|
+
* const tool = startObservation('weather-api', {
|
|
238
|
+
* input: { location: 'SF' }
|
|
239
|
+
* }, { asType: 'tool' });
|
|
240
|
+
*
|
|
241
|
+
* // Chain observation
|
|
242
|
+
* const chain = startObservation('rag-pipeline', {
|
|
243
|
+
* input: { question: 'What is AI?' }
|
|
244
|
+
* }, { asType: 'chain' });
|
|
245
|
+
* ```
|
|
246
|
+
*
|
|
247
|
+
* @public
|
|
248
|
+
*/
|
|
249
|
+
export function startObservation(
|
|
250
|
+
name: string,
|
|
251
|
+
attributes?:
|
|
252
|
+
| BaseSpanAttributes
|
|
253
|
+
| LLMAttributes
|
|
254
|
+
| EmbeddingAttributes
|
|
255
|
+
| AgentAttributes
|
|
256
|
+
| ToolAttributes
|
|
257
|
+
| ChainAttributes
|
|
258
|
+
| RetrieverAttributes
|
|
259
|
+
| RerankerAttributes
|
|
260
|
+
| EvaluatorAttributes
|
|
261
|
+
| GuardrailAttributes,
|
|
262
|
+
options?: StartObservationOpts,
|
|
263
|
+
): Observation {
|
|
264
|
+
const { asType = "span", ...observationOptions } = options || {};
|
|
265
|
+
|
|
266
|
+
const otelSpan = createOtelSpan({
|
|
267
|
+
name,
|
|
268
|
+
...observationOptions,
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
switch (asType) {
|
|
272
|
+
case "llm":
|
|
273
|
+
return new ObservationLLM({
|
|
274
|
+
otelSpan,
|
|
275
|
+
attributes: attributes as LLMAttributes,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
case "embedding":
|
|
279
|
+
return new ObservationEmbedding({
|
|
280
|
+
otelSpan,
|
|
281
|
+
attributes: attributes as EmbeddingAttributes,
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
case "agent":
|
|
285
|
+
return new ObservationAgent({
|
|
286
|
+
otelSpan,
|
|
287
|
+
attributes: attributes as AgentAttributes,
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
case "tool":
|
|
291
|
+
return new ObservationTool({
|
|
292
|
+
otelSpan,
|
|
293
|
+
attributes: attributes as ToolAttributes,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
case "chain":
|
|
297
|
+
return new ObservationChain({
|
|
298
|
+
otelSpan,
|
|
299
|
+
attributes: attributes as ChainAttributes,
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
case "retriever":
|
|
303
|
+
return new ObservationRetriever({
|
|
304
|
+
otelSpan,
|
|
305
|
+
attributes: attributes as RetrieverAttributes,
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
case "reranker":
|
|
309
|
+
return new ObservationReranker({
|
|
310
|
+
otelSpan,
|
|
311
|
+
attributes: attributes as RerankerAttributes,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
case "evaluator":
|
|
315
|
+
return new ObservationEvaluator({
|
|
316
|
+
otelSpan,
|
|
317
|
+
attributes: attributes as EvaluatorAttributes,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
case "guardrail":
|
|
321
|
+
return new ObservationGuardrail({
|
|
322
|
+
otelSpan,
|
|
323
|
+
attributes: attributes as GuardrailAttributes,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
case "span":
|
|
327
|
+
default:
|
|
328
|
+
return new ObservationSpan({
|
|
329
|
+
otelSpan,
|
|
330
|
+
attributes: attributes as BaseSpanAttributes,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/**
|
|
336
|
+
* Updates the currently active trace with new attributes.
|
|
337
|
+
*
|
|
338
|
+
* @param attributes - Trace attributes to set
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```typescript
|
|
342
|
+
* import { updateActiveTrace } from './observability';
|
|
343
|
+
*
|
|
344
|
+
* updateActiveTrace({
|
|
345
|
+
* name: 'user-workflow',
|
|
346
|
+
* userId: 'user-123',
|
|
347
|
+
* tags: ['production']
|
|
348
|
+
* });
|
|
349
|
+
* ```
|
|
350
|
+
*
|
|
351
|
+
* @public
|
|
352
|
+
*/
|
|
353
|
+
export function updateActiveTrace(attributes: TraceAttributes) {
|
|
354
|
+
const span = trace.getActiveSpan();
|
|
355
|
+
|
|
356
|
+
if (!span) {
|
|
357
|
+
console.warn(
|
|
358
|
+
"[Observability] No active OTEL span in context. Skipping trace update.",
|
|
359
|
+
);
|
|
360
|
+
return;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
span.setAttributes(createTraceAttributes(attributes));
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Gets the current active trace ID.
|
|
368
|
+
*
|
|
369
|
+
* @returns The trace ID of the currently active span, or undefined
|
|
370
|
+
*
|
|
371
|
+
* @public
|
|
372
|
+
*/
|
|
373
|
+
export function getActiveTraceId(): string | undefined {
|
|
374
|
+
return trace.getActiveSpan()?.spanContext().traceId;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Gets the current active observation ID.
|
|
379
|
+
*
|
|
380
|
+
* @returns The span ID of the currently active span, or undefined
|
|
381
|
+
*
|
|
382
|
+
* @public
|
|
383
|
+
*/
|
|
384
|
+
export function getActiveSpanId(): string | undefined {
|
|
385
|
+
return trace.getActiveSpan()?.spanContext().spanId;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// ============================================================================
|
|
389
|
+
// Active Observation Functions
|
|
390
|
+
// ============================================================================
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Options for startActiveObservation.
|
|
394
|
+
*
|
|
395
|
+
* @public
|
|
396
|
+
*/
|
|
397
|
+
export type StartActiveObservationOpts = StartObservationOpts & {
|
|
398
|
+
/** Whether to automatically end the observation when the function exits. Default: true */
|
|
399
|
+
endOnExit?: boolean;
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Wraps a Promise to automatically end the span when it resolves/rejects.
|
|
404
|
+
*
|
|
405
|
+
* @param promise - The promise to wrap
|
|
406
|
+
* @param span - The OpenTelemetry span
|
|
407
|
+
* @param endOnExit - Whether to end the span on exit
|
|
408
|
+
* @returns The wrapped promise
|
|
409
|
+
* @internal
|
|
410
|
+
*/
|
|
411
|
+
function wrapPromise<T>(
|
|
412
|
+
promise: Promise<T>,
|
|
413
|
+
span: Span,
|
|
414
|
+
endOnExit: boolean | undefined,
|
|
415
|
+
): Promise<T> {
|
|
416
|
+
return promise.then(
|
|
417
|
+
(value) => {
|
|
418
|
+
if (endOnExit !== false) {
|
|
419
|
+
span.end();
|
|
420
|
+
}
|
|
421
|
+
return value;
|
|
422
|
+
},
|
|
423
|
+
(err: unknown) => {
|
|
424
|
+
span.setStatus({
|
|
425
|
+
code: SpanStatusCode.ERROR,
|
|
426
|
+
message: err instanceof Error ? err.message : "Unknown error",
|
|
427
|
+
});
|
|
428
|
+
if (endOnExit !== false) {
|
|
429
|
+
span.end();
|
|
430
|
+
}
|
|
431
|
+
throw err;
|
|
432
|
+
},
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
// Function overloads for startActiveObservation
|
|
437
|
+
export function startActiveObservation<
|
|
438
|
+
F extends (observation: ObservationSpan) => unknown,
|
|
439
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
440
|
+
export function startActiveObservation<
|
|
441
|
+
F extends (observation: ObservationLLM) => unknown,
|
|
442
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
443
|
+
export function startActiveObservation<
|
|
444
|
+
F extends (observation: ObservationEmbedding) => unknown,
|
|
445
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
446
|
+
export function startActiveObservation<
|
|
447
|
+
F extends (observation: ObservationAgent) => unknown,
|
|
448
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
449
|
+
export function startActiveObservation<
|
|
450
|
+
F extends (observation: ObservationTool) => unknown,
|
|
451
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
452
|
+
export function startActiveObservation<
|
|
453
|
+
F extends (observation: ObservationChain) => unknown,
|
|
454
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
455
|
+
export function startActiveObservation<
|
|
456
|
+
F extends (observation: ObservationRetriever) => unknown,
|
|
457
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
458
|
+
export function startActiveObservation<
|
|
459
|
+
F extends (observation: ObservationReranker) => unknown,
|
|
460
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
461
|
+
export function startActiveObservation<
|
|
462
|
+
F extends (observation: ObservationEvaluator) => unknown,
|
|
463
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
464
|
+
export function startActiveObservation<
|
|
465
|
+
F extends (observation: ObservationGuardrail) => unknown,
|
|
466
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F>;
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Creates an observation with automatic lifecycle management.
|
|
470
|
+
*
|
|
471
|
+
* This function creates an observation and executes a function with that observation
|
|
472
|
+
* as a parameter. The observation is automatically ended when the function completes
|
|
473
|
+
* (unless `endOnExit` is set to `false`).
|
|
474
|
+
*
|
|
475
|
+
* Supports both synchronous and asynchronous functions, with automatic error handling.
|
|
476
|
+
*
|
|
477
|
+
* @param name - Descriptive name for the observation
|
|
478
|
+
* @param fn - Function to execute with the observation
|
|
479
|
+
* @param options - Configuration options
|
|
480
|
+
* @returns The result of the function
|
|
481
|
+
*
|
|
482
|
+
* @example
|
|
483
|
+
* ```typescript
|
|
484
|
+
* import { startActiveObservation } from './observability';
|
|
485
|
+
*
|
|
486
|
+
* // Synchronous function
|
|
487
|
+
* const result = startActiveObservation('data-processing', (span) => {
|
|
488
|
+
* span.update({ input: { data: [1, 2, 3] } });
|
|
489
|
+
* const processed = data.map(x => x * 2);
|
|
490
|
+
* span.update({ output: { result: processed } });
|
|
491
|
+
* return processed;
|
|
492
|
+
* }, { asType: 'span' });
|
|
493
|
+
*
|
|
494
|
+
* // Asynchronous function
|
|
495
|
+
* const embeddings = await startActiveObservation(
|
|
496
|
+
* 'text-embeddings',
|
|
497
|
+
* async (embedding) => {
|
|
498
|
+
* embedding.update({
|
|
499
|
+
* input: { texts: ['Hello', 'World'] },
|
|
500
|
+
* model: 'text-embedding-ada-002'
|
|
501
|
+
* });
|
|
502
|
+
*
|
|
503
|
+
* const vectors = await generateEmbeddings(texts);
|
|
504
|
+
*
|
|
505
|
+
* embedding.update({ output: { embeddings: vectors } });
|
|
506
|
+
* return vectors;
|
|
507
|
+
* },
|
|
508
|
+
* { asType: 'embedding' }
|
|
509
|
+
* );
|
|
510
|
+
*
|
|
511
|
+
* // Disable automatic ending (for long-running operations)
|
|
512
|
+
* startActiveObservation(
|
|
513
|
+
* 'background-task',
|
|
514
|
+
* (span) => {
|
|
515
|
+
* span.update({ input: { taskId: '123' } });
|
|
516
|
+
* startBackgroundProcess(span);
|
|
517
|
+
* return 'started';
|
|
518
|
+
* },
|
|
519
|
+
* { asType: 'span', endOnExit: false }
|
|
520
|
+
* );
|
|
521
|
+
* ```
|
|
522
|
+
*
|
|
523
|
+
* @see {@link startObservation} for manual observation lifecycle management
|
|
524
|
+
* @see {@link observe} for decorator-style function wrapping
|
|
525
|
+
*
|
|
526
|
+
* @public
|
|
527
|
+
*/
|
|
528
|
+
export function startActiveObservation<
|
|
529
|
+
F extends (observation: Observation) => unknown,
|
|
530
|
+
>(name: string, fn: F, options?: StartActiveObservationOpts): ReturnType<F> {
|
|
531
|
+
const { asType = "span", endOnExit, ...observationOptions } = options || {};
|
|
532
|
+
|
|
533
|
+
return getTracer().startActiveSpan(
|
|
534
|
+
name,
|
|
535
|
+
{ startTime: observationOptions?.startTime },
|
|
536
|
+
createParentContext(observationOptions?.parentSpanContext) ??
|
|
537
|
+
context.active(),
|
|
538
|
+
(span) => {
|
|
539
|
+
try {
|
|
540
|
+
let observation: Observation;
|
|
541
|
+
|
|
542
|
+
switch (asType) {
|
|
543
|
+
case "llm":
|
|
544
|
+
observation = new ObservationLLM({ otelSpan: span });
|
|
545
|
+
break;
|
|
546
|
+
case "embedding":
|
|
547
|
+
observation = new ObservationEmbedding({ otelSpan: span });
|
|
548
|
+
break;
|
|
549
|
+
case "agent":
|
|
550
|
+
observation = new ObservationAgent({ otelSpan: span });
|
|
551
|
+
break;
|
|
552
|
+
case "tool":
|
|
553
|
+
observation = new ObservationTool({ otelSpan: span });
|
|
554
|
+
break;
|
|
555
|
+
case "chain":
|
|
556
|
+
observation = new ObservationChain({ otelSpan: span });
|
|
557
|
+
break;
|
|
558
|
+
case "retriever":
|
|
559
|
+
observation = new ObservationRetriever({ otelSpan: span });
|
|
560
|
+
break;
|
|
561
|
+
case "reranker":
|
|
562
|
+
observation = new ObservationReranker({ otelSpan: span });
|
|
563
|
+
break;
|
|
564
|
+
case "evaluator":
|
|
565
|
+
observation = new ObservationEvaluator({ otelSpan: span });
|
|
566
|
+
break;
|
|
567
|
+
case "guardrail":
|
|
568
|
+
observation = new ObservationGuardrail({ otelSpan: span });
|
|
569
|
+
break;
|
|
570
|
+
case "span":
|
|
571
|
+
default:
|
|
572
|
+
observation = new ObservationSpan({ otelSpan: span });
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
const result = fn(observation as Parameters<F>[0]);
|
|
576
|
+
|
|
577
|
+
if (result instanceof Promise) {
|
|
578
|
+
return wrapPromise(
|
|
579
|
+
result,
|
|
580
|
+
span,
|
|
581
|
+
endOnExit,
|
|
582
|
+
) as ReturnType<F>;
|
|
583
|
+
} else {
|
|
584
|
+
if (endOnExit !== false) {
|
|
585
|
+
span.end();
|
|
586
|
+
}
|
|
587
|
+
return result as ReturnType<F>;
|
|
588
|
+
}
|
|
589
|
+
} catch (err) {
|
|
590
|
+
span.setStatus({
|
|
591
|
+
code: SpanStatusCode.ERROR,
|
|
592
|
+
message: err instanceof Error ? err.message : "Unknown error",
|
|
593
|
+
});
|
|
594
|
+
if (endOnExit !== false) {
|
|
595
|
+
span.end();
|
|
596
|
+
}
|
|
597
|
+
throw err;
|
|
598
|
+
}
|
|
599
|
+
},
|
|
600
|
+
);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
/**
|
|
604
|
+
* Updates the currently active observation with new attributes.
|
|
605
|
+
*
|
|
606
|
+
* @param attributes - Observation attributes to set
|
|
607
|
+
*
|
|
608
|
+
* @example
|
|
609
|
+
* ```typescript
|
|
610
|
+
* import { updateActiveObservation } from './observability';
|
|
611
|
+
*
|
|
612
|
+
* // Within an active observation context
|
|
613
|
+
* updateActiveObservation({
|
|
614
|
+
* metadata: { stage: 'processing' }
|
|
615
|
+
* });
|
|
616
|
+
* ```
|
|
617
|
+
*
|
|
618
|
+
* @public
|
|
619
|
+
*/
|
|
620
|
+
export function updateActiveObservation(
|
|
621
|
+
attributes: BaseSpanAttributes,
|
|
622
|
+
): void {
|
|
623
|
+
const span = trace.getActiveSpan();
|
|
624
|
+
|
|
625
|
+
if (!span) {
|
|
626
|
+
console.warn(
|
|
627
|
+
"[Observability] No active OTEL span in context. Skipping observation update.",
|
|
628
|
+
);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
span.setAttributes(createObservationAttributes("span", attributes));
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
// ============================================================================
|
|
636
|
+
// Decorator Function
|
|
637
|
+
// ============================================================================
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Options for the observe decorator.
|
|
641
|
+
*
|
|
642
|
+
* @public
|
|
643
|
+
*/
|
|
644
|
+
export type ObserveOptions = Omit<StartObservationOpts, "name"> & {
|
|
645
|
+
/** Whether to capture function arguments as input. Default: true */
|
|
646
|
+
captureInput?: boolean;
|
|
647
|
+
/** Whether to capture return value as output. Default: true */
|
|
648
|
+
captureOutput?: boolean;
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Captures function arguments for observability input.
|
|
653
|
+
*
|
|
654
|
+
* @param args - Function arguments to capture
|
|
655
|
+
* @returns Serialized arguments
|
|
656
|
+
* @internal
|
|
657
|
+
*/
|
|
658
|
+
function _captureArguments(args: unknown[]): Record<string, unknown> {
|
|
659
|
+
if (args.length === 0) return {};
|
|
660
|
+
if (args.length === 1) return { arg: args[0] };
|
|
661
|
+
return { args };
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
/**
|
|
665
|
+
* Decorator function to add observability to any function.
|
|
666
|
+
*
|
|
667
|
+
* Wraps a function with automatic observation creation, input/output capture,
|
|
668
|
+
* and lifecycle management. The observation is automatically ended when the
|
|
669
|
+
* function completes.
|
|
670
|
+
*
|
|
671
|
+
* @param fn - Function to wrap
|
|
672
|
+
* @param options - Configuration options
|
|
673
|
+
* @returns Wrapped function with observability
|
|
674
|
+
*
|
|
675
|
+
* @example
|
|
676
|
+
* ```typescript
|
|
677
|
+
* import { observe } from './observability';
|
|
678
|
+
*
|
|
679
|
+
* // Wrap an existing function
|
|
680
|
+
* const fetchData = observe(async (url: string) => {
|
|
681
|
+
* const response = await fetch(url);
|
|
682
|
+
* return response.json();
|
|
683
|
+
* }, { asType: 'tool' });
|
|
684
|
+
*
|
|
685
|
+
* // Wrap with custom name
|
|
686
|
+
* const processPayment = observe(
|
|
687
|
+
* async (amount: number, currency: string) => {
|
|
688
|
+
* return await paymentGateway.charge(amount, currency);
|
|
689
|
+
* },
|
|
690
|
+
* { name: 'payment-gateway-call', asType: 'tool' }
|
|
691
|
+
* );
|
|
692
|
+
*
|
|
693
|
+
* // Class method decoration
|
|
694
|
+
* class UserService {
|
|
695
|
+
* @observe({ asType: 'chain' })
|
|
696
|
+
* async getUser(id: string) {
|
|
697
|
+
* return await db.users.find(id);
|
|
698
|
+
* }
|
|
699
|
+
* }
|
|
700
|
+
* ```
|
|
701
|
+
*
|
|
702
|
+
* @public
|
|
703
|
+
*/
|
|
704
|
+
export function observe<T extends (...args: any[]) => any>(
|
|
705
|
+
fn: T,
|
|
706
|
+
options: ObserveOptions = {},
|
|
707
|
+
): T {
|
|
708
|
+
const {
|
|
709
|
+
asType = "span",
|
|
710
|
+
captureInput = true,
|
|
711
|
+
captureOutput = true,
|
|
712
|
+
...observationOptions
|
|
713
|
+
} = options;
|
|
714
|
+
|
|
715
|
+
const wrappedFunction = function (
|
|
716
|
+
this: any,
|
|
717
|
+
...args: Parameters<T>
|
|
718
|
+
): ReturnType<T> {
|
|
719
|
+
const name = fn.name || "anonymous-function";
|
|
720
|
+
|
|
721
|
+
// Prepare input data
|
|
722
|
+
const inputData = captureInput ? _captureArguments(args) : undefined;
|
|
723
|
+
|
|
724
|
+
// Create the observation
|
|
725
|
+
const observation = startObservation(
|
|
726
|
+
name,
|
|
727
|
+
inputData ? { input: inputData } : {},
|
|
728
|
+
{
|
|
729
|
+
...observationOptions,
|
|
730
|
+
asType: asType as "span",
|
|
731
|
+
},
|
|
732
|
+
);
|
|
733
|
+
|
|
734
|
+
// Set the observation span as active in the context
|
|
735
|
+
const activeContext = trace.setSpan(context.active(), observation.otelSpan);
|
|
736
|
+
|
|
737
|
+
// Execute the function within the observation context
|
|
738
|
+
const result = context.with(activeContext, () => fn.apply(this, args));
|
|
739
|
+
|
|
740
|
+
// Handle promises
|
|
741
|
+
if (result instanceof Promise) {
|
|
742
|
+
return result.then(
|
|
743
|
+
(value) => {
|
|
744
|
+
if (captureOutput) {
|
|
745
|
+
observation.update({ output: value });
|
|
746
|
+
}
|
|
747
|
+
observation.end();
|
|
748
|
+
return value;
|
|
749
|
+
},
|
|
750
|
+
(err: unknown) => {
|
|
751
|
+
observation.update({
|
|
752
|
+
level: "ERROR",
|
|
753
|
+
statusMessage: err instanceof Error ? err.message : "Unknown error",
|
|
754
|
+
});
|
|
755
|
+
observation.end();
|
|
756
|
+
throw err;
|
|
757
|
+
},
|
|
758
|
+
) as ReturnType<T>;
|
|
759
|
+
}
|
|
760
|
+
|
|
761
|
+
// Handle synchronous functions
|
|
762
|
+
if (captureOutput) {
|
|
763
|
+
observation.update({ output: result });
|
|
764
|
+
}
|
|
765
|
+
observation.end();
|
|
766
|
+
|
|
767
|
+
return result as ReturnType<T>;
|
|
768
|
+
};
|
|
769
|
+
|
|
770
|
+
// Preserve function properties
|
|
771
|
+
Object.defineProperty(wrappedFunction, "name", { value: fn.name });
|
|
772
|
+
Object.defineProperty(wrappedFunction, "length", { value: fn.length });
|
|
773
|
+
|
|
774
|
+
return wrappedFunction as T;
|
|
775
|
+
}
|