@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/package.json ADDED
@@ -0,0 +1,91 @@
1
+ {
2
+ "name": "@cloudbase/agent-observability",
3
+ "version": "1.0.1-alpha.9",
4
+ "description": "OpenInference-compatible observability for AG-Kit",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
18
+ },
19
+ "./langchain": {
20
+ "import": {
21
+ "types": "./dist/langchain.d.mts",
22
+ "default": "./dist/langchain.mjs"
23
+ },
24
+ "require": {
25
+ "types": "./dist/langchain.d.ts",
26
+ "default": "./dist/langchain.js"
27
+ }
28
+ },
29
+ "./core": {
30
+ "import": {
31
+ "types": "./dist/core.d.mts",
32
+ "default": "./dist/core.mjs"
33
+ },
34
+ "require": {
35
+ "types": "./dist/core.d.ts",
36
+ "default": "./dist/core.js"
37
+ }
38
+ },
39
+ "./server": {
40
+ "import": {
41
+ "types": "./dist/server.d.mts",
42
+ "default": "./dist/server.mjs"
43
+ },
44
+ "require": {
45
+ "types": "./dist/server.d.ts",
46
+ "default": "./dist/server.js"
47
+ }
48
+ }
49
+ },
50
+ "files": [
51
+ "dist",
52
+ "src"
53
+ ],
54
+ "keywords": [
55
+ "ag-kit",
56
+ "observability",
57
+ "opentelemetry",
58
+ "openinference",
59
+ "langfuse",
60
+ "tracing"
61
+ ],
62
+ "author": "",
63
+ "license": "ISC",
64
+ "dependencies": {
65
+ "@arizeai/openinference-semantic-conventions": "^2.1.7",
66
+ "@opentelemetry/api": "^1.9.0",
67
+ "@opentelemetry/semantic-conventions": "^1.39.0"
68
+ },
69
+ "optionalDependencies": {
70
+ "@opentelemetry/exporter-trace-otlp-http": "^0.210.0",
71
+ "@opentelemetry/resources": "^2.4.0",
72
+ "@opentelemetry/sdk-node": "^0.210.0",
73
+ "@opentelemetry/sdk-trace-base": "^2.4.0",
74
+ "@opentelemetry/sdk-trace-node": "^2.4.0"
75
+ },
76
+ "devDependencies": {
77
+ "@langchain/core": "^1.0.2",
78
+ "@opentelemetry/exporter-trace-otlp-http": "^0.210.0",
79
+ "@types/node": "^20.0.0",
80
+ "tsup": "^8.5.0",
81
+ "typescript": "^5.0.0"
82
+ },
83
+ "peerDependencies": {
84
+ "@langchain/core": "^1.0.0"
85
+ },
86
+ "scripts": {
87
+ "build": "tsup --config tsup.config.ts",
88
+ "dev": "tsup --config tsup.config.ts --watch",
89
+ "typecheck": "tsc --noEmit"
90
+ }
91
+ }
@@ -0,0 +1,233 @@
1
+ import { OBSERVABILITY_TRACER_NAME, OtelSpanAttributes } from "./constants.js";
2
+ import { SemanticConventions } from "@arizeai/openinference-semantic-conventions";
3
+ import { ObservationAttributes, TraceAttributes, ObservationType } from "../types.js";
4
+ import { type Attributes } from "@opentelemetry/api";
5
+
6
+ /**
7
+ * Creates OpenTelemetry attributes from trace attributes.
8
+ *
9
+ * Converts user-friendly trace attributes into OpenTelemetry attribute format
10
+ * using OpenInference semantic conventions where applicable.
11
+ *
12
+ * @param attributes - Trace attributes to convert
13
+ * @returns OpenTelemetry attributes object
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const otelAttributes = createTraceAttributes({
18
+ * name: 'user-workflow',
19
+ * userId: 'user-123',
20
+ * sessionId: 'session-456',
21
+ * tags: ['checkout', 'payment']
22
+ * });
23
+ * ```
24
+ *
25
+ * @public
26
+ */
27
+ export function createTraceAttributes({
28
+ name,
29
+ userId,
30
+ sessionId,
31
+ version,
32
+ release,
33
+ input,
34
+ output,
35
+ metadata,
36
+ tags,
37
+ environment,
38
+ public: isPublic,
39
+ }: TraceAttributes = {}): Attributes {
40
+ const attributes = {
41
+ [OtelSpanAttributes.TRACE_NAME]: name,
42
+ // Use OpenInference standard attributes for user and session
43
+ [OtelSpanAttributes.USER_ID]: userId,
44
+ [OtelSpanAttributes.SESSION_ID]: sessionId,
45
+ [OtelSpanAttributes.VERSION]: version,
46
+ [OtelSpanAttributes.RELEASE]: release,
47
+ [OtelSpanAttributes.TRACE_INPUT]: _serialize(input),
48
+ [OtelSpanAttributes.TRACE_OUTPUT]: _serialize(output),
49
+ [OtelSpanAttributes.TRACE_TAGS]: tags,
50
+ [OtelSpanAttributes.ENVIRONMENT]: environment,
51
+ [OtelSpanAttributes.TRACE_PUBLIC]: isPublic,
52
+ ..._flattenAndSerializeMetadata(metadata, OtelSpanAttributes.TRACE_METADATA),
53
+ };
54
+
55
+ return Object.fromEntries(
56
+ Object.entries(attributes).filter(([_, v]) => v != null),
57
+ );
58
+ }
59
+
60
+ /**
61
+ * Creates OpenTelemetry attributes from observation attributes.
62
+ *
63
+ * Maps observation attributes to OpenInference semantic conventions:
64
+ * - Uses `openinference.span.kind` for span type
65
+ * - Uses `llm.*` for LLM-specific attributes
66
+ * - Uses `tool.*` for tool-specific attributes
67
+ * - Falls back to `agkit.observation.*` for non-standard attributes
68
+ *
69
+ * @param type - Observation type (llm, tool, chain, etc.)
70
+ * @param attributes - Observation attributes to convert
71
+ * @returns OpenTelemetry attributes object
72
+ *
73
+ * @public
74
+ */
75
+ export function createObservationAttributes(
76
+ type: ObservationType,
77
+ attributes: ObservationAttributes,
78
+ ): Attributes {
79
+ const {
80
+ metadata,
81
+ input,
82
+ output,
83
+ level,
84
+ statusMessage,
85
+ version,
86
+ completionStartTime,
87
+ model,
88
+ modelParameters,
89
+ usageDetails,
90
+ } = attributes;
91
+
92
+ // Base attributes for all observation types
93
+ const otelAttributes: Attributes = {
94
+ [SemanticConventions.OPENINFERENCE_SPAN_KIND]: type.toUpperCase(),
95
+ [OtelSpanAttributes.OBSERVATION_TYPE]: type,
96
+ [OtelSpanAttributes.OBSERVATION_LEVEL]: level,
97
+ [OtelSpanAttributes.OBSERVATION_STATUS_MESSAGE]: statusMessage,
98
+ [OtelSpanAttributes.VERSION]: version,
99
+ // Use OpenInference input.value convention
100
+ [SemanticConventions.INPUT_VALUE]: _serialize(input),
101
+ // Also set legacy agkit.observation.input for compatibility
102
+ [OtelSpanAttributes.OBSERVATION_INPUT]: _serialize(input),
103
+ // Use OpenInference output.value convention
104
+ [SemanticConventions.OUTPUT_VALUE]: _serialize(output),
105
+ // Also set legacy agkit.observation.output for compatibility
106
+ [OtelSpanAttributes.OBSERVATION_OUTPUT]: _serialize(output),
107
+ };
108
+
109
+ // LLM-specific attributes
110
+ if (type === "llm") {
111
+ if (model) {
112
+ otelAttributes[SemanticConventions.LLM_MODEL_NAME] = model;
113
+ }
114
+ if (modelParameters) {
115
+ otelAttributes[SemanticConventions.LLM_INVOCATION_PARAMETERS] =
116
+ _serialize(modelParameters);
117
+ // Also set agkit.llm.model_parameters for compatibility
118
+ otelAttributes[OtelSpanAttributes.LLM_MODEL_PARAMETERS] =
119
+ _serialize(modelParameters);
120
+ }
121
+ if (usageDetails) {
122
+ // Map to OpenInference llm.token_count.* attributes
123
+ if (typeof usageDetails === "object") {
124
+ const usage = usageDetails as Record<string, number>;
125
+ if (usage.promptTokens !== undefined) {
126
+ otelAttributes[SemanticConventions.LLM_TOKEN_COUNT_PROMPT] =
127
+ usage.promptTokens;
128
+ }
129
+ if (usage.completionTokens !== undefined) {
130
+ otelAttributes[SemanticConventions.LLM_TOKEN_COUNT_COMPLETION] =
131
+ usage.completionTokens;
132
+ }
133
+ if (usage.totalTokens !== undefined) {
134
+ otelAttributes[SemanticConventions.LLM_TOKEN_COUNT_TOTAL] =
135
+ usage.totalTokens;
136
+ }
137
+ }
138
+ // Also set legacy agkit.llm.usage_details for compatibility
139
+ otelAttributes[OtelSpanAttributes.LLM_USAGE_DETAILS] =
140
+ _serialize(usageDetails);
141
+ }
142
+ if (completionStartTime) {
143
+ otelAttributes[OtelSpanAttributes.LLM_COMPLETION_START_TIME] =
144
+ _serialize(completionStartTime);
145
+ }
146
+ }
147
+
148
+ // Embedding-specific attributes
149
+ if (type === "embedding") {
150
+ if (model) {
151
+ otelAttributes[SemanticConventions.EMBEDDING_MODEL_NAME] = model;
152
+ }
153
+ if (modelParameters) {
154
+ otelAttributes[SemanticConventions.LLM_INVOCATION_PARAMETERS] =
155
+ _serialize(modelParameters);
156
+ }
157
+ }
158
+
159
+ // Add metadata (use OpenInference metadata convention)
160
+ const metadataAttrs = _flattenAndSerializeMetadata(
161
+ metadata,
162
+ SemanticConventions.METADATA,
163
+ );
164
+ Object.assign(otelAttributes, metadataAttrs);
165
+
166
+ // Also add agkit.observation.metadata for compatibility
167
+ const obsetvabilityMetadataAttrs = _flattenAndSerializeMetadata(
168
+ metadata,
169
+ OtelSpanAttributes.OBSERVATION_METADATA
170
+ );
171
+ Object.assign(otelAttributes, obsetvabilityMetadataAttrs);
172
+
173
+ // Filter out null/undefined values
174
+ return Object.fromEntries(
175
+ Object.entries(otelAttributes).filter(([_, v]) => v != null),
176
+ );
177
+ }
178
+
179
+ /**
180
+ * Safely serializes an object to JSON string.
181
+ *
182
+ * @param obj - Object to serialize
183
+ * @returns JSON string or undefined if null/undefined
184
+ * @internal
185
+ */
186
+ function _serialize(obj: unknown): string | undefined {
187
+ try {
188
+ if (typeof obj === "string") return obj;
189
+ if (obj instanceof Date) return obj.toISOString();
190
+ return obj != null ? JSON.stringify(obj) : undefined;
191
+ } catch {
192
+ return "<failed to serialize>";
193
+ }
194
+ }
195
+
196
+ /**
197
+ * Flattens and serializes metadata into OpenTelemetry attribute format.
198
+ *
199
+ * Converts nested metadata objects into dot-notation attribute keys.
200
+ * For example, `{ database: { host: 'localhost' } }` becomes
201
+ * `{ 'metadata.database.host': 'localhost' }` (or 'agkit.observation.metadata.database.host').
202
+ *
203
+ * @param metadata - Metadata object to flatten
204
+ * @param prefix - Attribute prefix (e.g., 'metadata' or 'agkit.observation.metadata')
205
+ * @returns Flattened metadata attributes
206
+ * @internal
207
+ */
208
+ function _flattenAndSerializeMetadata(
209
+ metadata: unknown,
210
+ prefix: string,
211
+ ): Record<string, string> {
212
+ const metadataAttributes: Record<string, string> = {};
213
+
214
+ if (metadata === undefined || metadata === null) {
215
+ return metadataAttributes;
216
+ }
217
+
218
+ if (typeof metadata !== "object" || Array.isArray(metadata)) {
219
+ const serialized = _serialize(metadata);
220
+ if (serialized) {
221
+ metadataAttributes[prefix] = serialized;
222
+ }
223
+ } else {
224
+ for (const [key, value] of Object.entries(metadata)) {
225
+ const serialized = typeof value === "string" ? value : _serialize(value);
226
+ if (serialized) {
227
+ metadataAttributes[`${prefix}.${key}`] = serialized;
228
+ }
229
+ }
230
+ }
231
+
232
+ return metadataAttributes;
233
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * OTEL attribute constants for AG-Kit observability.
3
+ *
4
+ * Uses OpenInference semantic conventions where applicable:
5
+ * https://github.com/Arize-ai/openinference/tree/main/spec
6
+ *
7
+ * Falls back to AG-Kit specific attributes where OpenInference
8
+ * doesn't define a standard.
9
+ *
10
+ * @module
11
+ */
12
+
13
+ import {
14
+ SemanticConventions,
15
+ OpenInferenceSpanKind,
16
+ } from "@arizeai/openinference-semantic-conventions";
17
+
18
+ // Re-export OpenInference types for convenience
19
+ export { OpenInferenceSpanKind };
20
+
21
+ /**
22
+ * SDK information
23
+ */
24
+ export const OBSERVABILITY_TRACER_NAME = "agkit-tracer";
25
+ export const OBSERVABILITY_SDK_NAME = "@agkit/observability";
26
+ // Version will be injected from package.json
27
+
28
+ /**
29
+ * Combined attribute namespace for internal use
30
+ * Provides a single namespace for all OTEL attributes used by AG-Kit
31
+ *
32
+ * Combines OpenInference SemanticConventions with AG-Kit specific attributes
33
+ */
34
+ export const OtelSpanAttributes = {
35
+ // OpenInference - re-export all standard conventions
36
+ ...SemanticConventions,
37
+
38
+ // AG-Kit Trace attributes (non-standard)
39
+ TRACE_NAME: "trace.name",
40
+ TRACE_TAGS: "trace.tags",
41
+ TRACE_PUBLIC: "trace.public",
42
+ TRACE_METADATA: "trace.metadata",
43
+ TRACE_INPUT: "trace.input",
44
+ TRACE_OUTPUT: "trace.output",
45
+
46
+ // AG-Kit Observation attributes (non-standard)
47
+ OBSERVATION_TYPE: "observation.type",
48
+ OBSERVATION_LEVEL: "observation.level",
49
+ OBSERVATION_STATUS_MESSAGE: "observation.status_message",
50
+ OBSERVATION_INPUT: "observation.input",
51
+ OBSERVATION_OUTPUT: "observation.output",
52
+ OBSERVATION_METADATA: "observation.metadata",
53
+
54
+ // AG-Kit LLM-specific (non-standard)
55
+ LLM_COMPLETION_START_TIME: "llm.completion_start_time",
56
+ LLM_MODEL_PARAMETERS: "llm.model_parameters",
57
+ LLM_USAGE_DETAILS: "llm.usage_details",
58
+ LLM_COST_DETAILS: "llm.cost_details",
59
+
60
+ // AG-Kit Retriever-specific (non-standard)
61
+ RETRIEVER_NAME: "retriever.name",
62
+ RETRIEVER_QUERY: "retriever.query",
63
+ RETRIEVER_INDEX_ID: "retriever.index_id",
64
+ RETRIEVER_TOP_K: "retriever.top_k",
65
+
66
+ // AG-Kit General (non-standard)
67
+ ENVIRONMENT: "environment",
68
+ RELEASE: "release",
69
+ VERSION: "version",
70
+ } as const;
71
+
72
+ /**
73
+ * Type for the OtelSpanAttributes object values
74
+ */
75
+ export type OtelSpanAttributeValues = typeof OtelSpanAttributes[keyof typeof OtelSpanAttributes];