@alvin0/ai-agent-sdk-observability-otel 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +45 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +483 -0
- package/dist/index.js.map +1 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 alvin0 (chaulamdinhai) <chaulamdinhai@gmail.com>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @alvin0/ai-agent-sdk-observability-otel
|
|
2
|
+
|
|
3
|
+
Runtime: **Universal** (the caller chooses runtime-compatible OpenTelemetry APIs).
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
pnpm add @alvin0/ai-agent-sdk-core @alvin0/ai-agent-sdk-observability-otel @opentelemetry/api @opentelemetry/api-logs
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
Universal mapping bridge for caller-supplied OpenTelemetry API objects. It
|
|
10
|
+
creates real spans synchronously, keeps explicit parent contexts without
|
|
11
|
+
ambient context or `AsyncLocalStorage`, and maps privacy-processed SDK events
|
|
12
|
+
to spans, metrics, and optional logs.
|
|
13
|
+
|
|
14
|
+
The package installs no global provider, owns no SDK or OTLP exporter, performs
|
|
15
|
+
no network I/O, and never claims telemetry delivery. Configure a journal or an
|
|
16
|
+
acknowledged exporter separately when delivery guarantees are required.
|
|
17
|
+
|
|
18
|
+
Content attributes remain disabled by default. Mapping prompt/completion bodies
|
|
19
|
+
requires the explicit bridge option `content: 'full'` and the same policy on the
|
|
20
|
+
SDK observability bus.
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { createObservability } from '@alvin0/ai-agent-sdk-core/observability'
|
|
24
|
+
import { createOpenTelemetryBridge } from '@alvin0/ai-agent-sdk-observability-otel'
|
|
25
|
+
|
|
26
|
+
const bridge = createOpenTelemetryBridge({
|
|
27
|
+
tracer: tracerProvider.getTracer('my-agent'),
|
|
28
|
+
meter: meterProvider.getMeter('my-agent'),
|
|
29
|
+
// logger: loggerProvider.getLogger('my-agent'),
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
const observation = createObservability({
|
|
33
|
+
openSpan: bridge.openSpan,
|
|
34
|
+
processors: [bridge.processor],
|
|
35
|
+
})
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Pass `observation` to the SDK core/agent entry point. The application keeps
|
|
39
|
+
ownership of provider registration, processors, exporters, flushing, and
|
|
40
|
+
shutdown. If `content: 'full'` is enabled on the bridge, enable the same policy
|
|
41
|
+
on `createObservability`; every other policy omits prompt and completion bodies.
|
|
42
|
+
|
|
43
|
+
Composition: `runtime.observability.openSpan-processors`. Lifecycle:
|
|
44
|
+
`borrowed-caller-owned`; the host retains ownership of OTel providers, flush,
|
|
45
|
+
and shutdown.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Context, Meter, Tracer } from "@opentelemetry/api";
|
|
2
|
+
import { ObservationPort, SafeErrorRecord } from "@alvin0/ai-agent-sdk-core";
|
|
3
|
+
import { ObservationContentPolicy, ObservationProcessor } from "@alvin0/ai-agent-sdk-core/observability";
|
|
4
|
+
//#region src/bridge.d.ts
|
|
5
|
+
declare const OTEL_SEMANTIC_CONVENTIONS_COMMIT = "5ca9052bc796ef1e497200b1d558fd87a201f335";
|
|
6
|
+
declare class OpenTelemetryBridgeError extends Error {
|
|
7
|
+
readonly name = "OpenTelemetryBridgeError";
|
|
8
|
+
readonly code: "OTEL_PROVIDER_UNCONFIGURED";
|
|
9
|
+
}
|
|
10
|
+
interface OpenTelemetryBridgeOptions {
|
|
11
|
+
readonly tracer: Tracer;
|
|
12
|
+
readonly meter: Meter;
|
|
13
|
+
readonly logger?: OpenTelemetryLogger;
|
|
14
|
+
/** Exact prompt/completion span attributes remain disabled unless this is explicitly `full`. */
|
|
15
|
+
readonly content?: ObservationContentPolicy;
|
|
16
|
+
readonly onDiagnostic?: (error: SafeErrorRecord) => void;
|
|
17
|
+
}
|
|
18
|
+
interface OpenTelemetryBridge {
|
|
19
|
+
readonly openSpan: ObservationPort['openSpan'];
|
|
20
|
+
readonly processor: ObservationProcessor;
|
|
21
|
+
diagnostics(): readonly SafeErrorRecord[];
|
|
22
|
+
}
|
|
23
|
+
/** Structural log API so consumers that omit logging need not install the optional logs peer. */
|
|
24
|
+
interface OpenTelemetryLogger {
|
|
25
|
+
emit(record: OpenTelemetryLogRecord): void;
|
|
26
|
+
enabled?(options?: OpenTelemetryLogEnabledOptions): boolean;
|
|
27
|
+
}
|
|
28
|
+
interface OpenTelemetryLogRecord {
|
|
29
|
+
readonly context?: Context;
|
|
30
|
+
readonly [key: string]: unknown;
|
|
31
|
+
}
|
|
32
|
+
interface OpenTelemetryLogEnabledOptions {
|
|
33
|
+
readonly context?: Context;
|
|
34
|
+
readonly [key: string]: unknown;
|
|
35
|
+
}
|
|
36
|
+
/** Build a mapping-only bridge around caller-owned OpenTelemetry API objects. */
|
|
37
|
+
declare function createOpenTelemetryBridge(options: OpenTelemetryBridgeOptions): OpenTelemetryBridge;
|
|
38
|
+
//#endregion
|
|
39
|
+
export { OTEL_SEMANTIC_CONVENTIONS_COMMIT, type OpenTelemetryBridge, OpenTelemetryBridgeError, type OpenTelemetryBridgeOptions, type OpenTelemetryLogEnabledOptions, type OpenTelemetryLogRecord, type OpenTelemetryLogger, createOpenTelemetryBridge };
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/bridge.ts"],"mappings":";;;;cAsCa;cAYA,iCAAiC;WAC1B;WACT;;UAGM;WACN,QAAQ;WACR,OAAO;WACP,SAAS;;WAET,UAAU;WACV,gBAAgB,OAAO;;UAGjB;WACN,UAAU;WACV,WAAW;EACpB,wBAAwB;;;UAIT;EACf,KAAK,QAAQ;EACb,SAAS,UAAU;;UAGJ;WACN,UAAU;YACT;;UAGK;WACN,UAAU;YACT;;;iBA+WI,0BAA0B,SAAS,6BAA6B"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
import { ROOT_CONTEXT, SpanKind, SpanStatusCode, TraceFlags, isSpanContextValid, trace } from "@opentelemetry/api";
|
|
2
|
+
import { OBSERVATION_ERROR_CODES, deepFreeze, freezeCorrelation, isSpanId, isTraceId, safeErrorRecord } from "@alvin0/ai-agent-sdk-core";
|
|
3
|
+
import { projectLog, projectMetrics } from "@alvin0/ai-agent-sdk-core/observability";
|
|
4
|
+
|
|
5
|
+
//#region src/bridge.ts
|
|
6
|
+
const OTEL_SEMANTIC_CONVENTIONS_COMMIT = "5ca9052bc796ef1e497200b1d558fd87a201f335";
|
|
7
|
+
const MAX_DIAGNOSTICS = 100;
|
|
8
|
+
const LOG_SEVERITY = Object.freeze({
|
|
9
|
+
trace: 1,
|
|
10
|
+
debug: 5,
|
|
11
|
+
info: 9,
|
|
12
|
+
warn: 13,
|
|
13
|
+
error: 17,
|
|
14
|
+
fatal: 21
|
|
15
|
+
});
|
|
16
|
+
var OpenTelemetryBridgeError = class extends Error {
|
|
17
|
+
name = "OpenTelemetryBridgeError";
|
|
18
|
+
code = OBSERVATION_ERROR_CODES.OTEL_PROVIDER_UNCONFIGURED;
|
|
19
|
+
};
|
|
20
|
+
function object(value) {
|
|
21
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
22
|
+
}
|
|
23
|
+
function finite(value) {
|
|
24
|
+
return typeof value === "number" && Number.isFinite(value) ? value : void 0;
|
|
25
|
+
}
|
|
26
|
+
function counter(value) {
|
|
27
|
+
return typeof value === "number" && Number.isSafeInteger(value) && value >= 0 ? value : void 0;
|
|
28
|
+
}
|
|
29
|
+
function string(value) {
|
|
30
|
+
return typeof value === "string" && value.length > 0 ? value : void 0;
|
|
31
|
+
}
|
|
32
|
+
function initialSpan(input) {
|
|
33
|
+
const operation = input.name === "sdk.agent.run" ? "invoke_agent" : input.name === "sdk.model.call" ? "chat" : input.name === "sdk.tool.call" ? "execute_tool" : void 0;
|
|
34
|
+
return {
|
|
35
|
+
name: operation ?? input.name,
|
|
36
|
+
kind: input.name === "sdk.model.call" || input.name === "sdk.provider.attempt" ? SpanKind.CLIENT : SpanKind.INTERNAL,
|
|
37
|
+
attributes: {
|
|
38
|
+
"ai_agent_sdk.operation.name": input.name,
|
|
39
|
+
"ai_agent_sdk.run.id": input.runId,
|
|
40
|
+
...operation === void 0 ? {} : { "gen_ai.operation.name": operation }
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
function explicitParent(input, states) {
|
|
45
|
+
if (input.parent === void 0) return ROOT_CONTEXT;
|
|
46
|
+
const owned = states.get(input.parent.spanId);
|
|
47
|
+
if (owned !== void 0) return owned.context;
|
|
48
|
+
return trace.setSpanContext(ROOT_CONTEXT, {
|
|
49
|
+
traceId: input.parent.traceId,
|
|
50
|
+
spanId: input.parent.spanId,
|
|
51
|
+
traceFlags: TraceFlags.SAMPLED,
|
|
52
|
+
isRemote: false
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function traceparent(context) {
|
|
56
|
+
const flags = (context.traceFlags & 255).toString(16).padStart(2, "0");
|
|
57
|
+
return `00-${context.traceId}-${context.spanId}-${flags}`;
|
|
58
|
+
}
|
|
59
|
+
function statusAttributes(event) {
|
|
60
|
+
const status = string(event.data.status);
|
|
61
|
+
const error = object(event.data.error);
|
|
62
|
+
const errorType = string(error?.code) ?? string(error?.type) ?? (status === "success" ? void 0 : status);
|
|
63
|
+
return {
|
|
64
|
+
...status === void 0 ? {} : { "ai_agent_sdk.operation.status": status },
|
|
65
|
+
...errorType === void 0 ? {} : { "error.type": errorType }
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function contentAttributes(event) {
|
|
69
|
+
const input = event.data.inputMessages ?? event.data.prompt;
|
|
70
|
+
const output = event.data.outputMessages ?? event.data.completion ?? event.data.response;
|
|
71
|
+
const render = (value) => {
|
|
72
|
+
if (value === void 0) return void 0;
|
|
73
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
74
|
+
};
|
|
75
|
+
const renderedInput = render(input);
|
|
76
|
+
const renderedOutput = render(output);
|
|
77
|
+
return {
|
|
78
|
+
...renderedInput === void 0 ? {} : { "gen_ai.input.messages": renderedInput },
|
|
79
|
+
...renderedOutput === void 0 ? {} : { "gen_ai.output.messages": renderedOutput }
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function applySpanEvent(state, event, allowContent) {
|
|
83
|
+
const span = state.span;
|
|
84
|
+
const attributes = {
|
|
85
|
+
"ai_agent_sdk.event.id": event.eventId,
|
|
86
|
+
"ai_agent_sdk.event.sequence": event.sequence,
|
|
87
|
+
...statusAttributes(event)
|
|
88
|
+
};
|
|
89
|
+
if (event.correlation.conversationId !== void 0) attributes["gen_ai.conversation.id"] = event.correlation.conversationId;
|
|
90
|
+
if (event.name === "sdk.agent.run") {
|
|
91
|
+
const agentId = string(event.data.agentId);
|
|
92
|
+
if (agentId !== void 0) attributes["gen_ai.agent.id"] = agentId;
|
|
93
|
+
span.updateName("invoke_agent");
|
|
94
|
+
} else if (event.name === "sdk.model.call") {
|
|
95
|
+
const provider = string(event.data.provider);
|
|
96
|
+
const model = string(event.data.model);
|
|
97
|
+
if (provider !== void 0) attributes["gen_ai.provider.name"] = provider;
|
|
98
|
+
if (model !== void 0) {
|
|
99
|
+
attributes[event.phase === "start" ? "gen_ai.request.model" : "gen_ai.response.model"] = model;
|
|
100
|
+
span.updateName(`chat ${model}`);
|
|
101
|
+
}
|
|
102
|
+
if (allowContent) Object.assign(attributes, contentAttributes(event));
|
|
103
|
+
if (event.phase === "end") Object.assign(attributes, reportedUsageAttributes(event));
|
|
104
|
+
} else if (event.name === "sdk.provider.attempt") {
|
|
105
|
+
const provider = string(event.data.provider);
|
|
106
|
+
const model = string(event.data.model);
|
|
107
|
+
const method = string(event.data.method);
|
|
108
|
+
const origin = string(event.data.origin);
|
|
109
|
+
if (provider !== void 0) attributes["gen_ai.provider.name"] = provider;
|
|
110
|
+
if (model !== void 0) attributes["gen_ai.request.model"] = model;
|
|
111
|
+
if (method !== void 0) attributes["http.request.method"] = method;
|
|
112
|
+
if (counter(event.data.httpStatus) !== void 0) attributes["http.response.status_code"] = event.data.httpStatus;
|
|
113
|
+
if (origin !== void 0) try {
|
|
114
|
+
const url = new URL(origin);
|
|
115
|
+
attributes["server.address"] = url.hostname;
|
|
116
|
+
attributes["url.scheme"] = url.protocol.slice(0, -1);
|
|
117
|
+
if (url.port.length > 0) attributes["server.port"] = Number(url.port);
|
|
118
|
+
} catch {}
|
|
119
|
+
} else if (event.name === "sdk.tool.call") {
|
|
120
|
+
const explicitName = string(event.data.toolName);
|
|
121
|
+
const legacyName = string(event.data.name)?.replace(/^execute_tool\s+/, "");
|
|
122
|
+
const toolName = explicitName ?? legacyName;
|
|
123
|
+
if (toolName !== void 0) {
|
|
124
|
+
attributes["gen_ai.tool.name"] = toolName;
|
|
125
|
+
span.updateName(`execute_tool ${toolName}`);
|
|
126
|
+
}
|
|
127
|
+
if (event.correlation.toolCallId !== void 0) attributes["gen_ai.tool.call.id"] = event.correlation.toolCallId;
|
|
128
|
+
}
|
|
129
|
+
span.setAttributes(attributes);
|
|
130
|
+
}
|
|
131
|
+
function reportedUsage(event) {
|
|
132
|
+
const direct = object(event.data.reported);
|
|
133
|
+
if (direct !== void 0) return direct;
|
|
134
|
+
return object(object(event.data.usageReport)?.reported) ?? object(object(event.data.usage)?.reported);
|
|
135
|
+
}
|
|
136
|
+
function estimatedUsage(event) {
|
|
137
|
+
return object(object(event.data.usageReport)?.estimated) ?? object(object(event.data.usage)?.estimated);
|
|
138
|
+
}
|
|
139
|
+
function reportedUsageAttributes(event) {
|
|
140
|
+
const usage = reportedUsage(event);
|
|
141
|
+
if (usage === void 0) return {};
|
|
142
|
+
const uncached = counter(usage.inputTokens);
|
|
143
|
+
const cacheRead = counter(usage.cacheReadTokens);
|
|
144
|
+
const cacheWrite = counter(usage.cacheWriteTokens);
|
|
145
|
+
const output = counter(usage.outputTokens);
|
|
146
|
+
const inputParts = [
|
|
147
|
+
uncached,
|
|
148
|
+
cacheRead,
|
|
149
|
+
cacheWrite
|
|
150
|
+
].filter((value) => value !== void 0);
|
|
151
|
+
const input = inputParts.length === 0 ? void 0 : inputParts.reduce((sum, value) => sum + value, 0);
|
|
152
|
+
return {
|
|
153
|
+
...input === void 0 || !Number.isSafeInteger(input) ? {} : { "gen_ai.usage.input_tokens": input },
|
|
154
|
+
...output === void 0 ? {} : { "gen_ai.usage.output_tokens": output },
|
|
155
|
+
...cacheRead === void 0 ? {} : { "gen_ai.usage.cache_read.input_tokens": cacheRead },
|
|
156
|
+
...cacheWrite === void 0 ? {} : { "gen_ai.usage.cache_write.input_tokens": cacheWrite },
|
|
157
|
+
...counter(usage.reasoningTokens) === void 0 ? {} : { "gen_ai.usage.reasoning.output_tokens": usage.reasoningTokens }
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
function metricAttributes(event) {
|
|
161
|
+
const error = object(event.data.error);
|
|
162
|
+
return {
|
|
163
|
+
...string(event.data.provider) === void 0 ? {} : { "gen_ai.provider.name": event.data.provider },
|
|
164
|
+
...string(event.data.operation) === void 0 ? {} : { "ai_agent_sdk.operation": event.data.operation },
|
|
165
|
+
...string(event.data.status) === void 0 ? {} : { "ai_agent_sdk.status": event.data.status },
|
|
166
|
+
...string(error?.code) === void 0 ? {} : { "error.type": error?.code }
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
function recordInternalUsage(sinks, event) {
|
|
170
|
+
if (event.phase !== "end") return;
|
|
171
|
+
const reported = reportedUsage(event);
|
|
172
|
+
const estimated = estimatedUsage(event);
|
|
173
|
+
const base = metricAttributes(event);
|
|
174
|
+
if (event.name === "sdk.model.call") {
|
|
175
|
+
recordCounterSet(sinks.sdkTokenUsage, reported, "reported", base);
|
|
176
|
+
recordCounterSet(sinks.sdkTokenUsage, estimated, "estimated", base);
|
|
177
|
+
const coverage = string(event.data.coverage) ?? string(object(event.data.usageReport)?.coverage);
|
|
178
|
+
if (coverage !== void 0) sinks.sdkUsageCoverage.add(1, {
|
|
179
|
+
...base,
|
|
180
|
+
"ai_agent_sdk.usage.coverage": coverage
|
|
181
|
+
});
|
|
182
|
+
recordSemanticUsage(sinks, reported, base);
|
|
183
|
+
} else if (event.name === "sdk.agent.run") {
|
|
184
|
+
recordCounterSet(sinks.sdkTokenUsage, estimated, "estimated", base);
|
|
185
|
+
const coverage = object(object(event.data.usage)?.coverage);
|
|
186
|
+
for (const state of [
|
|
187
|
+
"complete",
|
|
188
|
+
"partial",
|
|
189
|
+
"estimated",
|
|
190
|
+
"missing",
|
|
191
|
+
"notApplicable"
|
|
192
|
+
]) {
|
|
193
|
+
const value = counter(coverage?.[state]);
|
|
194
|
+
if (value !== void 0 && value > 0) sinks.sdkUsageCoverage.add(value, {
|
|
195
|
+
...base,
|
|
196
|
+
"ai_agent_sdk.usage.coverage": state === "notApplicable" ? "not-applicable" : state
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
function recordCounterSet(instrument, values, source, base) {
|
|
202
|
+
if (values === void 0) return;
|
|
203
|
+
for (const [key, tokenType] of [
|
|
204
|
+
["inputTokens", "input"],
|
|
205
|
+
["outputTokens", "output"],
|
|
206
|
+
["cacheReadTokens", "cache-read"],
|
|
207
|
+
["cacheWriteTokens", "cache-write"],
|
|
208
|
+
["reasoningTokens", "reasoning"]
|
|
209
|
+
]) {
|
|
210
|
+
const value = counter(values[key]);
|
|
211
|
+
if (value !== void 0) instrument.add(value, {
|
|
212
|
+
...base,
|
|
213
|
+
"ai_agent_sdk.token.type": tokenType,
|
|
214
|
+
"ai_agent_sdk.usage.source": source
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
function recordSemanticUsage(sinks, values, base) {
|
|
219
|
+
if (values === void 0) return;
|
|
220
|
+
const uncached = counter(values.inputTokens);
|
|
221
|
+
const cacheRead = counter(values.cacheReadTokens) ?? 0;
|
|
222
|
+
const cacheWrite = counter(values.cacheWriteTokens) ?? 0;
|
|
223
|
+
const output = counter(values.outputTokens);
|
|
224
|
+
if (uncached !== void 0 || counter(values.cacheReadTokens) !== void 0 || counter(values.cacheWriteTokens) !== void 0) {
|
|
225
|
+
const input = (uncached ?? 0) + cacheRead + cacheWrite;
|
|
226
|
+
if (Number.isSafeInteger(input)) sinks.genAiTokenUsage.record(input, {
|
|
227
|
+
...base,
|
|
228
|
+
"gen_ai.operation.name": "chat",
|
|
229
|
+
"gen_ai.token.type": "input",
|
|
230
|
+
"ai_agent_sdk.usage.source": "reported"
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
if (output !== void 0) sinks.genAiTokenUsage.record(output, {
|
|
234
|
+
...base,
|
|
235
|
+
"gen_ai.operation.name": "chat",
|
|
236
|
+
"gen_ai.token.type": "output",
|
|
237
|
+
"ai_agent_sdk.usage.source": "reported"
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
function recordMetrics(sinks, event) {
|
|
241
|
+
const base = metricAttributes(event);
|
|
242
|
+
for (const projection of projectMetrics(event)) {
|
|
243
|
+
const attributes = {
|
|
244
|
+
...base,
|
|
245
|
+
...projection.attributes
|
|
246
|
+
};
|
|
247
|
+
switch (projection.name) {
|
|
248
|
+
case "ai_agent_sdk.model.call.duration":
|
|
249
|
+
sinks.sdkModelDuration.record(projection.value, attributes);
|
|
250
|
+
sinks.genAiClientDuration.record(projection.value / 1e3, {
|
|
251
|
+
...base,
|
|
252
|
+
"gen_ai.operation.name": "chat"
|
|
253
|
+
});
|
|
254
|
+
break;
|
|
255
|
+
case "ai_agent_sdk.provider.attempt.duration":
|
|
256
|
+
sinks.sdkProviderDuration.record(projection.value, attributes);
|
|
257
|
+
break;
|
|
258
|
+
case "ai_agent_sdk.tool.call.duration":
|
|
259
|
+
sinks.sdkToolDuration.record(projection.value, attributes);
|
|
260
|
+
sinks.genAiToolDuration.record(projection.value / 1e3, {
|
|
261
|
+
...base,
|
|
262
|
+
"gen_ai.operation.name": "execute_tool"
|
|
263
|
+
});
|
|
264
|
+
break;
|
|
265
|
+
case "ai_agent_sdk.provider.retry": sinks.sdkProviderRetry.add(projection.value, attributes);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
if (event.name === "sdk.agent.run" && event.phase === "end") {
|
|
269
|
+
const duration = finite(event.data.durationMs);
|
|
270
|
+
if (duration !== void 0) sinks.genAiAgentDuration.record(duration / 1e3, {
|
|
271
|
+
...base,
|
|
272
|
+
"gen_ai.operation.name": "invoke_agent"
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
recordInternalUsage(sinks, event);
|
|
276
|
+
}
|
|
277
|
+
function createMetricSinks(meter) {
|
|
278
|
+
return {
|
|
279
|
+
sdkModelDuration: meter.createHistogram("ai_agent_sdk.model.call.duration", { unit: "ms" }),
|
|
280
|
+
sdkProviderDuration: meter.createHistogram("ai_agent_sdk.provider.attempt.duration", { unit: "ms" }),
|
|
281
|
+
sdkToolDuration: meter.createHistogram("ai_agent_sdk.tool.call.duration", { unit: "ms" }),
|
|
282
|
+
sdkTokenUsage: meter.createCounter("ai_agent_sdk.token.usage", { unit: "{token}" }),
|
|
283
|
+
sdkUsageCoverage: meter.createCounter("ai_agent_sdk.usage.coverage", { unit: "{call}" }),
|
|
284
|
+
sdkProviderRetry: meter.createCounter("ai_agent_sdk.provider.retry", { unit: "{retry}" }),
|
|
285
|
+
genAiClientDuration: meter.createHistogram("gen_ai.client.operation.duration", { unit: "s" }),
|
|
286
|
+
genAiTokenUsage: meter.createHistogram("gen_ai.client.token.usage", { unit: "{token}" }),
|
|
287
|
+
genAiAgentDuration: meter.createHistogram("gen_ai.invoke_agent.duration", { unit: "s" }),
|
|
288
|
+
genAiToolDuration: meter.createHistogram("gen_ai.execute_tool.duration", { unit: "s" })
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function logContext(event, states) {
|
|
292
|
+
const state = states.get(event.correlation.spanId);
|
|
293
|
+
if (state !== void 0) return state.context;
|
|
294
|
+
return trace.setSpanContext(ROOT_CONTEXT, {
|
|
295
|
+
traceId: event.correlation.traceId,
|
|
296
|
+
spanId: event.correlation.spanId,
|
|
297
|
+
traceFlags: TraceFlags.SAMPLED,
|
|
298
|
+
isRemote: false
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function emitLog(logger, event, states) {
|
|
302
|
+
if (logger === void 0) return;
|
|
303
|
+
const projected = projectLog(event);
|
|
304
|
+
if (projected === void 0) return;
|
|
305
|
+
const severityNumber = LOG_SEVERITY[projected.level];
|
|
306
|
+
const context = logContext(event, states);
|
|
307
|
+
if (logger.enabled?.({
|
|
308
|
+
context,
|
|
309
|
+
severityNumber,
|
|
310
|
+
eventName: "ai_agent_sdk.log"
|
|
311
|
+
}) === false) return;
|
|
312
|
+
logger.emit({
|
|
313
|
+
eventName: "ai_agent_sdk.log",
|
|
314
|
+
timestamp: new Date(event.occurredAt),
|
|
315
|
+
severityNumber,
|
|
316
|
+
severityText: projected.level.toUpperCase(),
|
|
317
|
+
body: projected.message,
|
|
318
|
+
attributes: {
|
|
319
|
+
...projected.fields,
|
|
320
|
+
"ai_agent_sdk.event.id": projected.eventId,
|
|
321
|
+
"ai_agent_sdk.run.id": event.correlation.runId
|
|
322
|
+
},
|
|
323
|
+
context
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
/** Build a mapping-only bridge around caller-owned OpenTelemetry API objects. */
|
|
327
|
+
function createOpenTelemetryBridge(options) {
|
|
328
|
+
if (typeof options?.tracer?.startSpan !== "function") throw new TypeError("OpenTelemetry bridge requires a tracer");
|
|
329
|
+
if (typeof options?.meter?.createHistogram !== "function" || typeof options.meter.createCounter !== "function") throw new TypeError("OpenTelemetry bridge requires a meter");
|
|
330
|
+
if (![
|
|
331
|
+
"none",
|
|
332
|
+
"metadata",
|
|
333
|
+
"redacted",
|
|
334
|
+
"full"
|
|
335
|
+
].includes(options.content ?? "none")) throw new TypeError("OpenTelemetry bridge content policy is invalid");
|
|
336
|
+
if (options.logger !== void 0 && typeof options.logger.emit !== "function") throw new TypeError("OpenTelemetry bridge logger is invalid");
|
|
337
|
+
const states = /* @__PURE__ */ new Map();
|
|
338
|
+
const diagnostics = [];
|
|
339
|
+
const sinks = createMetricSinks(options.meter);
|
|
340
|
+
const recordDiagnostic = (error) => {
|
|
341
|
+
const source = safeErrorRecord(error);
|
|
342
|
+
const safe = deepFreeze({
|
|
343
|
+
type: source.type,
|
|
344
|
+
message: source.code === OBSERVATION_ERROR_CODES.OTEL_PROVIDER_UNCONFIGURED ? "OpenTelemetry tracer provider is unconfigured or returned an invalid context" : "OpenTelemetry bridge API call failed",
|
|
345
|
+
...source.code === void 0 ? {} : { code: source.code }
|
|
346
|
+
});
|
|
347
|
+
diagnostics.push(safe);
|
|
348
|
+
if (diagnostics.length > MAX_DIAGNOSTICS) diagnostics.shift();
|
|
349
|
+
try {
|
|
350
|
+
options.onDiagnostic?.(safe);
|
|
351
|
+
} catch {}
|
|
352
|
+
};
|
|
353
|
+
const finish = (state) => {
|
|
354
|
+
if (state.finished || state.endRequested === void 0) return;
|
|
355
|
+
state.finished = true;
|
|
356
|
+
states.delete(state.sdkSpanId);
|
|
357
|
+
const request = state.endRequested;
|
|
358
|
+
const success = request.status === "success";
|
|
359
|
+
let failure;
|
|
360
|
+
try {
|
|
361
|
+
state.span.setStatus({
|
|
362
|
+
code: success ? SpanStatusCode.OK : SpanStatusCode.ERROR,
|
|
363
|
+
...success ? {} : { message: request.status }
|
|
364
|
+
});
|
|
365
|
+
} catch (error) {
|
|
366
|
+
failure = error;
|
|
367
|
+
}
|
|
368
|
+
try {
|
|
369
|
+
state.span.end(new Date(request.endedAt));
|
|
370
|
+
} catch (error) {
|
|
371
|
+
failure ??= error;
|
|
372
|
+
}
|
|
373
|
+
if (failure !== void 0) throw failure;
|
|
374
|
+
};
|
|
375
|
+
const openSpanImpl = (input) => {
|
|
376
|
+
const initial = initialSpan(input);
|
|
377
|
+
const parentContext = explicitParent(input, states);
|
|
378
|
+
const span = options.tracer.startSpan(initial.name, {
|
|
379
|
+
kind: initial.kind,
|
|
380
|
+
attributes: initial.attributes,
|
|
381
|
+
startTime: new Date(input.startedAt),
|
|
382
|
+
root: input.parent === void 0
|
|
383
|
+
}, parentContext);
|
|
384
|
+
let spanContext;
|
|
385
|
+
try {
|
|
386
|
+
spanContext = span.spanContext();
|
|
387
|
+
} catch (error) {
|
|
388
|
+
try {
|
|
389
|
+
span.end();
|
|
390
|
+
} catch {}
|
|
391
|
+
throw error;
|
|
392
|
+
}
|
|
393
|
+
if (!isSpanContextValid(spanContext) || !isTraceId(spanContext.traceId) || !isSpanId(spanContext.spanId) || !Number.isInteger(spanContext.traceFlags) || spanContext.traceFlags < 0 || spanContext.traceFlags > 255 || input.parent !== void 0 && spanContext.traceId !== input.parent.traceId || states.has(spanContext.spanId)) {
|
|
394
|
+
try {
|
|
395
|
+
span.end();
|
|
396
|
+
} catch {}
|
|
397
|
+
throw new OpenTelemetryBridgeError("supplied tracer returned an invalid or disconnected span context");
|
|
398
|
+
}
|
|
399
|
+
const correlation = freezeCorrelation({
|
|
400
|
+
traceId: spanContext.traceId,
|
|
401
|
+
spanId: spanContext.spanId,
|
|
402
|
+
parentSpanId: input.parent?.spanId ?? null,
|
|
403
|
+
runId: input.runId,
|
|
404
|
+
...input.correlation ?? {}
|
|
405
|
+
});
|
|
406
|
+
const state = {
|
|
407
|
+
span,
|
|
408
|
+
spanContext,
|
|
409
|
+
context: trace.setSpan(parentContext, span),
|
|
410
|
+
sdkSpanId: spanContext.spanId,
|
|
411
|
+
terminalSeen: false,
|
|
412
|
+
finished: false
|
|
413
|
+
};
|
|
414
|
+
states.set(state.sdkSpanId, state);
|
|
415
|
+
let ended = false;
|
|
416
|
+
return Object.freeze({
|
|
417
|
+
correlation,
|
|
418
|
+
traceparent: traceparent(spanContext),
|
|
419
|
+
end(status, endedAt) {
|
|
420
|
+
if (ended) return;
|
|
421
|
+
ended = true;
|
|
422
|
+
state.endRequested = {
|
|
423
|
+
status,
|
|
424
|
+
endedAt
|
|
425
|
+
};
|
|
426
|
+
if (state.terminalSeen) {
|
|
427
|
+
try {
|
|
428
|
+
finish(state);
|
|
429
|
+
} catch (error) {
|
|
430
|
+
recordDiagnostic(error);
|
|
431
|
+
throw error;
|
|
432
|
+
}
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
queueMicrotask(() => {
|
|
436
|
+
try {
|
|
437
|
+
finish(state);
|
|
438
|
+
} catch (error) {
|
|
439
|
+
recordDiagnostic(error);
|
|
440
|
+
}
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
});
|
|
444
|
+
};
|
|
445
|
+
const openSpan = (input) => {
|
|
446
|
+
try {
|
|
447
|
+
return openSpanImpl(input);
|
|
448
|
+
} catch (error) {
|
|
449
|
+
recordDiagnostic(error);
|
|
450
|
+
throw error;
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
const processor = Object.freeze({
|
|
454
|
+
id: "otel",
|
|
455
|
+
transform(event) {
|
|
456
|
+
try {
|
|
457
|
+
const state = states.get(event.correlation.spanId);
|
|
458
|
+
if (state !== void 0) {
|
|
459
|
+
applySpanEvent(state, event, options.content === "full");
|
|
460
|
+
if (event.phase === "end") {
|
|
461
|
+
state.terminalSeen = true;
|
|
462
|
+
finish(state);
|
|
463
|
+
} else if (event.phase === "point") state.span.addEvent(event.name, statusAttributes(event), new Date(event.occurredAt));
|
|
464
|
+
}
|
|
465
|
+
recordMetrics(sinks, event);
|
|
466
|
+
emitLog(options.logger, event, states);
|
|
467
|
+
return event;
|
|
468
|
+
} catch (error) {
|
|
469
|
+
recordDiagnostic(error);
|
|
470
|
+
throw error;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
});
|
|
474
|
+
return Object.freeze({
|
|
475
|
+
openSpan,
|
|
476
|
+
processor,
|
|
477
|
+
diagnostics: () => deepFreeze([...diagnostics])
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
//#endregion
|
|
482
|
+
export { OTEL_SEMANTIC_CONVENTIONS_COMMIT, OpenTelemetryBridgeError, createOpenTelemetryBridge };
|
|
483
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/bridge.ts"],"sourcesContent":["import {\n ROOT_CONTEXT,\n SpanKind,\n SpanStatusCode,\n TraceFlags,\n isSpanContextValid,\n trace,\n type Attributes,\n type Context,\n type Counter,\n type Histogram,\n type Meter,\n type Span,\n type SpanContext,\n type Tracer,\n} from '@opentelemetry/api'\nimport {\n OBSERVATION_ERROR_CODES,\n deepFreeze,\n freezeCorrelation,\n isSpanId,\n isTraceId,\n safeErrorRecord,\n type ObservationEvent,\n type ObservationPort,\n type ObservationSpan,\n type OpenObservationSpanInput,\n type OperationStatus,\n type SafeErrorRecord,\n} from '@alvin0/ai-agent-sdk-core'\nimport {\n projectLog,\n projectMetrics,\n type LogLevel,\n type ObservationContentPolicy,\n type ObservationProcessor,\n} from '@alvin0/ai-agent-sdk-core/observability'\n\nexport const OTEL_SEMANTIC_CONVENTIONS_COMMIT = '5ca9052bc796ef1e497200b1d558fd87a201f335'\n\nconst MAX_DIAGNOSTICS = 100\nconst LOG_SEVERITY: Readonly<Record<LogLevel, number>> = Object.freeze({\n trace: 1,\n debug: 5,\n info: 9,\n warn: 13,\n error: 17,\n fatal: 21,\n})\n\nexport class OpenTelemetryBridgeError extends Error {\n override readonly name = 'OpenTelemetryBridgeError'\n readonly code = OBSERVATION_ERROR_CODES.OTEL_PROVIDER_UNCONFIGURED\n}\n\nexport interface OpenTelemetryBridgeOptions {\n readonly tracer: Tracer\n readonly meter: Meter\n readonly logger?: OpenTelemetryLogger\n /** Exact prompt/completion span attributes remain disabled unless this is explicitly `full`. */\n readonly content?: ObservationContentPolicy\n readonly onDiagnostic?: (error: SafeErrorRecord) => void\n}\n\nexport interface OpenTelemetryBridge {\n readonly openSpan: ObservationPort['openSpan']\n readonly processor: ObservationProcessor\n diagnostics(): readonly SafeErrorRecord[]\n}\n\n/** Structural log API so consumers that omit logging need not install the optional logs peer. */\nexport interface OpenTelemetryLogger {\n emit(record: OpenTelemetryLogRecord): void\n enabled?(options?: OpenTelemetryLogEnabledOptions): boolean\n}\n\nexport interface OpenTelemetryLogRecord {\n readonly context?: Context\n readonly [key: string]: unknown\n}\n\nexport interface OpenTelemetryLogEnabledOptions {\n readonly context?: Context\n readonly [key: string]: unknown\n}\n\ninterface SpanState {\n readonly span: Span\n readonly context: Context\n readonly spanContext: SpanContext\n readonly sdkSpanId: string\n endRequested?: { readonly status: OperationStatus; readonly endedAt: string }\n terminalSeen: boolean\n finished: boolean\n}\n\ninterface MetricSinks {\n readonly sdkModelDuration: Histogram\n readonly sdkProviderDuration: Histogram\n readonly sdkToolDuration: Histogram\n readonly sdkTokenUsage: Counter\n readonly sdkUsageCoverage: Counter\n readonly sdkProviderRetry: Counter\n readonly genAiClientDuration: Histogram\n readonly genAiTokenUsage: Histogram\n readonly genAiAgentDuration: Histogram\n readonly genAiToolDuration: Histogram\n}\n\nfunction object(value: unknown): Readonly<Record<string, unknown>> | undefined {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? value as Readonly<Record<string, unknown>>\n : undefined\n}\n\nfunction finite(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isFinite(value) ? value : undefined\n}\n\nfunction counter(value: unknown): number | undefined {\n return typeof value === 'number' && Number.isSafeInteger(value) && value >= 0 ? value : undefined\n}\n\nfunction string(value: unknown): string | undefined {\n return typeof value === 'string' && value.length > 0 ? value : undefined\n}\n\nfunction initialSpan(input: OpenObservationSpanInput): {\n readonly name: string\n readonly kind: SpanKind\n readonly attributes: Attributes\n} {\n const operation = input.name === 'sdk.agent.run'\n ? 'invoke_agent'\n : input.name === 'sdk.model.call'\n ? 'chat'\n : input.name === 'sdk.tool.call'\n ? 'execute_tool'\n : undefined\n return {\n name: operation ?? input.name,\n kind: input.name === 'sdk.model.call' || input.name === 'sdk.provider.attempt'\n ? SpanKind.CLIENT\n : SpanKind.INTERNAL,\n attributes: {\n 'ai_agent_sdk.operation.name': input.name,\n 'ai_agent_sdk.run.id': input.runId,\n ...operation === undefined ? {} : { 'gen_ai.operation.name': operation },\n },\n }\n}\n\nfunction explicitParent(input: OpenObservationSpanInput, states: Map<string, SpanState>): Context {\n if (input.parent === undefined) return ROOT_CONTEXT\n const owned = states.get(input.parent.spanId)\n if (owned !== undefined) return owned.context\n return trace.setSpanContext(ROOT_CONTEXT, {\n traceId: input.parent.traceId,\n spanId: input.parent.spanId,\n traceFlags: TraceFlags.SAMPLED,\n isRemote: false,\n })\n}\n\nfunction traceparent(context: SpanContext): string {\n const flags = (context.traceFlags & 0xff).toString(16).padStart(2, '0')\n return `00-${context.traceId}-${context.spanId}-${flags}`\n}\n\nfunction statusAttributes(event: ObservationEvent): Attributes {\n const status = string(event.data.status)\n const error = object(event.data.error)\n const errorType = string(error?.code) ?? string(error?.type) ?? (status === 'success' ? undefined : status)\n return {\n ...status === undefined ? {} : { 'ai_agent_sdk.operation.status': status },\n ...errorType === undefined ? {} : { 'error.type': errorType },\n }\n}\n\nfunction contentAttributes(event: ObservationEvent): Attributes {\n const input = event.data.inputMessages ?? event.data.prompt\n const output = event.data.outputMessages ?? event.data.completion ?? event.data.response\n const render = (value: unknown): string | undefined => {\n if (value === undefined) return undefined\n return typeof value === 'string' ? value : JSON.stringify(value)\n }\n const renderedInput = render(input)\n const renderedOutput = render(output)\n return {\n ...renderedInput === undefined ? {} : { 'gen_ai.input.messages': renderedInput },\n ...renderedOutput === undefined ? {} : { 'gen_ai.output.messages': renderedOutput },\n }\n}\n\nfunction applySpanEvent(state: SpanState, event: ObservationEvent, allowContent: boolean): void {\n const span = state.span\n const attributes: Attributes = {\n 'ai_agent_sdk.event.id': event.eventId,\n 'ai_agent_sdk.event.sequence': event.sequence,\n ...statusAttributes(event),\n }\n if (event.correlation.conversationId !== undefined) {\n attributes['gen_ai.conversation.id'] = event.correlation.conversationId\n }\n if (event.name === 'sdk.agent.run') {\n const agentId = string(event.data.agentId)\n if (agentId !== undefined) attributes['gen_ai.agent.id'] = agentId\n span.updateName('invoke_agent')\n } else if (event.name === 'sdk.model.call') {\n const provider = string(event.data.provider)\n const model = string(event.data.model)\n if (provider !== undefined) attributes['gen_ai.provider.name'] = provider\n if (model !== undefined) {\n attributes[event.phase === 'start' ? 'gen_ai.request.model' : 'gen_ai.response.model'] = model\n span.updateName(`chat ${model}`)\n }\n if (allowContent) Object.assign(attributes, contentAttributes(event))\n if (event.phase === 'end') Object.assign(attributes, reportedUsageAttributes(event))\n } else if (event.name === 'sdk.provider.attempt') {\n const provider = string(event.data.provider)\n const model = string(event.data.model)\n const method = string(event.data.method)\n const origin = string(event.data.origin)\n if (provider !== undefined) attributes['gen_ai.provider.name'] = provider\n if (model !== undefined) attributes['gen_ai.request.model'] = model\n if (method !== undefined) attributes['http.request.method'] = method\n if (counter(event.data.httpStatus) !== undefined) attributes['http.response.status_code'] = event.data.httpStatus as number\n if (origin !== undefined) {\n try {\n const url = new URL(origin)\n attributes['server.address'] = url.hostname\n attributes['url.scheme'] = url.protocol.slice(0, -1)\n if (url.port.length > 0) attributes['server.port'] = Number(url.port)\n } catch { /* origin was already validated upstream; omit it if a custom event is malformed */ }\n }\n } else if (event.name === 'sdk.tool.call') {\n const explicitName = string(event.data.toolName)\n const legacyName = string(event.data.name)?.replace(/^execute_tool\\s+/, '')\n const toolName = explicitName ?? legacyName\n if (toolName !== undefined) {\n attributes['gen_ai.tool.name'] = toolName\n span.updateName(`execute_tool ${toolName}`)\n }\n if (event.correlation.toolCallId !== undefined) {\n attributes['gen_ai.tool.call.id'] = event.correlation.toolCallId\n }\n }\n span.setAttributes(attributes)\n}\n\nfunction reportedUsage(event: ObservationEvent): Readonly<Record<string, unknown>> | undefined {\n const direct = object(event.data.reported)\n if (direct !== undefined) return direct\n return object(object(event.data.usageReport)?.reported) ?? object(object(event.data.usage)?.reported)\n}\n\nfunction estimatedUsage(event: ObservationEvent): Readonly<Record<string, unknown>> | undefined {\n return object(object(event.data.usageReport)?.estimated) ?? object(object(event.data.usage)?.estimated)\n}\n\nfunction reportedUsageAttributes(event: ObservationEvent): Attributes {\n const usage = reportedUsage(event)\n if (usage === undefined) return {}\n const uncached = counter(usage.inputTokens)\n const cacheRead = counter(usage.cacheReadTokens)\n const cacheWrite = counter(usage.cacheWriteTokens)\n const output = counter(usage.outputTokens)\n const inputParts = [uncached, cacheRead, cacheWrite].filter((value): value is number => value !== undefined)\n const input = inputParts.length === 0 ? undefined : inputParts.reduce((sum, value) => sum + value, 0)\n return {\n ...input === undefined || !Number.isSafeInteger(input) ? {} : { 'gen_ai.usage.input_tokens': input },\n ...output === undefined ? {} : { 'gen_ai.usage.output_tokens': output },\n ...cacheRead === undefined ? {} : { 'gen_ai.usage.cache_read.input_tokens': cacheRead },\n ...cacheWrite === undefined ? {} : { 'gen_ai.usage.cache_write.input_tokens': cacheWrite },\n ...counter(usage.reasoningTokens) === undefined\n ? {}\n : { 'gen_ai.usage.reasoning.output_tokens': usage.reasoningTokens as number },\n }\n}\n\nfunction metricAttributes(event: ObservationEvent): Attributes {\n const error = object(event.data.error)\n return {\n ...string(event.data.provider) === undefined ? {} : { 'gen_ai.provider.name': event.data.provider as string },\n ...string(event.data.operation) === undefined ? {} : { 'ai_agent_sdk.operation': event.data.operation as string },\n ...string(event.data.status) === undefined ? {} : { 'ai_agent_sdk.status': event.data.status as string },\n ...string(error?.code) === undefined ? {} : { 'error.type': error?.code as string },\n }\n}\n\nfunction recordInternalUsage(sinks: MetricSinks, event: ObservationEvent): void {\n if (event.phase !== 'end') return\n const reported = reportedUsage(event)\n const estimated = estimatedUsage(event)\n const base = metricAttributes(event)\n if (event.name === 'sdk.model.call') {\n recordCounterSet(sinks.sdkTokenUsage, reported, 'reported', base)\n recordCounterSet(sinks.sdkTokenUsage, estimated, 'estimated', base)\n const coverage = string(event.data.coverage) ?? string(object(event.data.usageReport)?.coverage)\n if (coverage !== undefined) sinks.sdkUsageCoverage.add(1, { ...base, 'ai_agent_sdk.usage.coverage': coverage })\n recordSemanticUsage(sinks, reported, base)\n } else if (event.name === 'sdk.agent.run') {\n recordCounterSet(sinks.sdkTokenUsage, estimated, 'estimated', base)\n const coverage = object(object(event.data.usage)?.coverage)\n for (const state of ['complete', 'partial', 'estimated', 'missing', 'notApplicable'] as const) {\n const value = counter(coverage?.[state])\n if (value !== undefined && value > 0) sinks.sdkUsageCoverage.add(value, {\n ...base,\n 'ai_agent_sdk.usage.coverage': state === 'notApplicable' ? 'not-applicable' : state,\n })\n }\n }\n}\n\nfunction recordCounterSet(\n instrument: Counter,\n values: Readonly<Record<string, unknown>> | undefined,\n source: 'reported' | 'estimated',\n base: Attributes,\n): void {\n if (values === undefined) return\n for (const [key, tokenType] of [\n ['inputTokens', 'input'],\n ['outputTokens', 'output'],\n ['cacheReadTokens', 'cache-read'],\n ['cacheWriteTokens', 'cache-write'],\n ['reasoningTokens', 'reasoning'],\n ] as const) {\n const value = counter(values[key])\n if (value !== undefined) instrument.add(value, {\n ...base,\n 'ai_agent_sdk.token.type': tokenType,\n 'ai_agent_sdk.usage.source': source,\n })\n }\n}\n\nfunction recordSemanticUsage(\n sinks: MetricSinks,\n values: Readonly<Record<string, unknown>> | undefined,\n base: Attributes,\n): void {\n if (values === undefined) return\n const uncached = counter(values.inputTokens)\n const cacheRead = counter(values.cacheReadTokens) ?? 0\n const cacheWrite = counter(values.cacheWriteTokens) ?? 0\n const output = counter(values.outputTokens)\n const hasInput = uncached !== undefined\n || counter(values.cacheReadTokens) !== undefined\n || counter(values.cacheWriteTokens) !== undefined\n if (hasInput) {\n const input = (uncached ?? 0) + cacheRead + cacheWrite\n if (Number.isSafeInteger(input)) sinks.genAiTokenUsage.record(input, {\n ...base,\n 'gen_ai.operation.name': 'chat',\n 'gen_ai.token.type': 'input',\n 'ai_agent_sdk.usage.source': 'reported',\n })\n }\n if (output !== undefined) sinks.genAiTokenUsage.record(output, {\n ...base,\n 'gen_ai.operation.name': 'chat',\n 'gen_ai.token.type': 'output',\n 'ai_agent_sdk.usage.source': 'reported',\n })\n}\n\nfunction recordMetrics(sinks: MetricSinks, event: ObservationEvent): void {\n const base = metricAttributes(event)\n for (const projection of projectMetrics(event)) {\n const attributes = { ...base, ...projection.attributes } as Attributes\n switch (projection.name) {\n case 'ai_agent_sdk.model.call.duration':\n sinks.sdkModelDuration.record(projection.value, attributes)\n sinks.genAiClientDuration.record(projection.value / 1_000, {\n ...base, 'gen_ai.operation.name': 'chat',\n })\n break\n case 'ai_agent_sdk.provider.attempt.duration': sinks.sdkProviderDuration.record(projection.value, attributes); break\n case 'ai_agent_sdk.tool.call.duration':\n sinks.sdkToolDuration.record(projection.value, attributes)\n sinks.genAiToolDuration.record(projection.value / 1_000, {\n ...base, 'gen_ai.operation.name': 'execute_tool',\n })\n break\n case 'ai_agent_sdk.provider.retry': sinks.sdkProviderRetry.add(projection.value, attributes); break\n // Usage is mapped from the canonical reported/estimated shapes below so\n // model, attempt, and run aggregates cannot double count one call.\n case 'ai_agent_sdk.token.usage':\n case 'ai_agent_sdk.usage.coverage': break\n }\n }\n if (event.name === 'sdk.agent.run' && event.phase === 'end') {\n const duration = finite(event.data.durationMs)\n if (duration !== undefined) sinks.genAiAgentDuration.record(duration / 1_000, {\n ...base, 'gen_ai.operation.name': 'invoke_agent',\n })\n }\n recordInternalUsage(sinks, event)\n}\n\nfunction createMetricSinks(meter: Meter): MetricSinks {\n return {\n sdkModelDuration: meter.createHistogram('ai_agent_sdk.model.call.duration', { unit: 'ms' }),\n sdkProviderDuration: meter.createHistogram('ai_agent_sdk.provider.attempt.duration', { unit: 'ms' }),\n sdkToolDuration: meter.createHistogram('ai_agent_sdk.tool.call.duration', { unit: 'ms' }),\n sdkTokenUsage: meter.createCounter('ai_agent_sdk.token.usage', { unit: '{token}' }),\n sdkUsageCoverage: meter.createCounter('ai_agent_sdk.usage.coverage', { unit: '{call}' }),\n sdkProviderRetry: meter.createCounter('ai_agent_sdk.provider.retry', { unit: '{retry}' }),\n genAiClientDuration: meter.createHistogram('gen_ai.client.operation.duration', { unit: 's' }),\n genAiTokenUsage: meter.createHistogram('gen_ai.client.token.usage', { unit: '{token}' }),\n genAiAgentDuration: meter.createHistogram('gen_ai.invoke_agent.duration', { unit: 's' }),\n genAiToolDuration: meter.createHistogram('gen_ai.execute_tool.duration', { unit: 's' }),\n }\n}\n\nfunction logContext(event: ObservationEvent, states: Map<string, SpanState>): Context {\n const state = states.get(event.correlation.spanId)\n if (state !== undefined) return state.context\n return trace.setSpanContext(ROOT_CONTEXT, {\n traceId: event.correlation.traceId,\n spanId: event.correlation.spanId,\n traceFlags: TraceFlags.SAMPLED,\n isRemote: false,\n })\n}\n\nfunction emitLog(logger: OpenTelemetryLogger | undefined, event: ObservationEvent, states: Map<string, SpanState>): void {\n if (logger === undefined) return\n const projected = projectLog(event)\n if (projected === undefined) return\n const severityNumber = LOG_SEVERITY[projected.level]\n const context = logContext(event, states)\n if (logger.enabled?.({ context, severityNumber, eventName: 'ai_agent_sdk.log' }) === false) return\n logger.emit({\n eventName: 'ai_agent_sdk.log',\n timestamp: new Date(event.occurredAt),\n severityNumber,\n severityText: projected.level.toUpperCase(),\n body: projected.message,\n attributes: {\n ...projected.fields,\n 'ai_agent_sdk.event.id': projected.eventId,\n 'ai_agent_sdk.run.id': event.correlation.runId,\n },\n context,\n })\n}\n\n/** Build a mapping-only bridge around caller-owned OpenTelemetry API objects. */\nexport function createOpenTelemetryBridge(options: OpenTelemetryBridgeOptions): OpenTelemetryBridge {\n if (typeof options?.tracer?.startSpan !== 'function') throw new TypeError('OpenTelemetry bridge requires a tracer')\n if (typeof options?.meter?.createHistogram !== 'function' || typeof options.meter.createCounter !== 'function') {\n throw new TypeError('OpenTelemetry bridge requires a meter')\n }\n if (!['none', 'metadata', 'redacted', 'full'].includes(options.content ?? 'none')) {\n throw new TypeError('OpenTelemetry bridge content policy is invalid')\n }\n if (options.logger !== undefined && typeof options.logger.emit !== 'function') {\n throw new TypeError('OpenTelemetry bridge logger is invalid')\n }\n const states = new Map<string, SpanState>()\n const diagnostics: SafeErrorRecord[] = []\n const sinks = createMetricSinks(options.meter)\n const recordDiagnostic = (error: unknown): void => {\n const source = safeErrorRecord(error)\n const safe = deepFreeze({\n type: source.type,\n message: source.code === OBSERVATION_ERROR_CODES.OTEL_PROVIDER_UNCONFIGURED\n ? 'OpenTelemetry tracer provider is unconfigured or returned an invalid context'\n : 'OpenTelemetry bridge API call failed',\n ...source.code === undefined ? {} : { code: source.code },\n })\n diagnostics.push(safe)\n if (diagnostics.length > MAX_DIAGNOSTICS) diagnostics.shift()\n try { options.onDiagnostic?.(safe) } catch { /* caller diagnostics are contained */ }\n }\n const finish = (state: SpanState): void => {\n if (state.finished || state.endRequested === undefined) return\n state.finished = true\n states.delete(state.sdkSpanId)\n const request = state.endRequested\n const success = request.status === 'success'\n let failure: unknown\n try {\n state.span.setStatus({\n code: success ? SpanStatusCode.OK : SpanStatusCode.ERROR,\n ...success ? {} : { message: request.status },\n })\n } catch (error) { failure = error }\n try { state.span.end(new Date(request.endedAt)) }\n catch (error) { failure ??= error }\n if (failure !== undefined) throw failure\n }\n const openSpanImpl = (input: OpenObservationSpanInput): ObservationSpan => {\n const initial = initialSpan(input)\n const parentContext = explicitParent(input, states)\n const span = options.tracer.startSpan(initial.name, {\n kind: initial.kind,\n attributes: initial.attributes,\n startTime: new Date(input.startedAt),\n root: input.parent === undefined,\n }, parentContext)\n let spanContext: SpanContext\n try { spanContext = span.spanContext() }\n catch (error) {\n try { span.end() } catch { /* original failure remains authoritative */ }\n throw error\n }\n if (!isSpanContextValid(spanContext) || !isTraceId(spanContext.traceId) || !isSpanId(spanContext.spanId)\n || !Number.isInteger(spanContext.traceFlags) || spanContext.traceFlags < 0 || spanContext.traceFlags > 255\n || (input.parent !== undefined && spanContext.traceId !== input.parent.traceId)\n || states.has(spanContext.spanId)) {\n try { span.end() } catch { /* invalid identity remains authoritative */ }\n const error = new OpenTelemetryBridgeError('supplied tracer returned an invalid or disconnected span context')\n throw error\n }\n const correlation = freezeCorrelation({\n traceId: spanContext.traceId,\n spanId: spanContext.spanId,\n parentSpanId: input.parent?.spanId ?? null,\n runId: input.runId,\n ...(input.correlation ?? {}),\n })\n const state: SpanState = {\n span,\n spanContext,\n context: trace.setSpan(parentContext, span),\n sdkSpanId: spanContext.spanId,\n terminalSeen: false,\n finished: false,\n }\n states.set(state.sdkSpanId, state)\n let ended = false\n return Object.freeze({\n correlation,\n traceparent: traceparent(spanContext),\n end(status: OperationStatus, endedAt: string): void {\n if (ended) return\n ended = true\n state.endRequested = { status, endedAt }\n if (state.terminalSeen) {\n try { finish(state) }\n catch (error) { recordDiagnostic(error); throw error }\n return\n }\n queueMicrotask(() => {\n try { finish(state) } catch (error) { recordDiagnostic(error) }\n })\n },\n })\n }\n const openSpan = (input: OpenObservationSpanInput): ObservationSpan => {\n try { return openSpanImpl(input) }\n catch (error) { recordDiagnostic(error); throw error }\n }\n const processor: ObservationProcessor = Object.freeze({\n id: 'otel',\n transform(event: ObservationEvent): ObservationEvent {\n try {\n const state = states.get(event.correlation.spanId)\n if (state !== undefined) {\n applySpanEvent(state, event, options.content === 'full')\n if (event.phase === 'end') {\n state.terminalSeen = true\n finish(state)\n } else if (event.phase === 'point') {\n state.span.addEvent(event.name, statusAttributes(event), new Date(event.occurredAt))\n }\n }\n recordMetrics(sinks, event)\n emitLog(options.logger, event, states)\n return event\n } catch (error) {\n recordDiagnostic(error)\n throw error\n }\n },\n })\n return Object.freeze({\n openSpan,\n processor,\n diagnostics: () => deepFreeze([...diagnostics]),\n })\n}\n"],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@alvin0/ai-agent-sdk-observability-otel",
|
|
3
|
+
"author": {
|
|
4
|
+
"name": "alvin0 - chaulamdinhai",
|
|
5
|
+
"email": "chaulamdinhai@gmail.com"
|
|
6
|
+
},
|
|
7
|
+
"version": "0.1.0",
|
|
8
|
+
"description": "Universal OpenTelemetry API bridge for correlated ai-agent-sdk spans, metrics, and logs",
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/alvin0/ai-agent-sdk.git",
|
|
13
|
+
"directory": "packages/observability-otel"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/alvin0/ai-agent-sdk/tree/main/packages/observability-otel#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/alvin0/ai-agent-sdk/issues"
|
|
18
|
+
},
|
|
19
|
+
"type": "module",
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"files": [
|
|
22
|
+
"dist",
|
|
23
|
+
"README.md",
|
|
24
|
+
"LICENSE"
|
|
25
|
+
],
|
|
26
|
+
"main": "./dist/index.js",
|
|
27
|
+
"types": "./dist/index.d.ts",
|
|
28
|
+
"exports": {
|
|
29
|
+
".": {
|
|
30
|
+
"types": "./dist/index.d.ts",
|
|
31
|
+
"import": "./dist/index.js",
|
|
32
|
+
"default": "./dist/index.js"
|
|
33
|
+
},
|
|
34
|
+
"./package.json": "./package.json"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public",
|
|
38
|
+
"provenance": true
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@alvin0/ai-agent-sdk-core": "^0.1.0",
|
|
42
|
+
"@opentelemetry/api": "^1.9.1",
|
|
43
|
+
"@opentelemetry/api-logs": "^0.221.0"
|
|
44
|
+
},
|
|
45
|
+
"peerDependenciesMeta": {
|
|
46
|
+
"@opentelemetry/api-logs": {
|
|
47
|
+
"optional": true
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"@alvin0/ai-agent-sdk-core": "^0.1.0",
|
|
52
|
+
"@arethetypeswrong/cli": "0.18.5",
|
|
53
|
+
"@opentelemetry/api": "1.9.1",
|
|
54
|
+
"@opentelemetry/api-logs": "0.221.0",
|
|
55
|
+
"playwright": "1.62.1",
|
|
56
|
+
"publint": "0.3.24",
|
|
57
|
+
"tsdown": "0.22.14",
|
|
58
|
+
"typescript": "7.0.2",
|
|
59
|
+
"vitest": "4.1.11",
|
|
60
|
+
"wrangler": "4.127.1"
|
|
61
|
+
},
|
|
62
|
+
"aiAgentSdk": {
|
|
63
|
+
"runtime": "universal",
|
|
64
|
+
"coreApi": 1,
|
|
65
|
+
"roles": [
|
|
66
|
+
"observation-processor"
|
|
67
|
+
]
|
|
68
|
+
},
|
|
69
|
+
"scripts": {
|
|
70
|
+
"build": "tsdown",
|
|
71
|
+
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true});require('node:fs').rmSync('artifacts',{recursive:true,force:true})\"",
|
|
72
|
+
"typecheck": "tsc --noEmit",
|
|
73
|
+
"test": "vitest run --config vitest.config.ts",
|
|
74
|
+
"pack": "pnpm pack --pack-destination artifacts",
|
|
75
|
+
"test:pack": "node scripts/test-packed.mts",
|
|
76
|
+
"check:publint": "publint",
|
|
77
|
+
"check:types": "attw --profile esm-only --pack ."
|
|
78
|
+
}
|
|
79
|
+
}
|