@cloudbase/agent-observability 1.0.1-alpha.16 → 1.0.1-alpha.18
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/dist/{chunk-ZGEMAYS4.mjs → chunk-MUTOOEDR.mjs} +159 -2
- package/dist/chunk-MUTOOEDR.mjs.map +1 -0
- package/dist/{chunk-NFEGQTCC.mjs → chunk-WMJKH4XE.mjs} +8 -1
- package/dist/{esm-PGEDANAI.mjs → esm-M4BB7YBD.mjs} +2 -2
- package/dist/index.d.mts +106 -6
- package/dist/index.d.ts +106 -6
- package/dist/index.js +157 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +2 -2
- package/dist/langchain.d.mts +3 -1
- package/dist/langchain.d.ts +3 -1
- package/dist/langchain.js +187 -6
- package/dist/langchain.js.map +1 -1
- package/dist/langchain.mjs +34 -8
- package/dist/langchain.mjs.map +1 -1
- package/dist/server.d.mts +69 -1
- package/dist/server.d.ts +69 -1
- package/dist/server.js +83 -4
- package/dist/server.js.map +1 -1
- package/dist/server.mjs +83 -6
- package/dist/server.mjs.map +1 -1
- package/package.json +2 -2
- package/src/core/attributes.ts +243 -2
- package/src/core/spanWrapper.ts +5 -5
- package/src/index.ts +12 -6
- package/src/langchain/CallbackHandler.ts +44 -6
- package/src/langchain/index.ts +1 -1
- package/src/server/SingleLineConsoleSpanExporter.ts +131 -0
- package/src/server/index.ts +8 -2
- package/src/server/setup.ts +19 -4
- package/src/types.ts +108 -5
- package/dist/chunk-ZGEMAYS4.mjs.map +0 -1
- /package/dist/{chunk-NFEGQTCC.mjs.map → chunk-WMJKH4XE.mjs.map} +0 -0
- /package/dist/{esm-PGEDANAI.mjs.map → esm-M4BB7YBD.mjs.map} +0 -0
package/dist/server.mjs
CHANGED
|
@@ -1,4 +1,78 @@
|
|
|
1
|
-
import "./chunk-
|
|
1
|
+
import "./chunk-WMJKH4XE.mjs";
|
|
2
|
+
|
|
3
|
+
// src/server/SingleLineConsoleSpanExporter.ts
|
|
4
|
+
var SingleLineConsoleSpanExporter = class {
|
|
5
|
+
/**
|
|
6
|
+
* Export spans as single-line JSON.
|
|
7
|
+
*/
|
|
8
|
+
export(spans, resultCallback) {
|
|
9
|
+
try {
|
|
10
|
+
for (const span of spans) {
|
|
11
|
+
const spanDict = this.spanToDict(span);
|
|
12
|
+
const jsonLine = JSON.stringify(spanDict);
|
|
13
|
+
console.log(jsonLine);
|
|
14
|
+
}
|
|
15
|
+
resultCallback({ code: 0 /* SUCCESS */ });
|
|
16
|
+
} catch (error) {
|
|
17
|
+
resultCallback({ code: 1 /* FAILED */, error });
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Shutdown the exporter.
|
|
22
|
+
*/
|
|
23
|
+
shutdown() {
|
|
24
|
+
return Promise.resolve();
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Force flush the exporter.
|
|
28
|
+
*/
|
|
29
|
+
forceFlush() {
|
|
30
|
+
return Promise.resolve();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Convert a ReadableSpan to a dictionary for JSON serialization.
|
|
34
|
+
*/
|
|
35
|
+
spanToDict(span) {
|
|
36
|
+
const context = span.spanContext();
|
|
37
|
+
const parentId = span.parentSpanId || span.parentSpanContext?.spanId;
|
|
38
|
+
return {
|
|
39
|
+
name: span.name,
|
|
40
|
+
context: {
|
|
41
|
+
trace_id: context.traceId,
|
|
42
|
+
span_id: context.spanId,
|
|
43
|
+
trace_flags: context.traceFlags
|
|
44
|
+
},
|
|
45
|
+
kind: span.kind,
|
|
46
|
+
parent_id: parentId,
|
|
47
|
+
start_time: span.startTime,
|
|
48
|
+
end_time: span.endTime,
|
|
49
|
+
status: {
|
|
50
|
+
status_code: span.status.code,
|
|
51
|
+
description: span.status.message
|
|
52
|
+
},
|
|
53
|
+
attributes: span.attributes,
|
|
54
|
+
events: span.events.map((event) => ({
|
|
55
|
+
name: event.name,
|
|
56
|
+
timestamp: event.time,
|
|
57
|
+
attributes: event.attributes
|
|
58
|
+
})),
|
|
59
|
+
links: span.links.map((link) => ({
|
|
60
|
+
context: {
|
|
61
|
+
trace_id: link.context.traceId,
|
|
62
|
+
span_id: link.context.spanId
|
|
63
|
+
},
|
|
64
|
+
attributes: link.attributes
|
|
65
|
+
})),
|
|
66
|
+
resource: {
|
|
67
|
+
attributes: span.resource.attributes
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
function isSingleLineConsoleExporterEnabled() {
|
|
73
|
+
const value = process.env.CONSOLE_EXPORTER_SINGLE_LINE?.toLowerCase() || "true";
|
|
74
|
+
return ["true", "1", "yes", "on"].includes(value);
|
|
75
|
+
}
|
|
2
76
|
|
|
3
77
|
// src/server/setup.ts
|
|
4
78
|
var TRUTHY_ENV_VALUES = /* @__PURE__ */ new Set(["true", "1", "yes", "on"]);
|
|
@@ -54,21 +128,22 @@ async function setupConsoleExporter(config) {
|
|
|
54
128
|
const { NodeTracerProvider } = await import("@opentelemetry/sdk-trace-node");
|
|
55
129
|
const { ConsoleSpanExporter, BatchSpanProcessor } = await import("@opentelemetry/sdk-trace-base");
|
|
56
130
|
const batchConfig = resolveBatchConfig(config.batch);
|
|
131
|
+
const useSingleLine = isSingleLineConsoleExporterEnabled();
|
|
132
|
+
const exporter = useSingleLine ? new SingleLineConsoleSpanExporter() : new ConsoleSpanExporter();
|
|
133
|
+
const exporterType = useSingleLine ? "single-line" : "multi-line";
|
|
57
134
|
let provider = trace.getTracerProvider();
|
|
58
135
|
const isRealProvider = "addSpanProcessor" in provider;
|
|
59
136
|
if (isRealProvider) {
|
|
60
|
-
const exporter = new ConsoleSpanExporter();
|
|
61
137
|
const processor = new BatchSpanProcessor(exporter, batchConfig);
|
|
62
138
|
provider.addSpanProcessor(processor);
|
|
63
139
|
console.info(
|
|
64
|
-
`[Observability] Console exporter configured (batch=${batchConfig.maxExportBatchSize}, delay=${batchConfig.scheduledDelayMillis}ms)`
|
|
140
|
+
`[Observability] Console exporter configured (${exporterType}, batch=${batchConfig.maxExportBatchSize}, delay=${batchConfig.scheduledDelayMillis}ms)`
|
|
65
141
|
);
|
|
66
142
|
} else {
|
|
67
143
|
const resource = resourceFromAttributes({
|
|
68
144
|
"service.name": process.env.OTEL_SERVICE_NAME || "ag-ui-server",
|
|
69
145
|
"service.version": "1.0.0"
|
|
70
146
|
});
|
|
71
|
-
const exporter = new ConsoleSpanExporter();
|
|
72
147
|
const processor = new BatchSpanProcessor(exporter, batchConfig);
|
|
73
148
|
const tracerProvider = new NodeTracerProvider({
|
|
74
149
|
resource,
|
|
@@ -76,7 +151,7 @@ async function setupConsoleExporter(config) {
|
|
|
76
151
|
});
|
|
77
152
|
tracerProvider.register();
|
|
78
153
|
console.info(
|
|
79
|
-
`[Observability] Console exporter configured (batch=${batchConfig.maxExportBatchSize}, delay=${batchConfig.scheduledDelayMillis}ms)`
|
|
154
|
+
`[Observability] Console exporter configured (${exporterType}, batch=${batchConfig.maxExportBatchSize}, delay=${batchConfig.scheduledDelayMillis}ms)`
|
|
80
155
|
);
|
|
81
156
|
}
|
|
82
157
|
}
|
|
@@ -84,7 +159,7 @@ async function setupOTLPExporter(config) {
|
|
|
84
159
|
const { trace } = await import("@opentelemetry/api");
|
|
85
160
|
const { resourceFromAttributes } = await import("@opentelemetry/resources");
|
|
86
161
|
const { NodeTracerProvider } = await import("@opentelemetry/sdk-trace-node");
|
|
87
|
-
const { OTLPTraceExporter } = await import("./esm-
|
|
162
|
+
const { OTLPTraceExporter } = await import("./esm-M4BB7YBD.mjs");
|
|
88
163
|
const { BatchSpanProcessor } = await import("@opentelemetry/sdk-trace-base");
|
|
89
164
|
const batchConfig = resolveBatchConfig(config.batch);
|
|
90
165
|
let provider = trace.getTracerProvider();
|
|
@@ -172,6 +247,8 @@ var ExporterType = {
|
|
|
172
247
|
};
|
|
173
248
|
export {
|
|
174
249
|
ExporterType,
|
|
250
|
+
SingleLineConsoleSpanExporter,
|
|
251
|
+
isSingleLineConsoleExporterEnabled,
|
|
175
252
|
setupObservability
|
|
176
253
|
};
|
|
177
254
|
//# sourceMappingURL=server.mjs.map
|
package/dist/server.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/server/setup.ts","../src/server/config.ts"],"sourcesContent":["/**\n * Observability setup implementation for AG-Kit Server.\n *\n * Merges configuration from environment variables and parameters,\n * then applies each exporter configuration.\n *\n * @packageDocumentation\n */\n\nimport type {\n BatchConfig,\n ObservabilityConfig,\n ConsoleTraceConfig,\n OTLPTraceConfig,\n CustomTraceConfig,\n} from './config';\n\nexport type {\n BatchConfig,\n ObservabilityConfig,\n ConsoleTraceConfig,\n OTLPTraceConfig,\n CustomTraceConfig,\n} from './config';\n\n/**\n * Environment variable truthy values.\n * Matches Python SDK implementation for consistency.\n */\nconst TRUTHY_ENV_VALUES = new Set(['true', '1', 'yes', 'on']);\n\n/**\n * Merged configuration result.\n * Console config allows merge (param overrides env),\n * while OTLP and custom configs are arrays (additive).\n */\ninterface MergedConfig {\n console?: ConsoleTraceConfig | null;\n otlp: OTLPTraceConfig[];\n custom: CustomTraceConfig[];\n}\n\n/**\n * Default batch configuration.\n */\nconst DEFAULT_BATCH_CONFIG: Required<BatchConfig> = {\n maxExportBatchSize: 100,\n scheduledDelayMillis: 5000,\n maxQueueSize: 2048,\n exportTimeoutMillis: 30000,\n};\n\n/**\n * Merge environment variable and parameter configurations.\n *\n * - AUTO_TRACES_STDOUT env adds a console config\n * - Parameter configs override/extend env configs\n */\nfunction mergeConfigs(paramConfigs: ObservabilityConfig[]): MergedConfig {\n const result: MergedConfig = {\n console: null,\n otlp: [],\n custom: [],\n };\n\n // 1. Check AUTO_TRACES_STDOUT env\n const autoTracesStdout = process.env.AUTO_TRACES_STDOUT?.toLowerCase() || '';\n if (TRUTHY_ENV_VALUES.has(autoTracesStdout)) {\n result.console = { type: 'console' };\n console.debug(\n `[Observability] AUTO_TRACES_STDOUT=${autoTracesStdout}, console exporter enabled`\n );\n }\n\n // 2. Process parameter configs\n for (const config of paramConfigs) {\n switch (config.type) {\n case 'console':\n // Parameter overrides env (merge batch config)\n result.console = { ...result.console, ...config };\n break;\n\n case 'otlp':\n result.otlp.push(config);\n break;\n\n case 'custom':\n result.custom.push(config);\n break;\n }\n }\n\n return result;\n}\n\n/**\n * Apply batch configuration with defaults.\n */\nfunction resolveBatchConfig(batch?: BatchConfig): Required<BatchConfig> {\n return { ...DEFAULT_BATCH_CONFIG, ...batch };\n}\n\n/**\n * Safe wrapper for exporter setup functions.\n * Ensures individual exporter failures don't crash the entire setup.\n */\nasync function safeSetup(\n name: string,\n setupFn: () => Promise<void>\n): Promise<void> {\n try {\n await setupFn();\n } catch (error) {\n console.warn(\n `[Observability] ${name} setup failed (non-fatal): ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n // Don't rethrow - allow other exporters to continue\n }\n}\n\n/**\n * Setup console exporter.\n */\nasync function setupConsoleExporter(config: ConsoleTraceConfig): Promise<void> {\n const { trace } = await import('@opentelemetry/api');\n const { resourceFromAttributes } = await import('@opentelemetry/resources');\n const { NodeTracerProvider } = await import('@opentelemetry/sdk-trace-node');\n const { ConsoleSpanExporter, BatchSpanProcessor } = await import(\n '@opentelemetry/sdk-trace-base'\n );\n\n const batchConfig = resolveBatchConfig(config.batch);\n\n // Check if a real TracerProvider already exists\n let provider = trace.getTracerProvider();\n const isRealProvider = 'addSpanProcessor' in provider;\n\n if (isRealProvider) {\n // Add processor to existing provider\n const exporter = new ConsoleSpanExporter();\n const processor = new BatchSpanProcessor(exporter, batchConfig);\n (provider as any).addSpanProcessor(processor);\n\n console.info(\n `[Observability] Console exporter configured (batch=${batchConfig.maxExportBatchSize}, ` +\n `delay=${batchConfig.scheduledDelayMillis}ms)`\n );\n } else {\n // Create new provider with console exporter\n const resource = resourceFromAttributes({\n 'service.name': process.env.OTEL_SERVICE_NAME || 'ag-ui-server',\n 'service.version': '1.0.0',\n });\n\n const exporter = new ConsoleSpanExporter();\n const processor = new BatchSpanProcessor(exporter, batchConfig);\n\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n tracerProvider.register();\n\n console.info(\n `[Observability] Console exporter configured (batch=${batchConfig.maxExportBatchSize}, ` +\n `delay=${batchConfig.scheduledDelayMillis}ms)`\n );\n }\n}\n\n/**\n * Setup OTLP exporter.\n */\nasync function setupOTLPExporter(config: OTLPTraceConfig): Promise<void> {\n const { trace } = await import('@opentelemetry/api');\n const { resourceFromAttributes } = await import('@opentelemetry/resources');\n const { NodeTracerProvider } = await import('@opentelemetry/sdk-trace-node');\n const { OTLPTraceExporter } = await import('@opentelemetry/exporter-trace-otlp-http');\n const { BatchSpanProcessor } = await import('@opentelemetry/sdk-trace-base');\n\n const batchConfig = resolveBatchConfig(config.batch);\n\n // Check if a real TracerProvider already exists\n let provider = trace.getTracerProvider();\n const isRealProvider = 'addSpanProcessor' in provider;\n\n if (isRealProvider) {\n // Add processor to existing provider\n const exporter = new OTLPTraceExporter({\n url: config.url,\n headers: config.headers,\n timeoutMillis: config.timeout ?? 10000,\n });\n\n const processor = new BatchSpanProcessor(exporter, batchConfig);\n (provider as any).addSpanProcessor(processor);\n\n console.info(\n `[Observability] OTLP exporter configured (url=${config.url}, ` +\n `batch=${batchConfig.maxExportBatchSize}, delay=${batchConfig.scheduledDelayMillis}ms)`\n );\n } else {\n // Create new provider with OTLP exporter\n const resource = resourceFromAttributes({\n 'service.name': process.env.OTEL_SERVICE_NAME || 'ag-ui-server',\n 'service.version': '1.0.0',\n });\n\n const exporter = new OTLPTraceExporter({\n url: config.url,\n headers: config.headers,\n timeoutMillis: config.timeout ?? 10000,\n });\n\n const processor = new BatchSpanProcessor(exporter, batchConfig);\n\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n tracerProvider.register();\n\n console.info(\n `[Observability] OTLP exporter configured (url=${config.url}, ` +\n `batch=${batchConfig.maxExportBatchSize}, delay=${batchConfig.scheduledDelayMillis}ms)`\n );\n }\n}\n\n/**\n * Setup custom exporter.\n */\nasync function setupCustomExporter(config: CustomTraceConfig): Promise<void> {\n await config.setup();\n console.info(`[Observability] Custom exporter setup completed`);\n}\n\n/**\n * Setup observability from merged configuration.\n *\n * @internal\n */\nasync function applyMergedConfigs(merged: MergedConfig): Promise<void> {\n const setupTasks: Promise<void>[] = [];\n\n // Apply console (non-blocking)\n if (merged.console) {\n setupTasks.push(safeSetup('Console exporter', () => setupConsoleExporter(merged.console!)));\n }\n\n // Apply otlp (non-blocking)\n for (const otlp of merged.otlp) {\n setupTasks.push(safeSetup(`OTLP exporter (${otlp.url})`, () => setupOTLPExporter(otlp)));\n }\n\n // Apply custom (non-blocking)\n for (const custom of merged.custom) {\n setupTasks.push(safeSetup('Custom exporter', () => setupCustomExporter(custom)));\n }\n\n // Wait for all exporters to complete (or fail gracefully)\n await Promise.all(setupTasks);\n\n if (merged.console || merged.otlp.length > 0 || merged.custom.length > 0) {\n console.info(`[Observability] Setup completed`);\n }\n}\n\n/**\n * Setup observability from configuration.\n *\n * Merges environment variable (AUTO_TRACES_STDOUT) with parameter configs,\n * then applies each exporter configuration.\n *\n * Environment variables act as presets, parameter configs override or extend.\n *\n * Returns a promise that resolves when setup is complete. This allows callers\n * to await initialization before proceeding, eliminating race conditions.\n *\n * The returned promise is cached - subsequent calls return the same promise\n * to avoid duplicate initialization.\n *\n * @param configs - Observability configuration(s)\n *\n * @example\n * ```typescript\n * // Console only (from env)\n * await setupObservability();\n *\n * // Console + OTLP\n * await setupObservability([\n * { type: 'console' },\n * { type: 'otlp', url: 'http://localhost:4318/v1/traces' }\n * ]);\n *\n * // OTLP only\n * await setupObservability({\n * type: 'otlp',\n * url: 'https://cloud.langfuse.com/api/public/otlp/v1/traces',\n * headers: { 'Authorization': 'Basic xxx' }\n * });\n * ```\n *\n * @public\n */\n\nlet setupPromise: Promise<void> | null = null;\n\nexport async function setupObservability(\n configs?: ObservabilityConfig | ObservabilityConfig[]\n): Promise<void> {\n // Return cached promise if setup is in progress or completed\n if (setupPromise) {\n return setupPromise;\n }\n\n // Create the setup promise\n setupPromise = (async () => {\n try {\n // Normalize to array\n const configsArray = configs\n ? Array.isArray(configs)\n ? configs\n : [configs]\n : [];\n\n // Merge env and parameter configs\n const merged = mergeConfigs(configsArray);\n\n // Apply merged configs\n await applyMergedConfigs(merged);\n } catch (error) {\n // Reset promise on error to allow retry\n setupPromise = null;\n // Silent failure - observability should never block main flow\n console.warn(\n `[Observability] Setup failed: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n })();\n\n return setupPromise;\n}\n","/**\n * Observability configuration types for AG-Kit Server.\n *\n * Provides a unified configuration interface for trace exporters:\n * - Console: Development/debugging output\n * - OTLP: Production export to Langfuse, Jaeger, etc.\n * - Custom: User-defined setup logic\n *\n * @packageDocumentation\n */\n\n/**\n * Trace exporter type constants.\n *\n * @example\n * ```typescript\n * import { ExporterType } from '@cloudbase/agent-observability/server';\n *\n * { type: ExporterType.Console }\n * { type: ExporterType.OTLP }\n * { type: ExporterType.Custom }\n * ```\n *\n * @public\n */\nexport const ExporterType = {\n /** Console exporter - outputs traces to stdout */\n Console: 'console',\n /** OTLP exporter - sends traces to OTLP-compatible backend */\n OTLP: 'otlp',\n /** Custom exporter - user-defined setup logic */\n Custom: 'custom',\n} as const;\n\n/**\n * Trace exporter type literal values.\n *\n * @public\n */\nexport type ExporterType = typeof ExporterType[keyof typeof ExporterType];\n\n/**\n * Batch processing configuration for span exporters.\n *\n * Used by BatchSpanProcessor to optimize performance:\n * - Collects spans in memory and exports them in batches\n * - Reduces I/O operations (console) or network requests (OTLP)\n * - Recommended for production environments\n *\n * @public\n */\nexport interface BatchConfig {\n /** Maximum number of spans per export batch (default: 100) */\n maxExportBatchSize?: number;\n /** Maximum delay in milliseconds before exporting (default: 5000) */\n scheduledDelayMillis?: number;\n /** Maximum queue size (default: 2048) */\n maxQueueSize?: number;\n /** Export timeout in milliseconds (default: 30000) */\n exportTimeoutMillis?: number;\n}\n\n/**\n * Console trace exporter configuration.\n *\n * Outputs traces to stdout in JSON format using ConsoleSpanExporter.\n * Useful for development and debugging.\n *\n * @example\n * ```typescript\n * import { ExporterType } from '@cloudbase/agent-observability/server';\n *\n * { type: ExporterType.Console }\n * { type: ExporterType.Console, batch: { maxExportBatchSize: 200 } }\n * ```\n *\n * @public\n */\nexport interface ConsoleTraceConfig {\n /** Discriminator for console exporter */\n type: typeof ExporterType.Console;\n /** Optional batch processing configuration */\n batch?: BatchConfig;\n}\n\n/**\n * OTLP trace exporter configuration.\n *\n * Exports traces via OTLP protocol to any compatible backend:\n * - Langfuse: https://cloud.langfuse.com/api/public/otlp/v1/traces\n * - Jaeger: http://localhost:4318/v1/traces\n * - OTel Collector: custom endpoint\n *\n * @example\n * ```typescript\n * import { ExporterType } from '@cloudbase/agent-observability/server';\n *\n * {\n * type: ExporterType.OTLP,\n * url: 'https://cloud.langfuse.com/api/public/otlp/v1/traces',\n * headers: {\n * 'Authorization': 'Basic ' + btoa('pk-lf-xxx:sk-lf-xxx')\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface OTLPTraceConfig {\n /** Discriminator for OTLP exporter */\n type: typeof ExporterType.OTLP;\n /** OTLP endpoint URL (http/https) */\n url: string;\n /** Optional HTTP headers for authentication */\n headers?: Record<string, string>;\n /** Request timeout in milliseconds (default: 10000) */\n timeout?: number;\n /** Optional batch processing configuration */\n batch?: BatchConfig;\n}\n\n/**\n * Custom trace exporter configuration.\n *\n * Allows users to provide custom trace setup logic.\n * Useful for integrations not covered by console/otlp.\n *\n * @example\n * ```typescript\n * import { ExporterType } from '@cloudbase/agent-observability/server';\n *\n * {\n * type: ExporterType.Custom,\n * setup: async () => {\n * const exporter = new MyCustomExporter();\n * const provider = new BasicTracerProvider();\n * provider.addSpanProcessor(new SimpleSpanProcessor(exporter));\n * provider.register();\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface CustomTraceConfig {\n /** Discriminator for custom setup */\n type: typeof ExporterType.Custom;\n /** User-defined setup function */\n setup: () => Promise<void> | void;\n}\n\n/**\n * Union type of all supported trace exporter configurations.\n *\n * @public\n */\nexport type ObservabilityConfig =\n | ConsoleTraceConfig\n | OTLPTraceConfig\n | CustomTraceConfig;\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/server/SingleLineConsoleSpanExporter.ts","../src/server/setup.ts","../src/server/config.ts"],"sourcesContent":["/**\n * Custom console exporter that outputs single-line JSON.\n *\n * This exporter outputs spans as single-line JSON for easier parsing\n * with line-based tools (grep, jq, etc.).\n *\n * To switch back to standard multi-line output, use ConsoleSpanExporter instead.\n *\n * @example\n * ```typescript\n * // Single-line (current default)\n * const exporter = new SingleLineConsoleSpanExporter();\n *\n * // Multi-line (standard OTel)\n * import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-base';\n * const exporter = new ConsoleSpanExporter();\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';\n\n/**\n * Export result codes.\n */\nexport enum ExportResultCode {\n SUCCESS = 0,\n FAILED = 1,\n}\n\n/**\n * Export result interface.\n */\nexport interface ExportResult {\n code: ExportResultCode;\n error?: Error;\n}\n\n/**\n * Custom console exporter that outputs single-line JSON.\n *\n * This exporter outputs spans as single-line JSON for easier parsing\n * with line-based tools (grep, jq, etc.).\n */\nexport class SingleLineConsoleSpanExporter implements SpanExporter {\n /**\n * Export spans as single-line JSON.\n */\n export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {\n try {\n for (const span of spans) {\n const spanDict = this.spanToDict(span);\n // Single-line JSON output\n const jsonLine = JSON.stringify(spanDict);\n console.log(jsonLine);\n }\n resultCallback({ code: ExportResultCode.SUCCESS });\n } catch (error) {\n resultCallback({ code: ExportResultCode.FAILED, error: error as Error });\n }\n }\n\n /**\n * Shutdown the exporter.\n */\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Force flush the exporter.\n */\n forceFlush?(): Promise<void> {\n return Promise.resolve();\n }\n\n /**\n * Convert a ReadableSpan to a dictionary for JSON serialization.\n */\n private spanToDict(span: ReadableSpan): Record<string, unknown> {\n const context = span.spanContext();\n\n // Get parent span ID from parentSpanContext if available\n // Some versions use parentSpanId directly, others use parentSpanContext.spanId\n const parentId = (span as any).parentSpanId || span.parentSpanContext?.spanId;\n\n return {\n name: span.name,\n context: {\n trace_id: context.traceId,\n span_id: context.spanId,\n trace_flags: context.traceFlags,\n },\n kind: span.kind,\n parent_id: parentId,\n start_time: span.startTime,\n end_time: span.endTime,\n status: {\n status_code: span.status.code,\n description: span.status.message,\n },\n attributes: span.attributes,\n events: span.events.map((event) => ({\n name: event.name,\n timestamp: event.time,\n attributes: event.attributes,\n })),\n links: span.links.map((link) => ({\n context: {\n trace_id: link.context.traceId,\n span_id: link.context.spanId,\n },\n attributes: link.attributes,\n })),\n resource: {\n attributes: span.resource.attributes,\n },\n };\n }\n}\n\n/**\n * Check if single-line console exporter should be used.\n *\n * Set CONSOLE_EXPORTER_SINGLE_LINE=false to use standard multi-line output.\n */\nexport function isSingleLineConsoleExporterEnabled(): boolean {\n const value = process.env.CONSOLE_EXPORTER_SINGLE_LINE?.toLowerCase() || 'true';\n return ['true', '1', 'yes', 'on'].includes(value);\n}\n","/**\n * Observability setup implementation for AG-Kit Server.\n *\n * Merges configuration from environment variables and parameters,\n * then applies each exporter configuration.\n *\n * @packageDocumentation\n */\n\nimport type {\n BatchConfig,\n ObservabilityConfig,\n ConsoleTraceConfig,\n OTLPTraceConfig,\n CustomTraceConfig,\n} from './config';\nimport {\n SingleLineConsoleSpanExporter,\n isSingleLineConsoleExporterEnabled,\n} from './SingleLineConsoleSpanExporter';\n\nexport type {\n BatchConfig,\n ObservabilityConfig,\n ConsoleTraceConfig,\n OTLPTraceConfig,\n CustomTraceConfig,\n} from './config';\nexport { SingleLineConsoleSpanExporter } from './SingleLineConsoleSpanExporter';\n\n/**\n * Environment variable truthy values.\n * Matches Python SDK implementation for consistency.\n */\nconst TRUTHY_ENV_VALUES = new Set(['true', '1', 'yes', 'on']);\n\n/**\n * Merged configuration result.\n * Console config allows merge (param overrides env),\n * while OTLP and custom configs are arrays (additive).\n */\ninterface MergedConfig {\n console?: ConsoleTraceConfig | null;\n otlp: OTLPTraceConfig[];\n custom: CustomTraceConfig[];\n}\n\n/**\n * Default batch configuration.\n */\nconst DEFAULT_BATCH_CONFIG: Required<BatchConfig> = {\n maxExportBatchSize: 100,\n scheduledDelayMillis: 5000,\n maxQueueSize: 2048,\n exportTimeoutMillis: 30000,\n};\n\n/**\n * Merge environment variable and parameter configurations.\n *\n * - AUTO_TRACES_STDOUT env adds a console config\n * - Parameter configs override/extend env configs\n */\nfunction mergeConfigs(paramConfigs: ObservabilityConfig[]): MergedConfig {\n const result: MergedConfig = {\n console: null,\n otlp: [],\n custom: [],\n };\n\n // 1. Check AUTO_TRACES_STDOUT env\n const autoTracesStdout = process.env.AUTO_TRACES_STDOUT?.toLowerCase() || '';\n if (TRUTHY_ENV_VALUES.has(autoTracesStdout)) {\n result.console = { type: 'console' };\n console.debug(\n `[Observability] AUTO_TRACES_STDOUT=${autoTracesStdout}, console exporter enabled`\n );\n }\n\n // 2. Process parameter configs\n for (const config of paramConfigs) {\n switch (config.type) {\n case 'console':\n // Parameter overrides env (merge batch config)\n result.console = { ...result.console, ...config };\n break;\n\n case 'otlp':\n result.otlp.push(config);\n break;\n\n case 'custom':\n result.custom.push(config);\n break;\n }\n }\n\n return result;\n}\n\n/**\n * Apply batch configuration with defaults.\n */\nfunction resolveBatchConfig(batch?: BatchConfig): Required<BatchConfig> {\n return { ...DEFAULT_BATCH_CONFIG, ...batch };\n}\n\n/**\n * Safe wrapper for exporter setup functions.\n * Ensures individual exporter failures don't crash the entire setup.\n */\nasync function safeSetup(\n name: string,\n setupFn: () => Promise<void>\n): Promise<void> {\n try {\n await setupFn();\n } catch (error) {\n console.warn(\n `[Observability] ${name} setup failed (non-fatal): ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n // Don't rethrow - allow other exporters to continue\n }\n}\n\n/**\n * Setup console exporter.\n *\n * Uses SingleLineConsoleSpanExporter by default for easier parsing with line-based tools.\n * Set CONSOLE_EXPORTER_SINGLE_LINE=false to use standard multi-line output.\n */\nasync function setupConsoleExporter(config: ConsoleTraceConfig): Promise<void> {\n const { trace } = await import('@opentelemetry/api');\n const { resourceFromAttributes } = await import('@opentelemetry/resources');\n const { NodeTracerProvider } = await import('@opentelemetry/sdk-trace-node');\n const { ConsoleSpanExporter, BatchSpanProcessor } = await import(\n '@opentelemetry/sdk-trace-base'\n );\n\n const batchConfig = resolveBatchConfig(config.batch);\n\n // Choose exporter type based on CONSOLE_EXPORTER_SINGLE_LINE env var\n // Single-line: easier for parsing with line-based tools (grep, jq, etc.)\n // Multi-line: more human-readable for debugging\n const useSingleLine = isSingleLineConsoleExporterEnabled();\n const exporter = useSingleLine\n ? new SingleLineConsoleSpanExporter()\n : new ConsoleSpanExporter();\n const exporterType = useSingleLine ? 'single-line' : 'multi-line';\n\n // Check if a real TracerProvider already exists\n let provider = trace.getTracerProvider();\n const isRealProvider = 'addSpanProcessor' in provider;\n\n if (isRealProvider) {\n // Add processor to existing provider\n const processor = new BatchSpanProcessor(exporter, batchConfig);\n (provider as any).addSpanProcessor(processor);\n\n console.info(\n `[Observability] Console exporter configured (${exporterType}, batch=${batchConfig.maxExportBatchSize}, ` +\n `delay=${batchConfig.scheduledDelayMillis}ms)`\n );\n } else {\n // Create new provider with console exporter\n const resource = resourceFromAttributes({\n 'service.name': process.env.OTEL_SERVICE_NAME || 'ag-ui-server',\n 'service.version': '1.0.0',\n });\n\n const processor = new BatchSpanProcessor(exporter, batchConfig);\n\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n tracerProvider.register();\n\n console.info(\n `[Observability] Console exporter configured (${exporterType}, batch=${batchConfig.maxExportBatchSize}, ` +\n `delay=${batchConfig.scheduledDelayMillis}ms)`\n );\n }\n}\n\n/**\n * Setup OTLP exporter.\n */\nasync function setupOTLPExporter(config: OTLPTraceConfig): Promise<void> {\n const { trace } = await import('@opentelemetry/api');\n const { resourceFromAttributes } = await import('@opentelemetry/resources');\n const { NodeTracerProvider } = await import('@opentelemetry/sdk-trace-node');\n const { OTLPTraceExporter } = await import('@opentelemetry/exporter-trace-otlp-http');\n const { BatchSpanProcessor } = await import('@opentelemetry/sdk-trace-base');\n\n const batchConfig = resolveBatchConfig(config.batch);\n\n // Check if a real TracerProvider already exists\n let provider = trace.getTracerProvider();\n const isRealProvider = 'addSpanProcessor' in provider;\n\n if (isRealProvider) {\n // Add processor to existing provider\n const exporter = new OTLPTraceExporter({\n url: config.url,\n headers: config.headers,\n timeoutMillis: config.timeout ?? 10000,\n });\n\n const processor = new BatchSpanProcessor(exporter, batchConfig);\n (provider as any).addSpanProcessor(processor);\n\n console.info(\n `[Observability] OTLP exporter configured (url=${config.url}, ` +\n `batch=${batchConfig.maxExportBatchSize}, delay=${batchConfig.scheduledDelayMillis}ms)`\n );\n } else {\n // Create new provider with OTLP exporter\n const resource = resourceFromAttributes({\n 'service.name': process.env.OTEL_SERVICE_NAME || 'ag-ui-server',\n 'service.version': '1.0.0',\n });\n\n const exporter = new OTLPTraceExporter({\n url: config.url,\n headers: config.headers,\n timeoutMillis: config.timeout ?? 10000,\n });\n\n const processor = new BatchSpanProcessor(exporter, batchConfig);\n\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [processor],\n });\n\n tracerProvider.register();\n\n console.info(\n `[Observability] OTLP exporter configured (url=${config.url}, ` +\n `batch=${batchConfig.maxExportBatchSize}, delay=${batchConfig.scheduledDelayMillis}ms)`\n );\n }\n}\n\n/**\n * Setup custom exporter.\n */\nasync function setupCustomExporter(config: CustomTraceConfig): Promise<void> {\n await config.setup();\n console.info(`[Observability] Custom exporter setup completed`);\n}\n\n/**\n * Setup observability from merged configuration.\n *\n * @internal\n */\nasync function applyMergedConfigs(merged: MergedConfig): Promise<void> {\n const setupTasks: Promise<void>[] = [];\n\n // Apply console (non-blocking)\n if (merged.console) {\n setupTasks.push(safeSetup('Console exporter', () => setupConsoleExporter(merged.console!)));\n }\n\n // Apply otlp (non-blocking)\n for (const otlp of merged.otlp) {\n setupTasks.push(safeSetup(`OTLP exporter (${otlp.url})`, () => setupOTLPExporter(otlp)));\n }\n\n // Apply custom (non-blocking)\n for (const custom of merged.custom) {\n setupTasks.push(safeSetup('Custom exporter', () => setupCustomExporter(custom)));\n }\n\n // Wait for all exporters to complete (or fail gracefully)\n await Promise.all(setupTasks);\n\n if (merged.console || merged.otlp.length > 0 || merged.custom.length > 0) {\n console.info(`[Observability] Setup completed`);\n }\n}\n\n/**\n * Setup observability from configuration.\n *\n * Merges environment variable (AUTO_TRACES_STDOUT) with parameter configs,\n * then applies each exporter configuration.\n *\n * Environment variables act as presets, parameter configs override or extend.\n *\n * Returns a promise that resolves when setup is complete. This allows callers\n * to await initialization before proceeding, eliminating race conditions.\n *\n * The returned promise is cached - subsequent calls return the same promise\n * to avoid duplicate initialization.\n *\n * @param configs - Observability configuration(s)\n *\n * @example\n * ```typescript\n * // Console only (from env)\n * await setupObservability();\n *\n * // Console + OTLP\n * await setupObservability([\n * { type: 'console' },\n * { type: 'otlp', url: 'http://localhost:4318/v1/traces' }\n * ]);\n *\n * // OTLP only\n * await setupObservability({\n * type: 'otlp',\n * url: 'https://cloud.langfuse.com/api/public/otlp/v1/traces',\n * headers: { 'Authorization': 'Basic xxx' }\n * });\n * ```\n *\n * @public\n */\n\nlet setupPromise: Promise<void> | null = null;\n\nexport async function setupObservability(\n configs?: ObservabilityConfig | ObservabilityConfig[]\n): Promise<void> {\n // Return cached promise if setup is in progress or completed\n if (setupPromise) {\n return setupPromise;\n }\n\n // Create the setup promise\n setupPromise = (async () => {\n try {\n // Normalize to array\n const configsArray = configs\n ? Array.isArray(configs)\n ? configs\n : [configs]\n : [];\n\n // Merge env and parameter configs\n const merged = mergeConfigs(configsArray);\n\n // Apply merged configs\n await applyMergedConfigs(merged);\n } catch (error) {\n // Reset promise on error to allow retry\n setupPromise = null;\n // Silent failure - observability should never block main flow\n console.warn(\n `[Observability] Setup failed: ${\n error instanceof Error ? error.message : String(error)\n }`\n );\n }\n })();\n\n return setupPromise;\n}\n","/**\n * Observability configuration types for AG-Kit Server.\n *\n * Provides a unified configuration interface for trace exporters:\n * - Console: Development/debugging output\n * - OTLP: Production export to Langfuse, Jaeger, etc.\n * - Custom: User-defined setup logic\n *\n * @packageDocumentation\n */\n\n/**\n * Trace exporter type constants.\n *\n * @example\n * ```typescript\n * import { ExporterType } from '@cloudbase/agent-observability/server';\n *\n * { type: ExporterType.Console }\n * { type: ExporterType.OTLP }\n * { type: ExporterType.Custom }\n * ```\n *\n * @public\n */\nexport const ExporterType = {\n /** Console exporter - outputs traces to stdout */\n Console: 'console',\n /** OTLP exporter - sends traces to OTLP-compatible backend */\n OTLP: 'otlp',\n /** Custom exporter - user-defined setup logic */\n Custom: 'custom',\n} as const;\n\n/**\n * Trace exporter type literal values.\n *\n * @public\n */\nexport type ExporterType = typeof ExporterType[keyof typeof ExporterType];\n\n/**\n * Batch processing configuration for span exporters.\n *\n * Used by BatchSpanProcessor to optimize performance:\n * - Collects spans in memory and exports them in batches\n * - Reduces I/O operations (console) or network requests (OTLP)\n * - Recommended for production environments\n *\n * @public\n */\nexport interface BatchConfig {\n /** Maximum number of spans per export batch (default: 100) */\n maxExportBatchSize?: number;\n /** Maximum delay in milliseconds before exporting (default: 5000) */\n scheduledDelayMillis?: number;\n /** Maximum queue size (default: 2048) */\n maxQueueSize?: number;\n /** Export timeout in milliseconds (default: 30000) */\n exportTimeoutMillis?: number;\n}\n\n/**\n * Console trace exporter configuration.\n *\n * Outputs traces to stdout in JSON format using ConsoleSpanExporter.\n * Useful for development and debugging.\n *\n * @example\n * ```typescript\n * import { ExporterType } from '@cloudbase/agent-observability/server';\n *\n * { type: ExporterType.Console }\n * { type: ExporterType.Console, batch: { maxExportBatchSize: 200 } }\n * ```\n *\n * @public\n */\nexport interface ConsoleTraceConfig {\n /** Discriminator for console exporter */\n type: typeof ExporterType.Console;\n /** Optional batch processing configuration */\n batch?: BatchConfig;\n}\n\n/**\n * OTLP trace exporter configuration.\n *\n * Exports traces via OTLP protocol to any compatible backend:\n * - Langfuse: https://cloud.langfuse.com/api/public/otlp/v1/traces\n * - Jaeger: http://localhost:4318/v1/traces\n * - OTel Collector: custom endpoint\n *\n * @example\n * ```typescript\n * import { ExporterType } from '@cloudbase/agent-observability/server';\n *\n * {\n * type: ExporterType.OTLP,\n * url: 'https://cloud.langfuse.com/api/public/otlp/v1/traces',\n * headers: {\n * 'Authorization': 'Basic ' + btoa('pk-lf-xxx:sk-lf-xxx')\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface OTLPTraceConfig {\n /** Discriminator for OTLP exporter */\n type: typeof ExporterType.OTLP;\n /** OTLP endpoint URL (http/https) */\n url: string;\n /** Optional HTTP headers for authentication */\n headers?: Record<string, string>;\n /** Request timeout in milliseconds (default: 10000) */\n timeout?: number;\n /** Optional batch processing configuration */\n batch?: BatchConfig;\n}\n\n/**\n * Custom trace exporter configuration.\n *\n * Allows users to provide custom trace setup logic.\n * Useful for integrations not covered by console/otlp.\n *\n * @example\n * ```typescript\n * import { ExporterType } from '@cloudbase/agent-observability/server';\n *\n * {\n * type: ExporterType.Custom,\n * setup: async () => {\n * const exporter = new MyCustomExporter();\n * const provider = new BasicTracerProvider();\n * provider.addSpanProcessor(new SimpleSpanProcessor(exporter));\n * provider.register();\n * }\n * }\n * ```\n *\n * @public\n */\nexport interface CustomTraceConfig {\n /** Discriminator for custom setup */\n type: typeof ExporterType.Custom;\n /** User-defined setup function */\n setup: () => Promise<void> | void;\n}\n\n/**\n * Union type of all supported trace exporter configurations.\n *\n * @public\n */\nexport type ObservabilityConfig =\n | ConsoleTraceConfig\n | OTLPTraceConfig\n | CustomTraceConfig;\n"],"mappings":"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","names":[]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cloudbase/agent-observability",
|
|
3
|
-
"version": "1.0.1-alpha.
|
|
3
|
+
"version": "1.0.1-alpha.18",
|
|
4
4
|
"description": "OpenInference-compatible observability for AG-Kit",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"@arizeai/openinference-semantic-conventions": "^2.1.7",
|
|
66
66
|
"@opentelemetry/api": "^1.9.0",
|
|
67
67
|
"@opentelemetry/semantic-conventions": "^1.39.0",
|
|
68
|
-
"@cloudbase/agent-shared": "^1.0.1-alpha.
|
|
68
|
+
"@cloudbase/agent-shared": "^1.0.1-alpha.18"
|
|
69
69
|
},
|
|
70
70
|
"optionalDependencies": {
|
|
71
71
|
"@opentelemetry/exporter-trace-otlp-http": "^0.210.0",
|
package/src/core/attributes.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { OBSERVABILITY_TRACER_NAME, OtelSpanAttributes } from "./constants
|
|
1
|
+
import { OBSERVABILITY_TRACER_NAME, OtelSpanAttributes } from "./constants";
|
|
2
2
|
import { SemanticConventions } from "@arizeai/openinference-semantic-conventions";
|
|
3
|
-
import { ObservationAttributes, TraceAttributes, ObservationType } from "../types
|
|
3
|
+
import { ObservationAttributes, TraceAttributes, ObservationType, LLMMessage, Document, ToolCall } from "../types";
|
|
4
4
|
import { type Attributes } from "@opentelemetry/api";
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -111,6 +111,20 @@ export function createObservationAttributes(
|
|
|
111
111
|
if (model) {
|
|
112
112
|
otelAttributes[SemanticConventions.LLM_MODEL_NAME] = model;
|
|
113
113
|
}
|
|
114
|
+
// Extract system and provider safely
|
|
115
|
+
const system = (attributes as Record<string, unknown>)?.system;
|
|
116
|
+
const provider = (attributes as Record<string, unknown>)?.provider;
|
|
117
|
+
const inputMessages = (attributes as Record<string, unknown>)?.inputMessages;
|
|
118
|
+
const outputMessages = (attributes as Record<string, unknown>)?.outputMessages;
|
|
119
|
+
const inputMimeType = (attributes as Record<string, unknown>)?.inputMimeType;
|
|
120
|
+
const outputMimeType = (attributes as Record<string, unknown>)?.outputMimeType;
|
|
121
|
+
|
|
122
|
+
if (system !== undefined) {
|
|
123
|
+
otelAttributes[SemanticConventions.LLM_SYSTEM] = String(system);
|
|
124
|
+
}
|
|
125
|
+
if (provider !== undefined) {
|
|
126
|
+
otelAttributes[SemanticConventions.LLM_PROVIDER] = String(provider);
|
|
127
|
+
}
|
|
114
128
|
if (modelParameters) {
|
|
115
129
|
otelAttributes[SemanticConventions.LLM_INVOCATION_PARAMETERS] =
|
|
116
130
|
_serialize(modelParameters);
|
|
@@ -143,6 +157,22 @@ export function createObservationAttributes(
|
|
|
143
157
|
otelAttributes[OtelSpanAttributes.LLM_COMPLETION_START_TIME] =
|
|
144
158
|
_serialize(completionStartTime);
|
|
145
159
|
}
|
|
160
|
+
// Flatten LLM messages
|
|
161
|
+
if (inputMessages !== undefined && Array.isArray(inputMessages)) {
|
|
162
|
+
const messageAttrs = _flattenLLMMessages(inputMessages as LLMMessage[], "llm.input_messages");
|
|
163
|
+
Object.assign(otelAttributes, messageAttrs);
|
|
164
|
+
}
|
|
165
|
+
if (outputMessages !== undefined && Array.isArray(outputMessages)) {
|
|
166
|
+
const messageAttrs = _flattenLLMMessages(outputMessages as LLMMessage[], "llm.output_messages");
|
|
167
|
+
Object.assign(otelAttributes, messageAttrs);
|
|
168
|
+
}
|
|
169
|
+
// Add MIME types
|
|
170
|
+
if (inputMimeType !== undefined) {
|
|
171
|
+
otelAttributes[SemanticConventions.INPUT_MIME_TYPE] = String(inputMimeType);
|
|
172
|
+
}
|
|
173
|
+
if (outputMimeType !== undefined) {
|
|
174
|
+
otelAttributes[SemanticConventions.OUTPUT_MIME_TYPE] = String(outputMimeType);
|
|
175
|
+
}
|
|
146
176
|
}
|
|
147
177
|
|
|
148
178
|
// Embedding-specific attributes
|
|
@@ -156,6 +186,51 @@ export function createObservationAttributes(
|
|
|
156
186
|
}
|
|
157
187
|
}
|
|
158
188
|
|
|
189
|
+
// Tool-specific attributes
|
|
190
|
+
if (type === "tool") {
|
|
191
|
+
const toolName = (attributes as Record<string, unknown>)?.toolName ?? (attributes as Record<string, unknown>)?.tool_name;
|
|
192
|
+
const toolDescription = (attributes as Record<string, unknown>)?.toolDescription;
|
|
193
|
+
const toolParameters = (attributes as Record<string, unknown>)?.toolParameters;
|
|
194
|
+
const toolCall = (attributes as Record<string, unknown>)?.toolCall;
|
|
195
|
+
|
|
196
|
+
if (toolName !== undefined) {
|
|
197
|
+
otelAttributes[SemanticConventions.TOOL_NAME] = String(toolName);
|
|
198
|
+
}
|
|
199
|
+
if (toolDescription !== undefined) {
|
|
200
|
+
otelAttributes[SemanticConventions.TOOL_DESCRIPTION] = String(toolDescription);
|
|
201
|
+
}
|
|
202
|
+
if (toolParameters !== undefined) {
|
|
203
|
+
otelAttributes[SemanticConventions.TOOL_PARAMETERS] = _serialize(toolParameters);
|
|
204
|
+
}
|
|
205
|
+
// Flatten tool call if present
|
|
206
|
+
if (toolCall !== undefined && typeof toolCall === "object") {
|
|
207
|
+
const toolCallAttrs = _flattenToolCall(toolCall as ToolCall);
|
|
208
|
+
Object.assign(otelAttributes, toolCallAttrs);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Agent-specific attributes
|
|
213
|
+
if (type === "agent") {
|
|
214
|
+
const agentName = (attributes as Record<string, unknown>)?.agentName;
|
|
215
|
+
if (agentName !== undefined) {
|
|
216
|
+
otelAttributes[SemanticConventions.AGENT_NAME] = String(agentName);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Retriever-specific attributes
|
|
221
|
+
if (type === "retriever") {
|
|
222
|
+
const documents = (attributes as Record<string, unknown>)?.documents;
|
|
223
|
+
const query = (attributes as Record<string, unknown>)?.query;
|
|
224
|
+
|
|
225
|
+
if (documents !== undefined && Array.isArray(documents)) {
|
|
226
|
+
const docAttrs = _flattenDocuments(documents as Document[]);
|
|
227
|
+
Object.assign(otelAttributes, docAttrs);
|
|
228
|
+
}
|
|
229
|
+
if (query !== undefined) {
|
|
230
|
+
otelAttributes["retriever.query"] = String(query);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
159
234
|
// Add metadata (use OpenInference metadata convention)
|
|
160
235
|
const metadataAttrs = _flattenAndSerializeMetadata(
|
|
161
236
|
metadata,
|
|
@@ -170,6 +245,14 @@ export function createObservationAttributes(
|
|
|
170
245
|
);
|
|
171
246
|
Object.assign(otelAttributes, obsetvabilityMetadataAttrs);
|
|
172
247
|
|
|
248
|
+
// Pass through any additional custom attributes (e.g., agui.thread_id, agui.run_id)
|
|
249
|
+
// Only pass through attributes that haven't been set yet to avoid overwriting internal ones
|
|
250
|
+
for (const [key, value] of Object.entries(attributes)) {
|
|
251
|
+
if (!(key in otelAttributes) && value !== undefined && value !== null) {
|
|
252
|
+
otelAttributes[key] = typeof value === 'string' ? value : _serialize(value);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
173
256
|
// Filter out null/undefined values
|
|
174
257
|
return Object.fromEntries(
|
|
175
258
|
Object.entries(otelAttributes).filter(([_, v]) => v != null),
|
|
@@ -231,3 +314,161 @@ function _flattenAndSerializeMetadata(
|
|
|
231
314
|
|
|
232
315
|
return metadataAttributes;
|
|
233
316
|
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Flattens LLM messages into OpenTelemetry attributes.
|
|
320
|
+
*
|
|
321
|
+
* Follows OpenInference convention:
|
|
322
|
+
* - llm.input_messages.{i}.message.role
|
|
323
|
+
* - llm.input_messages.{i}.message.content
|
|
324
|
+
* - llm.input_messages.{i}.message.tool_calls
|
|
325
|
+
*
|
|
326
|
+
* @param messages - Array of LLM messages
|
|
327
|
+
* @param prefix - Attribute prefix ('llm.input_messages' or 'llm.output_messages')
|
|
328
|
+
* @returns Flattened message attributes
|
|
329
|
+
* @internal
|
|
330
|
+
*/
|
|
331
|
+
function _flattenLLMMessages(
|
|
332
|
+
messages: LLMMessage[] | undefined,
|
|
333
|
+
prefix: string,
|
|
334
|
+
): Record<string, string> {
|
|
335
|
+
const attributes: Record<string, string> = {};
|
|
336
|
+
|
|
337
|
+
if (!messages || !Array.isArray(messages)) {
|
|
338
|
+
return attributes;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
try {
|
|
342
|
+
messages.forEach((msg, index) => {
|
|
343
|
+
if (!msg || typeof msg !== "object") return;
|
|
344
|
+
|
|
345
|
+
const baseKey = `${prefix}.${index}.message`;
|
|
346
|
+
|
|
347
|
+
if (msg.role !== undefined) {
|
|
348
|
+
attributes[`${baseKey}.role`] = String(msg.role);
|
|
349
|
+
}
|
|
350
|
+
if (msg.content !== undefined) {
|
|
351
|
+
attributes[`${baseKey}.content`] = String(msg.content);
|
|
352
|
+
}
|
|
353
|
+
if (msg.toolCallId !== undefined) {
|
|
354
|
+
attributes[`${baseKey}.tool_call_id`] = String(msg.toolCallId);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Flatten tool calls if present
|
|
358
|
+
if (msg.toolCalls && Array.isArray(msg.toolCalls)) {
|
|
359
|
+
msg.toolCalls.forEach((toolCall, tcIndex) => {
|
|
360
|
+
if (!toolCall || typeof toolCall !== "object") return;
|
|
361
|
+
|
|
362
|
+
const tcKey = `${baseKey}.tool_calls.${tcIndex}`;
|
|
363
|
+
|
|
364
|
+
if (toolCall.id !== undefined) {
|
|
365
|
+
attributes[`${tcKey}.id`] = String(toolCall.id);
|
|
366
|
+
}
|
|
367
|
+
if (toolCall.function !== undefined) {
|
|
368
|
+
if (toolCall.function.name !== undefined) {
|
|
369
|
+
attributes[`${tcKey}.function.name`] = String(toolCall.function.name);
|
|
370
|
+
}
|
|
371
|
+
if (toolCall.function.arguments !== undefined) {
|
|
372
|
+
attributes[`${tcKey}.function.arguments`] = String(toolCall.function.arguments);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
});
|
|
378
|
+
} catch (e) {
|
|
379
|
+
// Silent fail - don't block business logic for observability
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
return attributes;
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Flattens documents into OpenTelemetry attributes.
|
|
387
|
+
*
|
|
388
|
+
* Follows OpenInference convention:
|
|
389
|
+
* - retrieval.documents.{i}.document.id
|
|
390
|
+
* - retrieval.documents.{i}.document.content
|
|
391
|
+
* - retrieval.documents.{i}.document.score
|
|
392
|
+
* - retrieval.documents.{i}.document.metadata
|
|
393
|
+
*
|
|
394
|
+
* @param documents - Array of documents
|
|
395
|
+
* @returns Flattened document attributes
|
|
396
|
+
* @internal
|
|
397
|
+
*/
|
|
398
|
+
function _flattenDocuments(
|
|
399
|
+
documents: Document[] | undefined,
|
|
400
|
+
): Record<string, string> {
|
|
401
|
+
const attributes: Record<string, string> = {};
|
|
402
|
+
|
|
403
|
+
if (!documents || !Array.isArray(documents)) {
|
|
404
|
+
return attributes;
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
try {
|
|
408
|
+
documents.forEach((doc, index) => {
|
|
409
|
+
if (!doc || typeof doc !== "object") return;
|
|
410
|
+
|
|
411
|
+
const baseKey = `retrieval.documents.${index}.document`;
|
|
412
|
+
|
|
413
|
+
if (doc.id !== undefined) {
|
|
414
|
+
attributes[`${baseKey}.id`] = String(doc.id);
|
|
415
|
+
}
|
|
416
|
+
if (doc.content !== undefined) {
|
|
417
|
+
attributes[`${baseKey}.content`] = String(doc.content);
|
|
418
|
+
}
|
|
419
|
+
if (doc.score !== undefined) {
|
|
420
|
+
attributes[`${baseKey}.score`] = String(doc.score);
|
|
421
|
+
}
|
|
422
|
+
if (doc.metadata !== undefined && typeof doc.metadata === "object") {
|
|
423
|
+
const metadataSerialized = _serialize(doc.metadata);
|
|
424
|
+
if (metadataSerialized) {
|
|
425
|
+
attributes[`${baseKey}.metadata`] = metadataSerialized;
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
});
|
|
429
|
+
} catch (e) {
|
|
430
|
+
// Silent fail - don't block business logic for observability
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return attributes;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
/**
|
|
437
|
+
* Flattens tool call into OpenTelemetry attributes.
|
|
438
|
+
*
|
|
439
|
+
* Follows OpenInference convention:
|
|
440
|
+
* - tool_call.id
|
|
441
|
+
* - tool_call.function.name
|
|
442
|
+
* - tool_call.function.arguments
|
|
443
|
+
*
|
|
444
|
+
* @param toolCall - Tool call object
|
|
445
|
+
* @returns Flattened tool call attributes
|
|
446
|
+
* @internal
|
|
447
|
+
*/
|
|
448
|
+
function _flattenToolCall(
|
|
449
|
+
toolCall: ToolCall | undefined,
|
|
450
|
+
): Record<string, string> {
|
|
451
|
+
const attributes: Record<string, string> = {};
|
|
452
|
+
|
|
453
|
+
if (!toolCall || typeof toolCall !== "object") {
|
|
454
|
+
return attributes;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
try {
|
|
458
|
+
if (toolCall.id !== undefined) {
|
|
459
|
+
attributes["tool_call.id"] = String(toolCall.id);
|
|
460
|
+
}
|
|
461
|
+
if (toolCall.function !== undefined) {
|
|
462
|
+
if (toolCall.function.name !== undefined) {
|
|
463
|
+
attributes["tool_call.function.name"] = String(toolCall.function.name);
|
|
464
|
+
}
|
|
465
|
+
if (toolCall.function.arguments !== undefined) {
|
|
466
|
+
attributes["tool_call.function.arguments"] = String(toolCall.function.arguments);
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
} catch (e) {
|
|
470
|
+
// Silent fail - don't block business logic for observability
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return attributes;
|
|
474
|
+
}
|
package/src/core/spanWrapper.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { Span, TimeInput } from "@opentelemetry/api";
|
|
2
2
|
|
|
3
|
-
import { createObservationAttributes, createTraceAttributes } from "./attributes
|
|
4
|
-
import { getTracer } from "./tracerProvider
|
|
3
|
+
import { createObservationAttributes, createTraceAttributes } from "./attributes";
|
|
4
|
+
import { getTracer } from "./tracerProvider";
|
|
5
5
|
import {
|
|
6
6
|
BaseSpanAttributes,
|
|
7
7
|
LLMAttributes,
|
|
8
8
|
TraceAttributes,
|
|
9
9
|
ObservationType,
|
|
10
|
-
} from "../types
|
|
10
|
+
} from "../types";
|
|
11
11
|
import type {
|
|
12
12
|
ToolAttributes,
|
|
13
13
|
AgentAttributes,
|
|
@@ -18,7 +18,7 @@ import type {
|
|
|
18
18
|
GuardrailAttributes,
|
|
19
19
|
EmbeddingAttributes,
|
|
20
20
|
ObservationAttributes,
|
|
21
|
-
} from "../types
|
|
21
|
+
} from "../types";
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Union type representing any observation wrapper.
|
|
@@ -194,7 +194,7 @@ abstract class BaseObservation {
|
|
|
194
194
|
options?: { asType?: ObservationType },
|
|
195
195
|
): Observation {
|
|
196
196
|
// Import here to avoid circular dependency
|
|
197
|
-
const { startObservation: startObs } = require("../index
|
|
197
|
+
const { startObservation: startObs } = require("../index");
|
|
198
198
|
const { asType = "span" } = options || {};
|
|
199
199
|
|
|
200
200
|
return startObs(name, attributes, {
|
package/src/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ import { trace, context, TimeInput, SpanStatusCode, Span, SpanContext } from "@o
|
|
|
9
9
|
import {
|
|
10
10
|
createObservationAttributes,
|
|
11
11
|
createTraceAttributes,
|
|
12
|
-
} from "./core/attributes
|
|
12
|
+
} from "./core/attributes";
|
|
13
13
|
import {
|
|
14
14
|
ObservationSpan,
|
|
15
15
|
ObservationLLM,
|
|
@@ -22,8 +22,8 @@ import {
|
|
|
22
22
|
ObservationEvaluator,
|
|
23
23
|
ObservationGuardrail,
|
|
24
24
|
type Observation,
|
|
25
|
-
} from "./core/spanWrapper
|
|
26
|
-
import { getTracer } from "./core/tracerProvider
|
|
25
|
+
} from "./core/spanWrapper";
|
|
26
|
+
import { getTracer } from "./core/tracerProvider";
|
|
27
27
|
import {
|
|
28
28
|
ObservationType,
|
|
29
29
|
ObservationLevel,
|
|
@@ -39,7 +39,10 @@ import {
|
|
|
39
39
|
EmbeddingAttributes,
|
|
40
40
|
ObservationAttributes,
|
|
41
41
|
TraceAttributes,
|
|
42
|
-
|
|
42
|
+
LLMMessage,
|
|
43
|
+
ToolCall,
|
|
44
|
+
Document,
|
|
45
|
+
} from "./types";
|
|
43
46
|
|
|
44
47
|
// Export types
|
|
45
48
|
export type {
|
|
@@ -57,6 +60,9 @@ export type {
|
|
|
57
60
|
EmbeddingAttributes,
|
|
58
61
|
ObservationAttributes,
|
|
59
62
|
TraceAttributes,
|
|
63
|
+
LLMMessage,
|
|
64
|
+
ToolCall,
|
|
65
|
+
Document,
|
|
60
66
|
};
|
|
61
67
|
|
|
62
68
|
// Export observation classes
|
|
@@ -80,12 +86,12 @@ export type { Observation };
|
|
|
80
86
|
export {
|
|
81
87
|
createTraceAttributes,
|
|
82
88
|
createObservationAttributes,
|
|
83
|
-
} from "./core/attributes
|
|
89
|
+
} from "./core/attributes";
|
|
84
90
|
export {
|
|
85
91
|
setTracerProvider,
|
|
86
92
|
getTracerProvider,
|
|
87
93
|
getTracer,
|
|
88
|
-
} from "./core/tracerProvider
|
|
94
|
+
} from "./core/tracerProvider";
|
|
89
95
|
|
|
90
96
|
/**
|
|
91
97
|
* Options for starting observations (spans).
|