@livekit/agents 1.0.22 → 1.0.23
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/inference/api_protos.cjs +2 -2
- package/dist/inference/api_protos.cjs.map +1 -1
- package/dist/inference/api_protos.d.cts +16 -16
- package/dist/inference/api_protos.d.ts +16 -16
- package/dist/inference/api_protos.js +2 -2
- package/dist/inference/api_protos.js.map +1 -1
- package/dist/ipc/job_proc_lazy_main.cjs +35 -1
- package/dist/ipc/job_proc_lazy_main.cjs.map +1 -1
- package/dist/ipc/job_proc_lazy_main.js +13 -1
- package/dist/ipc/job_proc_lazy_main.js.map +1 -1
- package/dist/job.cjs +52 -6
- package/dist/job.cjs.map +1 -1
- package/dist/job.d.cts +2 -0
- package/dist/job.d.ts +2 -0
- package/dist/job.d.ts.map +1 -1
- package/dist/job.js +52 -6
- package/dist/job.js.map +1 -1
- package/dist/llm/llm.cjs +38 -3
- package/dist/llm/llm.cjs.map +1 -1
- package/dist/llm/llm.d.cts +1 -0
- package/dist/llm/llm.d.ts +1 -0
- package/dist/llm/llm.d.ts.map +1 -1
- package/dist/llm/llm.js +38 -3
- package/dist/llm/llm.js.map +1 -1
- package/dist/log.cjs +34 -10
- package/dist/log.cjs.map +1 -1
- package/dist/log.d.cts +7 -0
- package/dist/log.d.ts +7 -0
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +34 -11
- package/dist/log.js.map +1 -1
- package/dist/telemetry/index.cjs +23 -2
- package/dist/telemetry/index.cjs.map +1 -1
- package/dist/telemetry/index.d.cts +4 -1
- package/dist/telemetry/index.d.ts +4 -1
- package/dist/telemetry/index.d.ts.map +1 -1
- package/dist/telemetry/index.js +27 -2
- package/dist/telemetry/index.js.map +1 -1
- package/dist/telemetry/logging.cjs +65 -0
- package/dist/telemetry/logging.cjs.map +1 -0
- package/dist/telemetry/logging.d.cts +21 -0
- package/dist/telemetry/logging.d.ts +21 -0
- package/dist/telemetry/logging.d.ts.map +1 -0
- package/dist/telemetry/logging.js +40 -0
- package/dist/telemetry/logging.js.map +1 -0
- package/dist/telemetry/otel_http_exporter.cjs +144 -0
- package/dist/telemetry/otel_http_exporter.cjs.map +1 -0
- package/dist/telemetry/otel_http_exporter.d.cts +62 -0
- package/dist/telemetry/otel_http_exporter.d.ts +62 -0
- package/dist/telemetry/otel_http_exporter.d.ts.map +1 -0
- package/dist/telemetry/otel_http_exporter.js +120 -0
- package/dist/telemetry/otel_http_exporter.js.map +1 -0
- package/dist/telemetry/pino_otel_transport.cjs +217 -0
- package/dist/telemetry/pino_otel_transport.cjs.map +1 -0
- package/dist/telemetry/pino_otel_transport.d.cts +58 -0
- package/dist/telemetry/pino_otel_transport.d.ts +58 -0
- package/dist/telemetry/pino_otel_transport.d.ts.map +1 -0
- package/dist/telemetry/pino_otel_transport.js +189 -0
- package/dist/telemetry/pino_otel_transport.js.map +1 -0
- package/dist/telemetry/traces.cjs +225 -16
- package/dist/telemetry/traces.cjs.map +1 -1
- package/dist/telemetry/traces.d.cts +17 -0
- package/dist/telemetry/traces.d.ts +17 -0
- package/dist/telemetry/traces.d.ts.map +1 -1
- package/dist/telemetry/traces.js +211 -14
- package/dist/telemetry/traces.js.map +1 -1
- package/dist/tts/tts.cjs +62 -5
- package/dist/tts/tts.cjs.map +1 -1
- package/dist/tts/tts.d.cts +2 -0
- package/dist/tts/tts.d.ts +2 -0
- package/dist/tts/tts.d.ts.map +1 -1
- package/dist/tts/tts.js +62 -5
- package/dist/tts/tts.js.map +1 -1
- package/dist/utils.cjs +6 -0
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.d.cts +1 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +5 -0
- package/dist/utils.js.map +1 -1
- package/dist/voice/agent_activity.cjs +93 -7
- package/dist/voice/agent_activity.cjs.map +1 -1
- package/dist/voice/agent_activity.d.cts +3 -0
- package/dist/voice/agent_activity.d.ts +3 -0
- package/dist/voice/agent_activity.d.ts.map +1 -1
- package/dist/voice/agent_activity.js +93 -7
- package/dist/voice/agent_activity.js.map +1 -1
- package/dist/voice/agent_session.cjs +122 -27
- package/dist/voice/agent_session.cjs.map +1 -1
- package/dist/voice/agent_session.d.cts +15 -0
- package/dist/voice/agent_session.d.ts +15 -0
- package/dist/voice/agent_session.d.ts.map +1 -1
- package/dist/voice/agent_session.js +122 -27
- package/dist/voice/agent_session.js.map +1 -1
- package/dist/voice/audio_recognition.cjs +69 -22
- package/dist/voice/audio_recognition.cjs.map +1 -1
- package/dist/voice/audio_recognition.d.cts +5 -0
- package/dist/voice/audio_recognition.d.ts +5 -0
- package/dist/voice/audio_recognition.d.ts.map +1 -1
- package/dist/voice/audio_recognition.js +69 -22
- package/dist/voice/audio_recognition.js.map +1 -1
- package/dist/voice/generation.cjs +43 -3
- package/dist/voice/generation.cjs.map +1 -1
- package/dist/voice/generation.d.ts.map +1 -1
- package/dist/voice/generation.js +43 -3
- package/dist/voice/generation.js.map +1 -1
- package/dist/voice/report.cjs +3 -2
- package/dist/voice/report.cjs.map +1 -1
- package/dist/voice/report.d.cts +7 -1
- package/dist/voice/report.d.ts +7 -1
- package/dist/voice/report.d.ts.map +1 -1
- package/dist/voice/report.js +3 -2
- package/dist/voice/report.js.map +1 -1
- package/package.json +8 -2
- package/src/inference/api_protos.ts +2 -2
- package/src/ipc/job_proc_lazy_main.ts +12 -1
- package/src/job.ts +59 -10
- package/src/llm/llm.ts +48 -5
- package/src/log.ts +52 -15
- package/src/telemetry/index.ts +22 -4
- package/src/telemetry/logging.ts +55 -0
- package/src/telemetry/otel_http_exporter.ts +191 -0
- package/src/telemetry/pino_otel_transport.ts +265 -0
- package/src/telemetry/traces.ts +320 -20
- package/src/tts/tts.ts +71 -9
- package/src/utils.ts +5 -0
- package/src/voice/agent_activity.ts +140 -22
- package/src/voice/agent_session.ts +174 -34
- package/src/voice/audio_recognition.ts +85 -26
- package/src/voice/generation.ts +59 -7
- package/src/voice/report.ts +10 -4
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { SeverityNumber } from "@opentelemetry/api-logs";
|
|
2
|
+
import { AccessToken } from "livekit-server-sdk";
|
|
3
|
+
function mapPinoLevelToSeverity(pinoLevel) {
|
|
4
|
+
if (pinoLevel <= 10) {
|
|
5
|
+
return { severityNumber: SeverityNumber.TRACE, severityText: "TRACE" };
|
|
6
|
+
} else if (pinoLevel <= 20) {
|
|
7
|
+
return { severityNumber: SeverityNumber.DEBUG, severityText: "DEBUG" };
|
|
8
|
+
} else if (pinoLevel <= 30) {
|
|
9
|
+
return { severityNumber: SeverityNumber.INFO, severityText: "INFO" };
|
|
10
|
+
} else if (pinoLevel <= 40) {
|
|
11
|
+
return { severityNumber: SeverityNumber.WARN, severityText: "WARN" };
|
|
12
|
+
} else if (pinoLevel <= 50) {
|
|
13
|
+
return { severityNumber: SeverityNumber.ERROR, severityText: "ERROR" };
|
|
14
|
+
} else {
|
|
15
|
+
return { severityNumber: SeverityNumber.FATAL, severityText: "FATAL" };
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const EXCLUDE_FIELDS = /* @__PURE__ */ new Set(["level", "time", "msg", "pid", "hostname", "v"]);
|
|
19
|
+
function convertValue(value) {
|
|
20
|
+
if (value === null || value === void 0) {
|
|
21
|
+
return { stringValue: "" };
|
|
22
|
+
}
|
|
23
|
+
if (typeof value === "string") {
|
|
24
|
+
return { stringValue: value };
|
|
25
|
+
}
|
|
26
|
+
if (typeof value === "number") {
|
|
27
|
+
return Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value };
|
|
28
|
+
}
|
|
29
|
+
if (typeof value === "boolean") {
|
|
30
|
+
return { boolValue: value };
|
|
31
|
+
}
|
|
32
|
+
if (typeof value === "object") {
|
|
33
|
+
return { stringValue: JSON.stringify(value) };
|
|
34
|
+
}
|
|
35
|
+
return { stringValue: String(value) };
|
|
36
|
+
}
|
|
37
|
+
class PinoCloudExporter {
|
|
38
|
+
config;
|
|
39
|
+
loggerName;
|
|
40
|
+
batchSize;
|
|
41
|
+
flushIntervalMs;
|
|
42
|
+
jwt = null;
|
|
43
|
+
pendingLogs = [];
|
|
44
|
+
flushTimer = null;
|
|
45
|
+
constructor(config) {
|
|
46
|
+
this.config = config;
|
|
47
|
+
this.loggerName = config.loggerName || "livekit.agents";
|
|
48
|
+
this.batchSize = config.batchSize || 100;
|
|
49
|
+
this.flushIntervalMs = config.flushIntervalMs || 5e3;
|
|
50
|
+
}
|
|
51
|
+
emit(logObj) {
|
|
52
|
+
const record = this.convertToOtlpRecord(logObj);
|
|
53
|
+
this.pendingLogs.push(record);
|
|
54
|
+
if (!this.flushTimer) {
|
|
55
|
+
this.flushTimer = setTimeout(() => {
|
|
56
|
+
this.flush().catch(console.error);
|
|
57
|
+
}, this.flushIntervalMs);
|
|
58
|
+
}
|
|
59
|
+
if (this.pendingLogs.length >= this.batchSize) {
|
|
60
|
+
this.flush().catch(console.error);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
convertToOtlpRecord(logObj) {
|
|
64
|
+
const { severityNumber, severityText } = mapPinoLevelToSeverity(logObj.level);
|
|
65
|
+
const attributes = [
|
|
66
|
+
{ key: "room_id", value: { stringValue: this.config.roomId } },
|
|
67
|
+
{ key: "job_id", value: { stringValue: this.config.jobId } },
|
|
68
|
+
{ key: "logger.name", value: { stringValue: this.loggerName } }
|
|
69
|
+
];
|
|
70
|
+
if (logObj.pid !== void 0) {
|
|
71
|
+
attributes.push({ key: "process.pid", value: { intValue: String(logObj.pid) } });
|
|
72
|
+
}
|
|
73
|
+
if (logObj.hostname !== void 0) {
|
|
74
|
+
attributes.push({ key: "host.name", value: { stringValue: logObj.hostname } });
|
|
75
|
+
}
|
|
76
|
+
for (const [key, value] of Object.entries(logObj)) {
|
|
77
|
+
if (!EXCLUDE_FIELDS.has(key)) {
|
|
78
|
+
attributes.push({ key, value: convertValue(value) });
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
timeUnixNano: String(BigInt(logObj.time) * BigInt(1e6)),
|
|
83
|
+
observedTimeUnixNano: String(BigInt(Date.now()) * BigInt(1e6)),
|
|
84
|
+
severityNumber,
|
|
85
|
+
severityText,
|
|
86
|
+
body: { stringValue: logObj.msg || "" },
|
|
87
|
+
attributes,
|
|
88
|
+
traceId: "",
|
|
89
|
+
spanId: ""
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async flush() {
|
|
93
|
+
if (this.flushTimer) {
|
|
94
|
+
clearTimeout(this.flushTimer);
|
|
95
|
+
this.flushTimer = null;
|
|
96
|
+
}
|
|
97
|
+
if (this.pendingLogs.length === 0) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const logs = this.pendingLogs;
|
|
101
|
+
this.pendingLogs = [];
|
|
102
|
+
try {
|
|
103
|
+
await this.sendLogs(logs);
|
|
104
|
+
} catch (error) {
|
|
105
|
+
this.pendingLogs = [...logs, ...this.pendingLogs];
|
|
106
|
+
console.error("[PinoCloudExporter] Failed to flush logs:", error);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
async sendLogs(logRecords) {
|
|
110
|
+
await this.ensureJwt();
|
|
111
|
+
const payload = {
|
|
112
|
+
resourceLogs: [
|
|
113
|
+
{
|
|
114
|
+
resource: {
|
|
115
|
+
attributes: [
|
|
116
|
+
{ key: "service.name", value: { stringValue: "livekit-agents" } },
|
|
117
|
+
{ key: "room_id", value: { stringValue: this.config.roomId } },
|
|
118
|
+
{ key: "job_id", value: { stringValue: this.config.jobId } }
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
scopeLogs: [
|
|
122
|
+
{
|
|
123
|
+
scope: {
|
|
124
|
+
name: this.loggerName,
|
|
125
|
+
attributes: [
|
|
126
|
+
{ key: "room_id", value: { stringValue: this.config.roomId } },
|
|
127
|
+
{ key: "job_id", value: { stringValue: this.config.jobId } }
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
logRecords
|
|
131
|
+
}
|
|
132
|
+
]
|
|
133
|
+
}
|
|
134
|
+
]
|
|
135
|
+
};
|
|
136
|
+
const endpoint = `https://${this.config.cloudHostname}/observability/logs/otlp/v0`;
|
|
137
|
+
const response = await fetch(endpoint, {
|
|
138
|
+
method: "POST",
|
|
139
|
+
headers: {
|
|
140
|
+
Authorization: `Bearer ${this.jwt}`,
|
|
141
|
+
"Content-Type": "application/json"
|
|
142
|
+
},
|
|
143
|
+
body: JSON.stringify(payload)
|
|
144
|
+
});
|
|
145
|
+
if (!response.ok) {
|
|
146
|
+
const text = await response.text();
|
|
147
|
+
throw new Error(`Log export failed: ${response.status} ${response.statusText} - ${text}`);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
async ensureJwt() {
|
|
151
|
+
if (this.jwt) return;
|
|
152
|
+
const apiKey = process.env.LIVEKIT_API_KEY;
|
|
153
|
+
const apiSecret = process.env.LIVEKIT_API_SECRET;
|
|
154
|
+
if (!apiKey || !apiSecret) {
|
|
155
|
+
throw new Error("LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set");
|
|
156
|
+
}
|
|
157
|
+
const token = new AccessToken(apiKey, apiSecret, { ttl: "6h" });
|
|
158
|
+
token.addObservabilityGrant({ write: true });
|
|
159
|
+
this.jwt = await token.toJwt();
|
|
160
|
+
}
|
|
161
|
+
async shutdown() {
|
|
162
|
+
await this.flush();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
let globalExporter = null;
|
|
166
|
+
function initPinoCloudExporter(config) {
|
|
167
|
+
globalExporter = new PinoCloudExporter(config);
|
|
168
|
+
}
|
|
169
|
+
function getPinoCloudExporter() {
|
|
170
|
+
return globalExporter;
|
|
171
|
+
}
|
|
172
|
+
function emitToOtel(logObj) {
|
|
173
|
+
if (globalExporter) {
|
|
174
|
+
globalExporter.emit(logObj);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
async function flushPinoLogs() {
|
|
178
|
+
if (globalExporter) {
|
|
179
|
+
await globalExporter.flush();
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export {
|
|
183
|
+
PinoCloudExporter,
|
|
184
|
+
emitToOtel,
|
|
185
|
+
flushPinoLogs,
|
|
186
|
+
getPinoCloudExporter,
|
|
187
|
+
initPinoCloudExporter
|
|
188
|
+
};
|
|
189
|
+
//# sourceMappingURL=pino_otel_transport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/telemetry/pino_otel_transport.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\n\n/**\n * Custom Pino OTEL Transport\n *\n * Standalone exporter for Pino logs to LiveKit Cloud.\n * Uses raw HTTP JSON format, bypassing the OTEL SDK.\n */\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport { AccessToken } from 'livekit-server-sdk';\n\nexport interface PinoLogObject {\n level: number;\n time: number;\n msg: string;\n pid?: number;\n hostname?: string;\n [key: string]: unknown;\n}\n\nexport interface PinoCloudExporterConfig {\n cloudHostname: string;\n roomId: string;\n jobId: string;\n loggerName?: string;\n batchSize?: number;\n flushIntervalMs?: number;\n}\n\nfunction mapPinoLevelToSeverity(pinoLevel: number): {\n severityNumber: SeverityNumber;\n severityText: string;\n} {\n if (pinoLevel <= 10) {\n return { severityNumber: SeverityNumber.TRACE, severityText: 'TRACE' };\n } else if (pinoLevel <= 20) {\n return { severityNumber: SeverityNumber.DEBUG, severityText: 'DEBUG' };\n } else if (pinoLevel <= 30) {\n return { severityNumber: SeverityNumber.INFO, severityText: 'INFO' };\n } else if (pinoLevel <= 40) {\n return { severityNumber: SeverityNumber.WARN, severityText: 'WARN' };\n } else if (pinoLevel <= 50) {\n return { severityNumber: SeverityNumber.ERROR, severityText: 'ERROR' };\n } else {\n return { severityNumber: SeverityNumber.FATAL, severityText: 'FATAL' };\n }\n}\n\nconst EXCLUDE_FIELDS = new Set(['level', 'time', 'msg', 'pid', 'hostname', 'v']);\n\nfunction convertValue(value: unknown): unknown {\n if (value === null || value === undefined) {\n return { stringValue: '' };\n }\n if (typeof value === 'string') {\n return { stringValue: value };\n }\n if (typeof value === 'number') {\n return Number.isInteger(value) ? { intValue: String(value) } : { doubleValue: value };\n }\n if (typeof value === 'boolean') {\n return { boolValue: value };\n }\n if (typeof value === 'object') {\n return { stringValue: JSON.stringify(value) };\n }\n return { stringValue: String(value) };\n}\n\n/**\n * Standalone Pino log exporter for LiveKit Cloud.\n *\n * Collects Pino logs, batches them, and sends via raw HTTP JSON.\n * No OTEL SDK dependency\n *\n * @example\n * ```typescript\n * const exporter = new PinoCloudExporter({\n * cloudHostname: 'cloud.livekit.io',\n * roomId: 'RM_xxx',\n * jobId: 'AJ_xxx',\n * });\n *\n * // In Pino formatter hook:\n * exporter.emit(logObj);\n *\n * // On session end:\n * await exporter.flush();\n * ```\n */\nexport class PinoCloudExporter {\n private readonly config: PinoCloudExporterConfig;\n private readonly loggerName: string;\n private readonly batchSize: number;\n private readonly flushIntervalMs: number;\n private jwt: string | null = null;\n private pendingLogs: any[] = [];\n private flushTimer: NodeJS.Timeout | null = null;\n\n constructor(config: PinoCloudExporterConfig) {\n this.config = config;\n this.loggerName = config.loggerName || 'livekit.agents';\n this.batchSize = config.batchSize || 100;\n this.flushIntervalMs = config.flushIntervalMs || 5000;\n }\n\n emit(logObj: PinoLogObject): void {\n const record = this.convertToOtlpRecord(logObj);\n this.pendingLogs.push(record);\n\n if (!this.flushTimer) {\n this.flushTimer = setTimeout(() => {\n this.flush().catch(console.error);\n }, this.flushIntervalMs);\n }\n\n if (this.pendingLogs.length >= this.batchSize) {\n this.flush().catch(console.error);\n }\n }\n\n private convertToOtlpRecord(logObj: PinoLogObject): any {\n const { severityNumber, severityText } = mapPinoLevelToSeverity(logObj.level);\n\n const attributes: any[] = [\n { key: 'room_id', value: { stringValue: this.config.roomId } },\n { key: 'job_id', value: { stringValue: this.config.jobId } },\n { key: 'logger.name', value: { stringValue: this.loggerName } },\n ];\n\n if (logObj.pid !== undefined) {\n attributes.push({ key: 'process.pid', value: { intValue: String(logObj.pid) } });\n }\n if (logObj.hostname !== undefined) {\n attributes.push({ key: 'host.name', value: { stringValue: logObj.hostname } });\n }\n\n for (const [key, value] of Object.entries(logObj)) {\n if (!EXCLUDE_FIELDS.has(key)) {\n attributes.push({ key, value: convertValue(value) });\n }\n }\n\n return {\n timeUnixNano: String(BigInt(logObj.time) * BigInt(1_000_000)),\n observedTimeUnixNano: String(BigInt(Date.now()) * BigInt(1_000_000)),\n severityNumber,\n severityText,\n body: { stringValue: logObj.msg || '' },\n attributes,\n traceId: '',\n spanId: '',\n };\n }\n\n async flush(): Promise<void> {\n if (this.flushTimer) {\n clearTimeout(this.flushTimer);\n this.flushTimer = null;\n }\n\n if (this.pendingLogs.length === 0) {\n return;\n }\n\n const logs = this.pendingLogs;\n this.pendingLogs = [];\n\n try {\n await this.sendLogs(logs);\n } catch (error) {\n this.pendingLogs = [...logs, ...this.pendingLogs];\n console.error('[PinoCloudExporter] Failed to flush logs:', error);\n }\n }\n\n private async sendLogs(logRecords: any[]): Promise<void> {\n await this.ensureJwt();\n\n const payload = {\n resourceLogs: [\n {\n resource: {\n attributes: [\n { key: 'service.name', value: { stringValue: 'livekit-agents' } },\n { key: 'room_id', value: { stringValue: this.config.roomId } },\n { key: 'job_id', value: { stringValue: this.config.jobId } },\n ],\n },\n scopeLogs: [\n {\n scope: {\n name: this.loggerName,\n attributes: [\n { key: 'room_id', value: { stringValue: this.config.roomId } },\n { key: 'job_id', value: { stringValue: this.config.jobId } },\n ],\n },\n logRecords,\n },\n ],\n },\n ],\n };\n\n const endpoint = `https://${this.config.cloudHostname}/observability/logs/otlp/v0`;\n\n const response = await fetch(endpoint, {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${this.jwt}`,\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(`Log export failed: ${response.status} ${response.statusText} - ${text}`);\n }\n }\n\n private async ensureJwt(): Promise<void> {\n if (this.jwt) return;\n\n const apiKey = process.env.LIVEKIT_API_KEY;\n const apiSecret = process.env.LIVEKIT_API_SECRET;\n\n if (!apiKey || !apiSecret) {\n throw new Error('LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set');\n }\n\n const token = new AccessToken(apiKey, apiSecret, { ttl: '6h' });\n token.addObservabilityGrant({ write: true });\n this.jwt = await token.toJwt();\n }\n\n async shutdown(): Promise<void> {\n await this.flush();\n }\n}\n\nlet globalExporter: PinoCloudExporter | null = null;\n\nexport function initPinoCloudExporter(config: PinoCloudExporterConfig): void {\n globalExporter = new PinoCloudExporter(config);\n}\n\nexport function getPinoCloudExporter(): PinoCloudExporter | null {\n return globalExporter;\n}\n\nexport function emitToOtel(logObj: PinoLogObject): void {\n if (globalExporter) {\n globalExporter.emit(logObj);\n }\n}\n\nexport async function flushPinoLogs(): Promise<void> {\n if (globalExporter) {\n await globalExporter.flush();\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
8
|
var __export = (target, all) => {
|
|
7
9
|
for (var name in all)
|
|
@@ -15,21 +17,37 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
15
17
|
}
|
|
16
18
|
return to;
|
|
17
19
|
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
18
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
29
|
var traces_exports = {};
|
|
20
30
|
__export(traces_exports, {
|
|
31
|
+
flushOtelLogs: () => flushOtelLogs,
|
|
21
32
|
setTracerProvider: () => setTracerProvider,
|
|
22
33
|
setupCloudTracer: () => setupCloudTracer,
|
|
23
|
-
tracer: () => tracer
|
|
34
|
+
tracer: () => tracer,
|
|
35
|
+
uploadSessionReport: () => uploadSessionReport
|
|
24
36
|
});
|
|
25
37
|
module.exports = __toCommonJS(traces_exports);
|
|
38
|
+
var import_protocol = require("@livekit/protocol");
|
|
26
39
|
var import_api = require("@opentelemetry/api");
|
|
27
|
-
var
|
|
40
|
+
var import_api_logs = require("@opentelemetry/api-logs");
|
|
41
|
+
var import_exporter_trace_otlp_proto = require("@opentelemetry/exporter-trace-otlp-proto");
|
|
28
42
|
var import_otlp_exporter_base = require("@opentelemetry/otlp-exporter-base");
|
|
29
43
|
var import_resources = require("@opentelemetry/resources");
|
|
30
44
|
var import_sdk_trace_node = require("@opentelemetry/sdk-trace-node");
|
|
31
45
|
var import_semantic_conventions = require("@opentelemetry/semantic-conventions");
|
|
46
|
+
var import_form_data = __toESM(require("form-data"), 1);
|
|
32
47
|
var import_livekit_server_sdk = require("livekit-server-sdk");
|
|
48
|
+
var import_log = require("../log.cjs");
|
|
49
|
+
var import_otel_http_exporter = require("./otel_http_exporter.cjs");
|
|
50
|
+
var import_pino_otel_transport = require("./pino_otel_transport.cjs");
|
|
33
51
|
class DynamicTracer {
|
|
34
52
|
tracerProvider;
|
|
35
53
|
tracer;
|
|
@@ -84,19 +102,14 @@ class DynamicTracer {
|
|
|
84
102
|
const ctx = options.context || import_api.context.active();
|
|
85
103
|
const endOnExit = options.endOnExit === void 0 ? true : options.endOnExit;
|
|
86
104
|
const opts = { attributes: options.attributes };
|
|
87
|
-
return
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
reject(error);
|
|
94
|
-
} finally {
|
|
95
|
-
if (endOnExit) {
|
|
96
|
-
span.end();
|
|
97
|
-
}
|
|
105
|
+
return await this.tracer.startActiveSpan(options.name, opts, ctx, async (span) => {
|
|
106
|
+
try {
|
|
107
|
+
return await fn(span);
|
|
108
|
+
} finally {
|
|
109
|
+
if (endOnExit) {
|
|
110
|
+
span.end();
|
|
98
111
|
}
|
|
99
|
-
}
|
|
112
|
+
}
|
|
100
113
|
});
|
|
101
114
|
}
|
|
102
115
|
/**
|
|
@@ -171,7 +184,7 @@ async function setupCloudTracer(options) {
|
|
|
171
184
|
room_id: roomId,
|
|
172
185
|
job_id: jobId
|
|
173
186
|
});
|
|
174
|
-
const spanExporter = new
|
|
187
|
+
const spanExporter = new import_exporter_trace_otlp_proto.OTLPTraceExporter({
|
|
175
188
|
url: `https://${cloudHostname}/observability/traces/otlp/v0`,
|
|
176
189
|
headers,
|
|
177
190
|
compression: import_otlp_exporter_base.CompressionAlgorithm.GZIP
|
|
@@ -182,15 +195,211 @@ async function setupCloudTracer(options) {
|
|
|
182
195
|
});
|
|
183
196
|
tracerProvider.register();
|
|
184
197
|
setTracerProvider(tracerProvider);
|
|
198
|
+
(0, import_pino_otel_transport.initPinoCloudExporter)({
|
|
199
|
+
cloudHostname,
|
|
200
|
+
roomId,
|
|
201
|
+
jobId
|
|
202
|
+
});
|
|
203
|
+
(0, import_log.enableOtelLogging)();
|
|
185
204
|
} catch (error) {
|
|
186
205
|
console.error("Failed to setup cloud tracer:", error);
|
|
187
206
|
throw error;
|
|
188
207
|
}
|
|
189
208
|
}
|
|
209
|
+
async function flushOtelLogs() {
|
|
210
|
+
await (0, import_pino_otel_transport.flushPinoLogs)();
|
|
211
|
+
}
|
|
212
|
+
function chatItemToProto(item) {
|
|
213
|
+
var _a, _b;
|
|
214
|
+
const itemDict = {};
|
|
215
|
+
if (item.type === "message") {
|
|
216
|
+
const roleMap = {
|
|
217
|
+
developer: "DEVELOPER",
|
|
218
|
+
system: "SYSTEM",
|
|
219
|
+
user: "USER",
|
|
220
|
+
assistant: "ASSISTANT"
|
|
221
|
+
};
|
|
222
|
+
const msg = {
|
|
223
|
+
id: item.id,
|
|
224
|
+
role: roleMap[item.role] || item.role.toUpperCase(),
|
|
225
|
+
content: item.content.map((c) => ({ text: c })),
|
|
226
|
+
createdAt: toRFC3339(item.createdAt)
|
|
227
|
+
};
|
|
228
|
+
if (item.interrupted) {
|
|
229
|
+
msg.interrupted = item.interrupted;
|
|
230
|
+
}
|
|
231
|
+
itemDict.message = msg;
|
|
232
|
+
} else if (item.type === "function_call") {
|
|
233
|
+
itemDict.functionCall = {
|
|
234
|
+
id: item.id,
|
|
235
|
+
callId: item.callId,
|
|
236
|
+
arguments: item.args,
|
|
237
|
+
name: item.name,
|
|
238
|
+
createdAt: toRFC3339(item.createdAt)
|
|
239
|
+
};
|
|
240
|
+
} else if (item.type === "function_call_output") {
|
|
241
|
+
itemDict.functionCallOutput = {
|
|
242
|
+
id: item.id,
|
|
243
|
+
name: item.name,
|
|
244
|
+
callId: item.callId,
|
|
245
|
+
output: item.output,
|
|
246
|
+
isError: item.isError,
|
|
247
|
+
createdAt: toRFC3339(item.createdAt)
|
|
248
|
+
};
|
|
249
|
+
} else if (item.type === "agent_handoff") {
|
|
250
|
+
const handoff = {
|
|
251
|
+
id: item.id,
|
|
252
|
+
newAgentId: item.newAgentId,
|
|
253
|
+
createdAt: toRFC3339(item.createdAt)
|
|
254
|
+
};
|
|
255
|
+
if (item.oldAgentId !== void 0 && item.oldAgentId !== null && item.oldAgentId !== "") {
|
|
256
|
+
handoff.oldAgentId = item.oldAgentId;
|
|
257
|
+
}
|
|
258
|
+
itemDict.agentHandoff = handoff;
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
if (item.type === "function_call" && typeof ((_a = itemDict.functionCall) == null ? void 0 : _a.arguments) === "string") {
|
|
262
|
+
itemDict.functionCall.arguments = JSON.parse(itemDict.functionCall.arguments);
|
|
263
|
+
} else if (item.type === "function_call_output" && typeof ((_b = itemDict.functionCallOutput) == null ? void 0 : _b.output) === "string") {
|
|
264
|
+
itemDict.functionCallOutput.output = JSON.parse(itemDict.functionCallOutput.output);
|
|
265
|
+
}
|
|
266
|
+
} catch {
|
|
267
|
+
}
|
|
268
|
+
return itemDict;
|
|
269
|
+
}
|
|
270
|
+
function toRFC3339(valueMs) {
|
|
271
|
+
const dt = valueMs instanceof Date ? valueMs : new Date(valueMs);
|
|
272
|
+
const truncated = new Date(Math.floor(dt.getTime()));
|
|
273
|
+
return truncated.toISOString();
|
|
274
|
+
}
|
|
275
|
+
async function uploadSessionReport(options) {
|
|
276
|
+
const { agentName, cloudHostname, report } = options;
|
|
277
|
+
const logExporter = new import_otel_http_exporter.SimpleOTLPHttpLogExporter({
|
|
278
|
+
cloudHostname,
|
|
279
|
+
resourceAttributes: {
|
|
280
|
+
room_id: report.roomId,
|
|
281
|
+
job_id: report.jobId
|
|
282
|
+
},
|
|
283
|
+
scopeName: "chat_history",
|
|
284
|
+
scopeAttributes: {
|
|
285
|
+
room_id: report.roomId,
|
|
286
|
+
job_id: report.jobId,
|
|
287
|
+
room: report.room
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
const logRecords = [];
|
|
291
|
+
const commonAttrs = {
|
|
292
|
+
room_id: report.roomId,
|
|
293
|
+
job_id: report.jobId,
|
|
294
|
+
"logger.name": "chat_history"
|
|
295
|
+
};
|
|
296
|
+
logRecords.push({
|
|
297
|
+
body: "session report",
|
|
298
|
+
timestampMs: report.startedAt || report.timestamp || 0,
|
|
299
|
+
attributes: {
|
|
300
|
+
...commonAttrs,
|
|
301
|
+
"session.options": report.options || {},
|
|
302
|
+
"session.report_timestamp": report.timestamp,
|
|
303
|
+
agent_name: agentName
|
|
304
|
+
}
|
|
305
|
+
});
|
|
306
|
+
let lastTimestamp = 0;
|
|
307
|
+
for (const item of report.chatHistory.items) {
|
|
308
|
+
let itemTimestamp = item.createdAt;
|
|
309
|
+
if (itemTimestamp <= lastTimestamp) {
|
|
310
|
+
itemTimestamp = lastTimestamp + 1e-3;
|
|
311
|
+
}
|
|
312
|
+
lastTimestamp = itemTimestamp;
|
|
313
|
+
const itemProto = chatItemToProto(item);
|
|
314
|
+
let severityNumber = import_api_logs.SeverityNumber.UNSPECIFIED;
|
|
315
|
+
let severityText = "unspecified";
|
|
316
|
+
if (item.type === "function_call_output" && item.isError) {
|
|
317
|
+
severityNumber = import_api_logs.SeverityNumber.ERROR;
|
|
318
|
+
severityText = "error";
|
|
319
|
+
}
|
|
320
|
+
logRecords.push({
|
|
321
|
+
body: "chat item",
|
|
322
|
+
timestampMs: itemTimestamp,
|
|
323
|
+
// Adjusted for monotonic ordering
|
|
324
|
+
attributes: { "chat.item": itemProto, ...commonAttrs },
|
|
325
|
+
severityNumber,
|
|
326
|
+
severityText
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
await logExporter.export(logRecords);
|
|
330
|
+
const apiKey = process.env.LIVEKIT_API_KEY;
|
|
331
|
+
const apiSecret = process.env.LIVEKIT_API_SECRET;
|
|
332
|
+
if (!apiKey || !apiSecret) {
|
|
333
|
+
throw new Error("LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set for session upload");
|
|
334
|
+
}
|
|
335
|
+
const token = new import_livekit_server_sdk.AccessToken(apiKey, apiSecret, { ttl: "6h" });
|
|
336
|
+
token.addObservabilityGrant({ write: true });
|
|
337
|
+
const jwt = await token.toJwt();
|
|
338
|
+
const formData = new import_form_data.default();
|
|
339
|
+
const headerMsg = new import_protocol.MetricsRecordingHeader({
|
|
340
|
+
roomId: report.roomId,
|
|
341
|
+
duration: BigInt(0),
|
|
342
|
+
// TODO: Calculate actual duration from report
|
|
343
|
+
startTime: {
|
|
344
|
+
seconds: BigInt(Math.floor(report.timestamp / 1e3)),
|
|
345
|
+
nanos: Math.floor(report.timestamp % 1e3 * 1e6)
|
|
346
|
+
}
|
|
347
|
+
});
|
|
348
|
+
const headerBytes = Buffer.from(headerMsg.toBinary());
|
|
349
|
+
formData.append("header", headerBytes, {
|
|
350
|
+
filename: "header.binpb",
|
|
351
|
+
contentType: "application/protobuf",
|
|
352
|
+
knownLength: headerBytes.length,
|
|
353
|
+
header: {
|
|
354
|
+
"Content-Type": "application/protobuf",
|
|
355
|
+
"Content-Length": headerBytes.length.toString()
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
const chatHistoryJson = JSON.stringify(report.chatHistory.toJSON({ excludeTimestamp: false }));
|
|
359
|
+
const chatHistoryBuffer = Buffer.from(chatHistoryJson, "utf-8");
|
|
360
|
+
formData.append("chat_history", chatHistoryBuffer, {
|
|
361
|
+
filename: "chat_history.json",
|
|
362
|
+
contentType: "application/json",
|
|
363
|
+
knownLength: chatHistoryBuffer.length,
|
|
364
|
+
header: {
|
|
365
|
+
"Content-Type": "application/json",
|
|
366
|
+
"Content-Length": chatHistoryBuffer.length.toString()
|
|
367
|
+
}
|
|
368
|
+
});
|
|
369
|
+
return new Promise((resolve, reject) => {
|
|
370
|
+
formData.submit(
|
|
371
|
+
{
|
|
372
|
+
protocol: "https:",
|
|
373
|
+
host: cloudHostname,
|
|
374
|
+
path: "/observability/recordings/v0",
|
|
375
|
+
method: "POST",
|
|
376
|
+
headers: {
|
|
377
|
+
Authorization: `Bearer ${jwt}`
|
|
378
|
+
}
|
|
379
|
+
},
|
|
380
|
+
(err, res) => {
|
|
381
|
+
if (err) {
|
|
382
|
+
reject(new Error(`Failed to upload session report: ${err.message}`));
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
if (res.statusCode && res.statusCode >= 400) {
|
|
386
|
+
reject(
|
|
387
|
+
new Error(`Failed to upload session report: ${res.statusCode} ${res.statusMessage}`)
|
|
388
|
+
);
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
res.resume();
|
|
392
|
+
res.on("end", () => resolve());
|
|
393
|
+
}
|
|
394
|
+
);
|
|
395
|
+
});
|
|
396
|
+
}
|
|
190
397
|
// Annotate the CommonJS export names for ESM import in node:
|
|
191
398
|
0 && (module.exports = {
|
|
399
|
+
flushOtelLogs,
|
|
192
400
|
setTracerProvider,
|
|
193
401
|
setupCloudTracer,
|
|
194
|
-
tracer
|
|
402
|
+
tracer,
|
|
403
|
+
uploadSessionReport
|
|
195
404
|
});
|
|
196
405
|
//# sourceMappingURL=traces.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/telemetry/traces.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport {\n type Attributes,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n context as otelContext,\n trace,\n} from '@opentelemetry/api';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';\nimport { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';\nimport { Resource } from '@opentelemetry/resources';\nimport type { ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\nimport { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';\nimport { AccessToken } from 'livekit-server-sdk';\n\nexport interface StartSpanOptions {\n /** Name of the span */\n name: string;\n /** Optional parent context to use for this span */\n context?: Context;\n /** Attributes to set on the span when it starts */\n attributes?: Attributes;\n /** Whether to end the span when the function exits (default: true) */\n endOnExit?: boolean;\n}\n\n/**\n * A dynamic tracer that allows the tracer provider to be changed at runtime.\n */\nclass DynamicTracer {\n private tracerProvider: TracerProvider;\n private tracer: Tracer;\n private readonly instrumentingModuleName: string;\n\n constructor(instrumentingModuleName: string) {\n this.instrumentingModuleName = instrumentingModuleName;\n this.tracerProvider = trace.getTracerProvider();\n this.tracer = trace.getTracer(instrumentingModuleName);\n }\n\n /**\n * Set a new tracer provider. This updates the underlying tracer instance.\n * @param provider - The new tracer provider to use\n */\n setProvider(provider: TracerProvider): void {\n this.tracerProvider = provider;\n this.tracer = this.tracerProvider.getTracer(this.instrumentingModuleName);\n }\n\n /**\n * Get the underlying OpenTelemetry tracer.\n * Use this to access the full Tracer API when needed.\n */\n getTracer(): Tracer {\n return this.tracer;\n }\n\n /**\n * Start a span manually (without making it active).\n * You must call span.end() when done.\n *\n * @param options - Span configuration including name\n * @returns The created span\n */\n startSpan(options: StartSpanOptions): Span {\n const ctx = options.context || otelContext.active();\n const span = this.tracer.startSpan(\n options.name,\n {\n attributes: options.attributes,\n },\n ctx,\n );\n\n return span;\n }\n\n /**\n * Start a new span and make it active in the current context.\n * The span will automatically be ended when the provided function completes (unless endOnExit=false).\n *\n * @param fn - The function to execute within the span context\n * @param options - Span configuration including name\n * @returns The result of the provided function\n */\n async startActiveSpan<T>(fn: (span: Span) => Promise<T>, options: StartSpanOptions): Promise<T> {\n const ctx = options.context || otelContext.active();\n const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true\n const opts: SpanOptions = { attributes: options.attributes };\n\n return new Promise((resolve, reject) => {\n this.tracer.startActiveSpan(options.name, opts, ctx, async (span) => {\n try {\n const result = await fn(span);\n resolve(result);\n } catch (error) {\n reject(error);\n } finally {\n if (endOnExit) {\n span.end();\n }\n }\n });\n });\n }\n\n /**\n * Synchronous version of startActiveSpan for non-async operations.\n *\n * @param fn - The function to execute within the span context\n * @param options - Span configuration including name\n * @returns The result of the provided function\n */\n startActiveSpanSync<T>(fn: (span: Span) => T, options: StartSpanOptions): T {\n const ctx = options.context || otelContext.active();\n const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true\n const opts: SpanOptions = { attributes: options.attributes };\n\n return this.tracer.startActiveSpan(options.name, opts, ctx, (span) => {\n try {\n return fn(span);\n } finally {\n if (endOnExit) {\n span.end();\n }\n }\n });\n }\n}\n\n/**\n * The global tracer instance used throughout the agents framework.\n * This tracer can have its provider updated at runtime via setTracerProvider().\n */\nexport const tracer = new DynamicTracer('livekit-agents');\n\nclass MetadataSpanProcessor implements SpanProcessor {\n private metadata: Attributes;\n\n constructor(metadata: Attributes) {\n this.metadata = metadata;\n }\n\n onStart(span: Span, _parentContext: Context): void {\n span.setAttributes(this.metadata);\n }\n\n onEnd(_span: ReadableSpan): void {}\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\n// TODO(brian): PR4 - Add MetadataLogProcessor for structured logging\n\n// TODO(brian): PR4 - Add ExtraDetailsProcessor for structured logging\n\n/**\n * Set the tracer provider for the livekit-agents framework.\n * This should be called before agent session start if using custom tracer providers.\n *\n * @param provider - The tracer provider to use (must be a NodeTracerProvider)\n * @param options - Optional configuration with metadata property to inject into all spans\n *\n * @example\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n * import { setTracerProvider } from '@livekit/agents/telemetry';\n *\n * const provider = new NodeTracerProvider();\n * setTracerProvider(provider, {\n * metadata: { room_id: 'room123', job_id: 'job456' }\n * });\n * ```\n */\nexport function setTracerProvider(\n provider: NodeTracerProvider,\n options?: { metadata?: Attributes },\n): void {\n if (options?.metadata) {\n provider.addSpanProcessor(new MetadataSpanProcessor(options.metadata));\n }\n\n tracer.setProvider(provider);\n}\n\n/**\n * Setup OpenTelemetry tracer for LiveKit Cloud observability.\n * This configures OTLP exporters to send traces to LiveKit Cloud.\n *\n * @param options - Configuration for cloud tracer with roomId, jobId, and cloudHostname properties\n *\n * @internal\n */\nexport async function setupCloudTracer(options: {\n roomId: string;\n jobId: string;\n cloudHostname: string;\n}): Promise<void> {\n const { roomId, jobId, cloudHostname } = options;\n\n const apiKey = process.env.LIVEKIT_API_KEY;\n const apiSecret = process.env.LIVEKIT_API_SECRET;\n\n if (!apiKey || !apiSecret) {\n throw new Error('LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set for cloud tracing');\n }\n\n const token = new AccessToken(apiKey, apiSecret, {\n identity: 'livekit-agents-telemetry',\n ttl: '6h',\n });\n token.addObservabilityGrant({ write: true });\n\n try {\n const jwt = await token.toJwt();\n\n const headers = {\n Authorization: `Bearer ${jwt}`,\n };\n\n const metadata: Attributes = {\n room_id: roomId,\n job_id: jobId,\n };\n\n const resource = new Resource({\n [ATTR_SERVICE_NAME]: 'livekit-agents',\n room_id: roomId,\n job_id: jobId,\n });\n\n // Configure OTLP exporter to send traces to LiveKit Cloud\n const spanExporter = new OTLPTraceExporter({\n url: `https://${cloudHostname}/observability/traces/otlp/v0`,\n headers,\n compression: CompressionAlgorithm.GZIP,\n });\n\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [new MetadataSpanProcessor(metadata), new BatchSpanProcessor(spanExporter)],\n });\n tracerProvider.register();\n\n // Metadata processor is already configured in the constructor above\n setTracerProvider(tracerProvider);\n\n // TODO(brian): PR4 - Add logger provider setup here for structured logging\n // Similar to Python's setup: LoggerProvider, OTLPLogExporter, BatchLogRecordProcessor\n } catch (error) {\n console.error('Failed to setup cloud tracer:', error);\n throw error;\n }\n}\n"],"mappings":"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","names":["otelContext"]}
|
|
1
|
+
{"version":3,"sources":["../../src/telemetry/traces.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2025 LiveKit, Inc.\n//\n// SPDX-License-Identifier: Apache-2.0\nimport { MetricsRecordingHeader } from '@livekit/protocol';\nimport {\n type Attributes,\n type Context,\n type Span,\n type SpanOptions,\n type Tracer,\n type TracerProvider,\n context as otelContext,\n trace,\n} from '@opentelemetry/api';\nimport { SeverityNumber } from '@opentelemetry/api-logs';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';\nimport { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';\nimport { Resource } from '@opentelemetry/resources';\nimport type { ReadableSpan, SpanProcessor } from '@opentelemetry/sdk-trace-base';\nimport { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\nimport { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';\nimport FormData from 'form-data';\nimport { AccessToken } from 'livekit-server-sdk';\nimport type { ChatContent, ChatItem } from '../llm/index.js';\nimport { enableOtelLogging } from '../log.js';\nimport type { SessionReport } from '../voice/report.js';\nimport { type SimpleLogRecord, SimpleOTLPHttpLogExporter } from './otel_http_exporter.js';\nimport { flushPinoLogs, initPinoCloudExporter } from './pino_otel_transport.js';\n\nexport interface StartSpanOptions {\n /** Name of the span */\n name: string;\n /** Optional parent context to use for this span */\n context?: Context;\n /** Attributes to set on the span when it starts */\n attributes?: Attributes;\n /** Whether to end the span when the function exits (default: true) */\n endOnExit?: boolean;\n}\n\n/**\n * A dynamic tracer that allows the tracer provider to be changed at runtime.\n */\nclass DynamicTracer {\n private tracerProvider: TracerProvider;\n private tracer: Tracer;\n private readonly instrumentingModuleName: string;\n\n constructor(instrumentingModuleName: string) {\n this.instrumentingModuleName = instrumentingModuleName;\n this.tracerProvider = trace.getTracerProvider();\n this.tracer = trace.getTracer(instrumentingModuleName);\n }\n\n /**\n * Set a new tracer provider. This updates the underlying tracer instance.\n * @param provider - The new tracer provider to use\n */\n setProvider(provider: TracerProvider): void {\n this.tracerProvider = provider;\n this.tracer = this.tracerProvider.getTracer(this.instrumentingModuleName);\n }\n\n /**\n * Get the underlying OpenTelemetry tracer.\n * Use this to access the full Tracer API when needed.\n */\n getTracer(): Tracer {\n return this.tracer;\n }\n\n /**\n * Start a span manually (without making it active).\n * You must call span.end() when done.\n *\n * @param options - Span configuration including name\n * @returns The created span\n */\n startSpan(options: StartSpanOptions): Span {\n const ctx = options.context || otelContext.active();\n const span = this.tracer.startSpan(\n options.name,\n {\n attributes: options.attributes,\n },\n ctx,\n );\n\n return span;\n }\n\n /**\n * Start a new span and make it active in the current context.\n * The span will automatically be ended when the provided function completes (unless endOnExit=false).\n *\n * @param fn - The function to execute within the span context\n * @param options - Span configuration including name\n * @returns The result of the provided function\n */\n async startActiveSpan<T>(fn: (span: Span) => Promise<T>, options: StartSpanOptions): Promise<T> {\n const ctx = options.context || otelContext.active();\n const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true\n const opts: SpanOptions = { attributes: options.attributes };\n\n // Directly return the tracer's startActiveSpan result - it handles async correctly\n return await this.tracer.startActiveSpan(options.name, opts, ctx, async (span) => {\n try {\n return await fn(span);\n } finally {\n if (endOnExit) {\n span.end();\n }\n }\n });\n }\n\n /**\n * Synchronous version of startActiveSpan for non-async operations.\n *\n * @param fn - The function to execute within the span context\n * @param options - Span configuration including name\n * @returns The result of the provided function\n */\n startActiveSpanSync<T>(fn: (span: Span) => T, options: StartSpanOptions): T {\n const ctx = options.context || otelContext.active();\n const endOnExit = options.endOnExit === undefined ? true : options.endOnExit; // default true\n const opts: SpanOptions = { attributes: options.attributes };\n\n return this.tracer.startActiveSpan(options.name, opts, ctx, (span) => {\n try {\n return fn(span);\n } finally {\n if (endOnExit) {\n span.end();\n }\n }\n });\n }\n}\n\n/**\n * The global tracer instance used throughout the agents framework.\n * This tracer can have its provider updated at runtime via setTracerProvider().\n */\nexport const tracer = new DynamicTracer('livekit-agents');\n\nclass MetadataSpanProcessor implements SpanProcessor {\n private metadata: Attributes;\n\n constructor(metadata: Attributes) {\n this.metadata = metadata;\n }\n\n onStart(span: Span, _parentContext: Context): void {\n span.setAttributes(this.metadata);\n }\n\n onEnd(_span: ReadableSpan): void {}\n\n shutdown(): Promise<void> {\n return Promise.resolve();\n }\n\n forceFlush(): Promise<void> {\n return Promise.resolve();\n }\n}\n\n/**\n * Set the tracer provider for the livekit-agents framework.\n * This should be called before agent session start if using custom tracer providers.\n *\n * @param provider - The tracer provider to use (must be a NodeTracerProvider)\n * @param options - Optional configuration with metadata property to inject into all spans\n *\n * @example\n * ```typescript\n * import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\n * import { setTracerProvider } from '@livekit/agents/telemetry';\n *\n * const provider = new NodeTracerProvider();\n * setTracerProvider(provider, {\n * metadata: { room_id: 'room123', job_id: 'job456' }\n * });\n * ```\n */\nexport function setTracerProvider(\n provider: NodeTracerProvider,\n options?: { metadata?: Attributes },\n): void {\n if (options?.metadata) {\n provider.addSpanProcessor(new MetadataSpanProcessor(options.metadata));\n }\n\n tracer.setProvider(provider);\n}\n\n/**\n * Setup OpenTelemetry tracer for LiveKit Cloud observability.\n * This configures OTLP exporters to send traces to LiveKit Cloud.\n *\n * @param options - Configuration for cloud tracer with roomId, jobId, and cloudHostname properties\n *\n * @internal\n */\nexport async function setupCloudTracer(options: {\n roomId: string;\n jobId: string;\n cloudHostname: string;\n}): Promise<void> {\n const { roomId, jobId, cloudHostname } = options;\n\n const apiKey = process.env.LIVEKIT_API_KEY;\n const apiSecret = process.env.LIVEKIT_API_SECRET;\n\n if (!apiKey || !apiSecret) {\n throw new Error('LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set for cloud tracing');\n }\n\n const token = new AccessToken(apiKey, apiSecret, {\n identity: 'livekit-agents-telemetry',\n ttl: '6h',\n });\n token.addObservabilityGrant({ write: true });\n\n try {\n const jwt = await token.toJwt();\n\n const headers = {\n Authorization: `Bearer ${jwt}`,\n };\n\n const metadata: Attributes = {\n room_id: roomId,\n job_id: jobId,\n };\n\n const resource = new Resource({\n [ATTR_SERVICE_NAME]: 'livekit-agents',\n room_id: roomId,\n job_id: jobId,\n });\n\n // Configure OTLP exporter to send traces to LiveKit Cloud\n const spanExporter = new OTLPTraceExporter({\n url: `https://${cloudHostname}/observability/traces/otlp/v0`,\n headers,\n compression: CompressionAlgorithm.GZIP,\n });\n\n const tracerProvider = new NodeTracerProvider({\n resource,\n spanProcessors: [new MetadataSpanProcessor(metadata), new BatchSpanProcessor(spanExporter)],\n });\n tracerProvider.register();\n\n setTracerProvider(tracerProvider);\n\n // Initialize standalone Pino cloud exporter (no OTEL SDK dependency)\n initPinoCloudExporter({\n cloudHostname,\n roomId,\n jobId,\n });\n\n enableOtelLogging();\n } catch (error) {\n console.error('Failed to setup cloud tracer:', error);\n throw error;\n }\n}\n\n/**\n * Flush all pending Pino logs to ensure they are exported.\n * Call this before session/job ends to ensure all logs are sent.\n *\n * @internal\n */\nexport async function flushOtelLogs(): Promise<void> {\n await flushPinoLogs();\n}\n\n/**\n * Convert ChatItem to proto-compatible dictionary format.\n * TODO: Use actual agent_session proto types once @livekit/protocol v1.43.1+ is published\n */\nfunction chatItemToProto(item: ChatItem): Record<string, any> {\n const itemDict: Record<string, any> = {};\n\n if (item.type === 'message') {\n const roleMap: Record<string, string> = {\n developer: 'DEVELOPER',\n system: 'SYSTEM',\n user: 'USER',\n assistant: 'ASSISTANT',\n };\n\n const msg: Record<string, any> = {\n id: item.id,\n role: roleMap[item.role] || item.role.toUpperCase(),\n content: item.content.map((c: ChatContent) => ({ text: c })),\n createdAt: toRFC3339(item.createdAt),\n };\n\n if (item.interrupted) {\n msg.interrupted = item.interrupted;\n }\n\n // TODO(brian): Add extra and transcriptConfidence to ChatMessage\n // if (item.extra && Object.keys(item.extra).length > 0) {\n // msg.extra = item.extra;\n // }\n\n // if (item.transcriptConfidence !== undefined && item.transcriptConfidence !== null) {\n // msg.transcriptConfidence = item.transcriptConfidence;\n // }\n\n // TODO(brian): Add metrics to ChatMessage\n // const metrics = item.metrics || {};\n // if (Object.keys(metrics).length > 0) {\n // msg.metrics = {};\n // if (metrics.started_speaking_at) {\n // msg.metrics.startedSpeakingAt = toRFC3339(metrics.started_speaking_at);\n // }\n // if (metrics.stopped_speaking_at) {\n // msg.metrics.stoppedSpeakingAt = toRFC3339(metrics.stopped_speaking_at);\n // }\n // if (metrics.transcription_delay !== undefined) {\n // msg.metrics.transcriptionDelay = metrics.transcription_delay;\n // }\n // if (metrics.end_of_turn_delay !== undefined) {\n // msg.metrics.endOfTurnDelay = metrics.end_of_turn_delay;\n // }\n // if (metrics.on_user_turn_completed_delay !== undefined) {\n // msg.metrics.onUserTurnCompletedDelay = metrics.on_user_turn_completed_delay;\n // }\n // if (metrics.llm_node_ttft !== undefined) {\n // msg.metrics.llmNodeTtft = metrics.llm_node_ttft;\n // }\n // if (metrics.tts_node_ttfb !== undefined) {\n // msg.metrics.ttsNodeTtfb = metrics.tts_node_ttfb;\n // }\n // if (metrics.e2e_latency !== undefined) {\n // msg.metrics.e2eLatency = metrics.e2e_latency;\n // }\n // }\n\n itemDict.message = msg;\n } else if (item.type === 'function_call') {\n itemDict.functionCall = {\n id: item.id,\n callId: item.callId,\n arguments: item.args,\n name: item.name,\n createdAt: toRFC3339(item.createdAt),\n };\n } else if (item.type === 'function_call_output') {\n itemDict.functionCallOutput = {\n id: item.id,\n name: item.name,\n callId: item.callId,\n output: item.output,\n isError: item.isError,\n createdAt: toRFC3339(item.createdAt),\n };\n } else if (item.type === 'agent_handoff') {\n const handoff: Record<string, any> = {\n id: item.id,\n newAgentId: item.newAgentId,\n createdAt: toRFC3339(item.createdAt),\n };\n if (item.oldAgentId !== undefined && item.oldAgentId !== null && item.oldAgentId !== '') {\n handoff.oldAgentId = item.oldAgentId;\n }\n itemDict.agentHandoff = handoff;\n }\n\n try {\n if (item.type === 'function_call' && typeof itemDict.functionCall?.arguments === 'string') {\n itemDict.functionCall.arguments = JSON.parse(itemDict.functionCall.arguments);\n } else if (\n item.type === 'function_call_output' &&\n typeof itemDict.functionCallOutput?.output === 'string'\n ) {\n itemDict.functionCallOutput.output = JSON.parse(itemDict.functionCallOutput.output);\n }\n } catch {\n // ignore parsing errors\n }\n\n return itemDict;\n}\n\n/**\n * Convert timestamp to RFC3339 format matching Python's _to_rfc3339.\n * Note: TypeScript createdAt is in milliseconds (Date.now()), not seconds like Python.\n * @internal\n */\nfunction toRFC3339(valueMs: number | Date): string {\n // valueMs is already in milliseconds (from Date.now())\n const dt = valueMs instanceof Date ? valueMs : new Date(valueMs);\n // Truncate sub-millisecond precision\n const truncated = new Date(Math.floor(dt.getTime()));\n return truncated.toISOString();\n}\n\n/**\n * Upload session report to LiveKit Cloud observability.\n * @param options - Configuration with agentName, cloudHostname, and report\n */\nexport async function uploadSessionReport(options: {\n agentName: string;\n cloudHostname: string;\n report: SessionReport;\n}): Promise<void> {\n const { agentName, cloudHostname, report } = options;\n\n // Create OTLP HTTP exporter for chat history logs\n // Uses raw HTTP JSON format which is required by LiveKit Cloud\n const logExporter = new SimpleOTLPHttpLogExporter({\n cloudHostname,\n resourceAttributes: {\n room_id: report.roomId,\n job_id: report.jobId,\n },\n scopeName: 'chat_history',\n scopeAttributes: {\n room_id: report.roomId,\n job_id: report.jobId,\n room: report.room,\n },\n });\n\n // Build log records for session report and chat items\n const logRecords: SimpleLogRecord[] = [];\n\n const commonAttrs = {\n room_id: report.roomId,\n job_id: report.jobId,\n 'logger.name': 'chat_history',\n };\n\n logRecords.push({\n body: 'session report',\n timestampMs: report.startedAt || report.timestamp || 0,\n attributes: {\n ...commonAttrs,\n 'session.options': report.options || {},\n 'session.report_timestamp': report.timestamp,\n agent_name: agentName,\n },\n });\n\n // Track last timestamp to ensure monotonic ordering when items have identical timestamps\n // This fixes the issue where function_call and function_call_output with same timestamp\n // get reordered by the dashboard\n let lastTimestamp = 0;\n for (const item of report.chatHistory.items) {\n // Ensure monotonically increasing timestamps for proper ordering\n // Add 0.001ms (1 microsecond) offset when timestamps collide\n let itemTimestamp = item.createdAt;\n if (itemTimestamp <= lastTimestamp) {\n itemTimestamp = lastTimestamp + 0.001; // Add 1 microsecond\n }\n lastTimestamp = itemTimestamp;\n\n const itemProto = chatItemToProto(item);\n let severityNumber = SeverityNumber.UNSPECIFIED;\n let severityText = 'unspecified';\n\n if (item.type === 'function_call_output' && item.isError) {\n severityNumber = SeverityNumber.ERROR;\n severityText = 'error';\n }\n\n logRecords.push({\n body: 'chat item',\n timestampMs: itemTimestamp, // Adjusted for monotonic ordering\n attributes: { 'chat.item': itemProto, ...commonAttrs },\n severityNumber,\n severityText,\n });\n }\n await logExporter.export(logRecords);\n\n const apiKey = process.env.LIVEKIT_API_KEY;\n const apiSecret = process.env.LIVEKIT_API_SECRET;\n\n if (!apiKey || !apiSecret) {\n throw new Error('LIVEKIT_API_KEY and LIVEKIT_API_SECRET must be set for session upload');\n }\n\n const token = new AccessToken(apiKey, apiSecret, { ttl: '6h' });\n token.addObservabilityGrant({ write: true });\n const jwt = await token.toJwt();\n\n const formData = new FormData();\n\n // Add header (protobuf MetricsRecordingHeader)\n const headerMsg = new MetricsRecordingHeader({\n roomId: report.roomId,\n duration: BigInt(0), // TODO: Calculate actual duration from report\n startTime: {\n seconds: BigInt(Math.floor(report.timestamp / 1000)),\n nanos: Math.floor((report.timestamp % 1000) * 1e6),\n },\n });\n\n const headerBytes = Buffer.from(headerMsg.toBinary());\n formData.append('header', headerBytes, {\n filename: 'header.binpb',\n contentType: 'application/protobuf',\n knownLength: headerBytes.length,\n header: {\n 'Content-Type': 'application/protobuf',\n 'Content-Length': headerBytes.length.toString(),\n },\n });\n\n // Add chat_history JSON\n const chatHistoryJson = JSON.stringify(report.chatHistory.toJSON({ excludeTimestamp: false }));\n const chatHistoryBuffer = Buffer.from(chatHistoryJson, 'utf-8');\n formData.append('chat_history', chatHistoryBuffer, {\n filename: 'chat_history.json',\n contentType: 'application/json',\n knownLength: chatHistoryBuffer.length,\n header: {\n 'Content-Type': 'application/json',\n 'Content-Length': chatHistoryBuffer.length.toString(),\n },\n });\n\n // TODO(brian): Add audio recording file when recorder IO is implemented\n\n // Upload to LiveKit Cloud using form-data's submit method\n // This properly streams the multipart form with all headers including Content-Length\n return new Promise<void>((resolve, reject) => {\n formData.submit(\n {\n protocol: 'https:',\n host: cloudHostname,\n path: '/observability/recordings/v0',\n method: 'POST',\n headers: {\n Authorization: `Bearer ${jwt}`,\n },\n },\n (err, res) => {\n if (err) {\n reject(new Error(`Failed to upload session report: ${err.message}`));\n return;\n }\n\n if (res.statusCode && res.statusCode >= 400) {\n reject(\n new Error(`Failed to upload session report: ${res.statusCode} ${res.statusMessage}`),\n );\n return;\n }\n\n res.resume(); // Drain the response\n res.on('end', () => resolve());\n },\n );\n });\n}\n"],"mappings":"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","names":["otelContext","FormData"]}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Attributes, type Context, type Span, type Tracer, type TracerProvider } from '@opentelemetry/api';
|
|
2
2
|
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
3
|
+
import type { SessionReport } from '../voice/report.js';
|
|
3
4
|
export interface StartSpanOptions {
|
|
4
5
|
/** Name of the span */
|
|
5
6
|
name: string;
|
|
@@ -93,5 +94,21 @@ export declare function setupCloudTracer(options: {
|
|
|
93
94
|
jobId: string;
|
|
94
95
|
cloudHostname: string;
|
|
95
96
|
}): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Flush all pending Pino logs to ensure they are exported.
|
|
99
|
+
* Call this before session/job ends to ensure all logs are sent.
|
|
100
|
+
*
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
export declare function flushOtelLogs(): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Upload session report to LiveKit Cloud observability.
|
|
106
|
+
* @param options - Configuration with agentName, cloudHostname, and report
|
|
107
|
+
*/
|
|
108
|
+
export declare function uploadSessionReport(options: {
|
|
109
|
+
agentName: string;
|
|
110
|
+
cloudHostname: string;
|
|
111
|
+
report: SessionReport;
|
|
112
|
+
}): Promise<void>;
|
|
96
113
|
export {};
|
|
97
114
|
//# sourceMappingURL=traces.d.ts.map
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type Attributes, type Context, type Span, type Tracer, type TracerProvider } from '@opentelemetry/api';
|
|
2
2
|
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
3
|
+
import type { SessionReport } from '../voice/report.js';
|
|
3
4
|
export interface StartSpanOptions {
|
|
4
5
|
/** Name of the span */
|
|
5
6
|
name: string;
|
|
@@ -93,5 +94,21 @@ export declare function setupCloudTracer(options: {
|
|
|
93
94
|
jobId: string;
|
|
94
95
|
cloudHostname: string;
|
|
95
96
|
}): Promise<void>;
|
|
97
|
+
/**
|
|
98
|
+
* Flush all pending Pino logs to ensure they are exported.
|
|
99
|
+
* Call this before session/job ends to ensure all logs are sent.
|
|
100
|
+
*
|
|
101
|
+
* @internal
|
|
102
|
+
*/
|
|
103
|
+
export declare function flushOtelLogs(): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Upload session report to LiveKit Cloud observability.
|
|
106
|
+
* @param options - Configuration with agentName, cloudHostname, and report
|
|
107
|
+
*/
|
|
108
|
+
export declare function uploadSessionReport(options: {
|
|
109
|
+
agentName: string;
|
|
110
|
+
cloudHostname: string;
|
|
111
|
+
report: SessionReport;
|
|
112
|
+
}): Promise<void>;
|
|
96
113
|
export {};
|
|
97
114
|
//# sourceMappingURL=traces.d.ts.map
|