@llmrtc/llmrtc-core 1.0.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.
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Pre-built logging hooks for structured logging
3
+ *
4
+ * This module provides ready-to-use hooks that log all orchestrator and server
5
+ * events with structured timing information.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { LLMRTCServer, createLoggingHooks } from '@llmrtc/llmrtc-backend';
10
+ *
11
+ * const server = new LLMRTCServer({
12
+ * providers: { llm, stt, tts },
13
+ * hooks: createLoggingHooks({ level: 'info' })
14
+ * });
15
+ *
16
+ * // Output:
17
+ * // [llmrtc] Connection established: session=abc123
18
+ * // [llmrtc] Turn started: turn=xyz789 session=abc123
19
+ * // [llmrtc] STT completed: turn=xyz789 duration=142ms text="Hello there"
20
+ * // [llmrtc] LLM started: turn=xyz789
21
+ * // [llmrtc] LLM completed: turn=xyz789 duration=312ms
22
+ * // [llmrtc] TTS completed: turn=xyz789 duration=89ms
23
+ * // [llmrtc] Turn completed: turn=xyz789 duration=543ms
24
+ * ```
25
+ */
26
+ import type { OrchestratorHooks, ServerHooks } from './hooks.js';
27
+ /**
28
+ * Log levels in order of verbosity
29
+ */
30
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
31
+ /**
32
+ * Logger interface compatible with console
33
+ */
34
+ export interface LoggerLike {
35
+ debug: (...args: unknown[]) => void;
36
+ info: (...args: unknown[]) => void;
37
+ log: (...args: unknown[]) => void;
38
+ warn: (...args: unknown[]) => void;
39
+ error: (...args: unknown[]) => void;
40
+ }
41
+ /**
42
+ * Configuration for createLoggingHooks
43
+ */
44
+ export interface LoggingHooksConfig {
45
+ /**
46
+ * Custom logger instance (default: console)
47
+ * Must implement debug, info, log, warn, error methods
48
+ */
49
+ logger?: LoggerLike;
50
+ /**
51
+ * Minimum log level (default: 'info')
52
+ * - 'debug': Log everything including chunk details
53
+ * - 'info': Log turn lifecycle and timing
54
+ * - 'warn': Log warnings and errors only
55
+ * - 'error': Log errors only
56
+ */
57
+ level?: LogLevel;
58
+ /**
59
+ * Include payloads in logs (default: false)
60
+ * When true, includes STT text, LLM request/response content, etc.
61
+ * Warning: May log sensitive data
62
+ */
63
+ includePayloads?: boolean;
64
+ /**
65
+ * Log prefix (default: '[llmrtc]')
66
+ */
67
+ prefix?: string;
68
+ /**
69
+ * Include timestamps in logs (default: true)
70
+ */
71
+ includeTimestamp?: boolean;
72
+ }
73
+ /**
74
+ * Create a set of logging hooks for structured logging
75
+ *
76
+ * @param config - Configuration options
77
+ * @returns Combined OrchestratorHooks and ServerHooks
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * // Basic usage with console logging
82
+ * const hooks = createLoggingHooks();
83
+ *
84
+ * // Custom logger and level
85
+ * const hooks = createLoggingHooks({
86
+ * logger: pino(),
87
+ * level: 'debug',
88
+ * includePayloads: true
89
+ * });
90
+ *
91
+ * // Use with LLMRTCServer
92
+ * const server = new LLMRTCServer({
93
+ * providers: { llm, stt, tts },
94
+ * hooks: {
95
+ * ...createLoggingHooks({ level: 'info' }),
96
+ * // Add custom hook
97
+ * onLLMEnd(ctx, result) {
98
+ * // Custom guardrail
99
+ * if (result.fullText.includes('forbidden')) {
100
+ * throw new Error('Content policy violation');
101
+ * }
102
+ * }
103
+ * }
104
+ * });
105
+ * ```
106
+ */
107
+ export declare function createLoggingHooks(config?: LoggingHooksConfig): OrchestratorHooks & ServerHooks;
108
+ /**
109
+ * Create a minimal logging hook that only logs errors
110
+ */
111
+ export declare function createErrorOnlyHooks(config?: Omit<LoggingHooksConfig, 'level'>): OrchestratorHooks & ServerHooks;
112
+ /**
113
+ * Create a verbose logging hook that logs everything including chunks
114
+ */
115
+ export declare function createVerboseHooks(config?: Omit<LoggingHooksConfig, 'level' | 'includePayloads'>): OrchestratorHooks & ServerHooks;
116
+ /**
117
+ * Create timing-only hooks that just emit timing metrics without logging
118
+ * Useful when you want metrics without log noise
119
+ */
120
+ export declare function createTimingHooks(): Pick<OrchestratorHooks, 'onSTTEnd' | 'onLLMEnd' | 'onTTSEnd' | 'onTurnEnd'>;
@@ -0,0 +1,280 @@
1
+ /**
2
+ * Pre-built logging hooks for structured logging
3
+ *
4
+ * This module provides ready-to-use hooks that log all orchestrator and server
5
+ * events with structured timing information.
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * import { LLMRTCServer, createLoggingHooks } from '@llmrtc/llmrtc-backend';
10
+ *
11
+ * const server = new LLMRTCServer({
12
+ * providers: { llm, stt, tts },
13
+ * hooks: createLoggingHooks({ level: 'info' })
14
+ * });
15
+ *
16
+ * // Output:
17
+ * // [llmrtc] Connection established: session=abc123
18
+ * // [llmrtc] Turn started: turn=xyz789 session=abc123
19
+ * // [llmrtc] STT completed: turn=xyz789 duration=142ms text="Hello there"
20
+ * // [llmrtc] LLM started: turn=xyz789
21
+ * // [llmrtc] LLM completed: turn=xyz789 duration=312ms
22
+ * // [llmrtc] TTS completed: turn=xyz789 duration=89ms
23
+ * // [llmrtc] Turn completed: turn=xyz789 duration=543ms
24
+ * ```
25
+ */
26
+ // =============================================================================
27
+ // Log Level Utilities
28
+ // =============================================================================
29
+ const LOG_LEVEL_ORDER = {
30
+ debug: 0,
31
+ info: 1,
32
+ warn: 2,
33
+ error: 3
34
+ };
35
+ function shouldLog(configLevel, messageLevel) {
36
+ return LOG_LEVEL_ORDER[messageLevel] >= LOG_LEVEL_ORDER[configLevel];
37
+ }
38
+ // =============================================================================
39
+ // Factory Function
40
+ // =============================================================================
41
+ /**
42
+ * Create a set of logging hooks for structured logging
43
+ *
44
+ * @param config - Configuration options
45
+ * @returns Combined OrchestratorHooks and ServerHooks
46
+ *
47
+ * @example
48
+ * ```typescript
49
+ * // Basic usage with console logging
50
+ * const hooks = createLoggingHooks();
51
+ *
52
+ * // Custom logger and level
53
+ * const hooks = createLoggingHooks({
54
+ * logger: pino(),
55
+ * level: 'debug',
56
+ * includePayloads: true
57
+ * });
58
+ *
59
+ * // Use with LLMRTCServer
60
+ * const server = new LLMRTCServer({
61
+ * providers: { llm, stt, tts },
62
+ * hooks: {
63
+ * ...createLoggingHooks({ level: 'info' }),
64
+ * // Add custom hook
65
+ * onLLMEnd(ctx, result) {
66
+ * // Custom guardrail
67
+ * if (result.fullText.includes('forbidden')) {
68
+ * throw new Error('Content policy violation');
69
+ * }
70
+ * }
71
+ * }
72
+ * });
73
+ * ```
74
+ */
75
+ export function createLoggingHooks(config = {}) {
76
+ const { logger = console, level = 'info', includePayloads = false, prefix = '[llmrtc]', includeTimestamp = true } = config;
77
+ const formatLog = (message, data) => {
78
+ const timestamp = includeTimestamp ? new Date().toISOString() + ' ' : '';
79
+ const dataStr = data ? ' ' + Object.entries(data)
80
+ .map(([k, v]) => `${k}=${typeof v === 'string' ? v : JSON.stringify(v)}`)
81
+ .join(' ') : '';
82
+ return `${timestamp}${prefix} ${message}${dataStr}`;
83
+ };
84
+ const log = (messageLevel, message, data) => {
85
+ if (!shouldLog(level, messageLevel))
86
+ return;
87
+ const formatted = formatLog(message, data);
88
+ switch (messageLevel) {
89
+ case 'debug':
90
+ logger.debug(formatted);
91
+ break;
92
+ case 'info':
93
+ logger.info ? logger.info(formatted) : logger.log(formatted);
94
+ break;
95
+ case 'warn':
96
+ logger.warn(formatted);
97
+ break;
98
+ case 'error':
99
+ logger.error(formatted);
100
+ break;
101
+ }
102
+ };
103
+ return {
104
+ // =========================================================================
105
+ // Server Hooks
106
+ // =========================================================================
107
+ onConnection(sessionId, connectionId) {
108
+ log('info', 'Connection established', { session: sessionId, connection: connectionId });
109
+ },
110
+ onDisconnect(sessionId, timing) {
111
+ log('info', 'Connection closed', {
112
+ session: sessionId,
113
+ duration: `${timing.durationMs}ms`
114
+ });
115
+ },
116
+ onSpeechStart(sessionId, timestamp) {
117
+ log('debug', 'Speech started', { session: sessionId, timestamp });
118
+ },
119
+ onSpeechEnd(sessionId, timestamp, audioDurationMs) {
120
+ log('debug', 'Speech ended', {
121
+ session: sessionId,
122
+ audioDuration: `${audioDurationMs}ms`
123
+ });
124
+ },
125
+ onError(error, context) {
126
+ log('error', `Error in ${context.component}`, {
127
+ code: context.code,
128
+ message: error.message,
129
+ session: context.sessionId,
130
+ turn: context.turnId
131
+ });
132
+ },
133
+ // =========================================================================
134
+ // Turn Lifecycle Hooks
135
+ // =========================================================================
136
+ onTurnStart(ctx, audio) {
137
+ log('info', 'Turn started', {
138
+ turn: ctx.turnId,
139
+ session: ctx.sessionId,
140
+ audioSize: `${audio.length}b`
141
+ });
142
+ },
143
+ onTurnEnd(ctx, timing) {
144
+ log('info', 'Turn completed', {
145
+ turn: ctx.turnId,
146
+ duration: `${timing.durationMs}ms`
147
+ });
148
+ },
149
+ // =========================================================================
150
+ // STT Hooks
151
+ // =========================================================================
152
+ onSTTStart(ctx, audio) {
153
+ log('debug', 'STT started', {
154
+ turn: ctx.turnId,
155
+ audioSize: `${audio.length}b`
156
+ });
157
+ },
158
+ onSTTEnd(ctx, result, timing) {
159
+ const data = {
160
+ turn: ctx.turnId,
161
+ duration: `${timing.durationMs}ms`
162
+ };
163
+ if (includePayloads) {
164
+ data.text = result.text;
165
+ }
166
+ log('info', 'STT completed', data);
167
+ },
168
+ onSTTError(ctx, error) {
169
+ log('error', 'STT failed', {
170
+ turn: ctx.turnId,
171
+ error: error.message
172
+ });
173
+ },
174
+ // =========================================================================
175
+ // LLM Hooks
176
+ // =========================================================================
177
+ onLLMStart(ctx, request) {
178
+ const data = {
179
+ turn: ctx.turnId,
180
+ messages: request.messages.length
181
+ };
182
+ if (includePayloads && request.messages.length > 0) {
183
+ const lastMsg = request.messages[request.messages.length - 1];
184
+ data.lastMessage = lastMsg.content.slice(0, 100);
185
+ }
186
+ log('debug', 'LLM started', data);
187
+ },
188
+ onLLMChunk(ctx, chunk, chunkIndex) {
189
+ if (chunkIndex === 0) {
190
+ log('debug', 'LLM first chunk received', { turn: ctx.turnId });
191
+ }
192
+ },
193
+ onLLMEnd(ctx, result, timing) {
194
+ const data = {
195
+ turn: ctx.turnId,
196
+ duration: `${timing.durationMs}ms`,
197
+ chars: result.fullText.length
198
+ };
199
+ if (includePayloads) {
200
+ data.response = result.fullText.slice(0, 200);
201
+ }
202
+ log('info', 'LLM completed', data);
203
+ },
204
+ onLLMError(ctx, error) {
205
+ log('error', 'LLM failed', {
206
+ turn: ctx.turnId,
207
+ error: error.message
208
+ });
209
+ },
210
+ // =========================================================================
211
+ // TTS Hooks
212
+ // =========================================================================
213
+ onTTSStart(ctx, text) {
214
+ const data = {
215
+ turn: ctx.turnId,
216
+ textLength: text.length
217
+ };
218
+ if (includePayloads) {
219
+ data.text = text.slice(0, 100);
220
+ }
221
+ log('debug', 'TTS started', data);
222
+ },
223
+ onTTSChunk(ctx, chunk, chunkIndex) {
224
+ log('debug', 'TTS chunk', {
225
+ turn: ctx.turnId,
226
+ chunk: chunkIndex,
227
+ size: `${chunk.audio.length}b`
228
+ });
229
+ },
230
+ onTTSEnd(ctx, timing) {
231
+ log('info', 'TTS completed', {
232
+ turn: ctx.turnId,
233
+ duration: `${timing.durationMs}ms`
234
+ });
235
+ },
236
+ onTTSError(ctx, error) {
237
+ log('error', 'TTS failed', {
238
+ turn: ctx.turnId,
239
+ error: error.message
240
+ });
241
+ }
242
+ };
243
+ }
244
+ // =============================================================================
245
+ // Specialized Loggers
246
+ // =============================================================================
247
+ /**
248
+ * Create a minimal logging hook that only logs errors
249
+ */
250
+ export function createErrorOnlyHooks(config = {}) {
251
+ return createLoggingHooks({ ...config, level: 'error' });
252
+ }
253
+ /**
254
+ * Create a verbose logging hook that logs everything including chunks
255
+ */
256
+ export function createVerboseHooks(config = {}) {
257
+ return createLoggingHooks({ ...config, level: 'debug', includePayloads: true });
258
+ }
259
+ /**
260
+ * Create timing-only hooks that just emit timing metrics without logging
261
+ * Useful when you want metrics without log noise
262
+ */
263
+ export function createTimingHooks() {
264
+ return {
265
+ onSTTEnd(_ctx, _result, timing) {
266
+ // Just track timing, no logging
267
+ void timing;
268
+ },
269
+ onLLMEnd(_ctx, _result, timing) {
270
+ void timing;
271
+ },
272
+ onTTSEnd(_ctx, timing) {
273
+ void timing;
274
+ },
275
+ onTurnEnd(_ctx, timing) {
276
+ void timing;
277
+ }
278
+ };
279
+ }
280
+ //# sourceMappingURL=logging-hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logging-hooks.js","sourceRoot":"","sources":["../src/logging-hooks.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AA8DH,gFAAgF;AAChF,sBAAsB;AACtB,gFAAgF;AAEhF,MAAM,eAAe,GAA6B;IAChD,KAAK,EAAE,CAAC;IACR,IAAI,EAAE,CAAC;IACP,IAAI,EAAE,CAAC;IACP,KAAK,EAAE,CAAC;CACT,CAAC;AAEF,SAAS,SAAS,CAAC,WAAqB,EAAE,YAAsB;IAC9D,OAAO,eAAe,CAAC,YAAY,CAAC,IAAI,eAAe,CAAC,WAAW,CAAC,CAAC;AACvE,CAAC;AAED,gFAAgF;AAChF,mBAAmB;AACnB,gFAAgF;AAEhF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAA6B,EAAE;IAE/B,MAAM,EACJ,MAAM,GAAG,OAAO,EAChB,KAAK,GAAG,MAAM,EACd,eAAe,GAAG,KAAK,EACvB,MAAM,GAAG,UAAU,EACnB,gBAAgB,GAAG,IAAI,EACxB,GAAG,MAAM,CAAC;IAEX,MAAM,SAAS,GAAG,CAAC,OAAe,EAAE,IAA8B,EAAU,EAAE;QAC5E,MAAM,SAAS,GAAG,gBAAgB,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACzE,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC;aAC9C,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,OAAO,CAAC,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;aACxE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAClB,OAAO,GAAG,SAAS,GAAG,MAAM,IAAI,OAAO,GAAG,OAAO,EAAE,CAAC;IACtD,CAAC,CAAC;IAEF,MAAM,GAAG,GAAG,CAAC,YAAsB,EAAE,OAAe,EAAE,IAA8B,EAAE,EAAE;QACtF,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,YAAY,CAAC;YAAE,OAAO;QAE5C,MAAM,SAAS,GAAG,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC;QAC3C,QAAQ,YAAY,EAAE,CAAC;YACrB,KAAK,OAAO;gBACV,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACxB,MAAM;YACR,KAAK,MAAM;gBACT,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBAC7D,MAAM;YACR,KAAK,MAAM;gBACT,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACvB,MAAM;YACR,KAAK,OAAO;gBACV,MAAM,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;gBACxB,MAAM;QACV,CAAC;IACH,CAAC,CAAC;IAEF,OAAO;QACL,4EAA4E;QAC5E,eAAe;QACf,4EAA4E;QAE5E,YAAY,CAAC,SAAiB,EAAE,YAAoB;YAClD,GAAG,CAAC,MAAM,EAAE,wBAAwB,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,YAAY,EAAE,CAAC,CAAC;QAC1F,CAAC;QAED,YAAY,CAAC,SAAiB,EAAE,MAAkB;YAChD,GAAG,CAAC,MAAM,EAAE,mBAAmB,EAAE;gBAC/B,OAAO,EAAE,SAAS;gBAClB,QAAQ,EAAE,GAAG,MAAM,CAAC,UAAU,IAAI;aACnC,CAAC,CAAC;QACL,CAAC;QAED,aAAa,CAAC,SAAiB,EAAE,SAAiB;YAChD,GAAG,CAAC,OAAO,EAAE,gBAAgB,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,CAAC,CAAC;QACpE,CAAC;QAED,WAAW,CAAC,SAAiB,EAAE,SAAiB,EAAE,eAAuB;YACvE,GAAG,CAAC,OAAO,EAAE,cAAc,EAAE;gBAC3B,OAAO,EAAE,SAAS;gBAClB,aAAa,EAAE,GAAG,eAAe,IAAI;aACtC,CAAC,CAAC;QACL,CAAC;QAED,OAAO,CAAC,KAAY,EAAE,OAAqB;YACzC,GAAG,CAAC,OAAO,EAAE,YAAY,OAAO,CAAC,SAAS,EAAE,EAAE;gBAC5C,IAAI,EAAE,OAAO,CAAC,IAAI;gBAClB,OAAO,EAAE,KAAK,CAAC,OAAO;gBACtB,OAAO,EAAE,OAAO,CAAC,SAAS;gBAC1B,IAAI,EAAE,OAAO,CAAC,MAAM;aACrB,CAAC,CAAC;QACL,CAAC;QAED,4EAA4E;QAC5E,uBAAuB;QACvB,4EAA4E;QAE5E,WAAW,CAAC,GAAgB,EAAE,KAAa;YACzC,GAAG,CAAC,MAAM,EAAE,cAAc,EAAE;gBAC1B,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,OAAO,EAAE,GAAG,CAAC,SAAS;gBACtB,SAAS,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG;aAC9B,CAAC,CAAC;QACL,CAAC;QAED,SAAS,CAAC,GAAgB,EAAE,MAAkB;YAC5C,GAAG,CAAC,MAAM,EAAE,gBAAgB,EAAE;gBAC5B,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,QAAQ,EAAE,GAAG,MAAM,CAAC,UAAU,IAAI;aACnC,CAAC,CAAC;QACL,CAAC;QAED,4EAA4E;QAC5E,YAAY;QACZ,4EAA4E;QAE5E,UAAU,CAAC,GAAgB,EAAE,KAAa;YACxC,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE;gBAC1B,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,SAAS,EAAE,GAAG,KAAK,CAAC,MAAM,GAAG;aAC9B,CAAC,CAAC;QACL,CAAC;QAED,QAAQ,CAAC,GAAgB,EAAE,MAAiB,EAAE,MAAkB;YAC9D,MAAM,IAAI,GAA4B;gBACpC,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,QAAQ,EAAE,GAAG,MAAM,CAAC,UAAU,IAAI;aACnC,CAAC;YACF,IAAI,eAAe,EAAE,CAAC;gBACpB,IAAI,CAAC,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC;YAC1B,CAAC;YACD,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;QAED,UAAU,CAAC,GAAgB,EAAE,KAAY;YACvC,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE;gBACzB,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,KAAK,EAAE,KAAK,CAAC,OAAO;aACrB,CAAC,CAAC;QACL,CAAC;QAED,4EAA4E;QAC5E,YAAY;QACZ,4EAA4E;QAE5E,UAAU,CAAC,GAAgB,EAAE,OAAmB;YAC9C,MAAM,IAAI,GAA4B;gBACpC,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,QAAQ,EAAE,OAAO,CAAC,QAAQ,CAAC,MAAM;aAClC,CAAC;YACF,IAAI,eAAe,IAAI,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACnD,MAAM,OAAO,GAAG,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;gBAC9D,IAAI,CAAC,WAAW,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACnD,CAAC;YACD,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;QACpC,CAAC;QAED,UAAU,CAAC,GAAgB,EAAE,KAAe,EAAE,UAAkB;YAC9D,IAAI,UAAU,KAAK,CAAC,EAAE,CAAC;gBACrB,GAAG,CAAC,OAAO,EAAE,0BAA0B,EAAE,EAAE,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC;YACjE,CAAC;QACH,CAAC;QAED,QAAQ,CAAC,GAAgB,EAAE,MAAiB,EAAE,MAAkB;YAC9D,MAAM,IAAI,GAA4B;gBACpC,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,QAAQ,EAAE,GAAG,MAAM,CAAC,UAAU,IAAI;gBAClC,KAAK,EAAE,MAAM,CAAC,QAAQ,CAAC,MAAM;aAC9B,CAAC;YACF,IAAI,eAAe,EAAE,CAAC;gBACpB,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAChD,CAAC;YACD,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE,IAAI,CAAC,CAAC;QACrC,CAAC;QAED,UAAU,CAAC,GAAgB,EAAE,KAAY;YACvC,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE;gBACzB,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,KAAK,EAAE,KAAK,CAAC,OAAO;aACrB,CAAC,CAAC;QACL,CAAC;QAED,4EAA4E;QAC5E,YAAY;QACZ,4EAA4E;QAE5E,UAAU,CAAC,GAAgB,EAAE,IAAY;YACvC,MAAM,IAAI,GAA4B;gBACpC,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,UAAU,EAAE,IAAI,CAAC,MAAM;aACxB,CAAC;YACF,IAAI,eAAe,EAAE,CAAC;gBACpB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YACjC,CAAC;YACD,GAAG,CAAC,OAAO,EAAE,aAAa,EAAE,IAAI,CAAC,CAAC;QACpC,CAAC;QAED,UAAU,CAAC,GAAgB,EAAE,KAAe,EAAE,UAAkB;YAC9D,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE;gBACxB,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,KAAK,EAAE,UAAU;gBACjB,IAAI,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG;aAC/B,CAAC,CAAC;QACL,CAAC;QAED,QAAQ,CAAC,GAAgB,EAAE,MAAkB;YAC3C,GAAG,CAAC,MAAM,EAAE,eAAe,EAAE;gBAC3B,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,QAAQ,EAAE,GAAG,MAAM,CAAC,UAAU,IAAI;aACnC,CAAC,CAAC;QACL,CAAC;QAED,UAAU,CAAC,GAAgB,EAAE,KAAY;YACvC,GAAG,CAAC,OAAO,EAAE,YAAY,EAAE;gBACzB,IAAI,EAAE,GAAG,CAAC,MAAM;gBAChB,KAAK,EAAE,KAAK,CAAC,OAAO;aACrB,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,gFAAgF;AAChF,sBAAsB;AACtB,gFAAgF;AAEhF;;GAEG;AACH,MAAM,UAAU,oBAAoB,CAClC,SAA4C,EAAE;IAE9C,OAAO,kBAAkB,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,CAAC,CAAC;AAC3D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,SAAgE,EAAE;IAElE,OAAO,kBAAkB,CAAC,EAAE,GAAG,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAC;AAClF,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB;IAI/B,OAAO;QACL,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM;YAC5B,gCAAgC;YAChC,KAAK,MAAM,CAAC;QACd,CAAC;QACD,QAAQ,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM;YAC5B,KAAK,MAAM,CAAC;QACd,CAAC;QACD,QAAQ,CAAC,IAAI,EAAE,MAAM;YACnB,KAAK,MAAM,CAAC;QACd,CAAC;QACD,SAAS,CAAC,IAAI,EAAE,MAAM;YACpB,KAAK,MAAM,CAAC;QACd,CAAC;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * Metrics adapter interface for pluggable metrics reporting
3
+ *
4
+ * This module provides a simple interface for emitting metrics that can be
5
+ * implemented by various backends (Prometheus, StatsD, CloudWatch, etc.)
6
+ *
7
+ * @example
8
+ * ```typescript
9
+ * // Using console metrics for debugging
10
+ * const server = new LLMRTCServer({
11
+ * providers: { llm, stt, tts },
12
+ * metrics: new ConsoleMetrics()
13
+ * });
14
+ *
15
+ * // Using a custom Prometheus adapter
16
+ * class PrometheusMetrics implements MetricsAdapter {
17
+ * private histogram = new promClient.Histogram({
18
+ * name: 'llmrtc_duration_ms',
19
+ * help: 'Operation duration in milliseconds',
20
+ * labelNames: ['operation']
21
+ * });
22
+ *
23
+ * timing(name: string, durationMs: number, tags?: Record<string, string>) {
24
+ * this.histogram.observe({ operation: name, ...tags }, durationMs);
25
+ * }
26
+ * // ... other methods
27
+ * }
28
+ * ```
29
+ */
30
+ /**
31
+ * Standard metric names used by the SDK
32
+ *
33
+ * Integrators can use these constants when building dashboards or alerts:
34
+ *
35
+ * ```typescript
36
+ * // Example Prometheus query
37
+ * // histogram_quantile(0.95, rate(llmrtc_stt_duration_ms_bucket[5m]))
38
+ * ```
39
+ */
40
+ export declare const MetricNames: {
41
+ /** Duration of STT transcription in milliseconds */
42
+ readonly STT_DURATION: "llmrtc.stt.duration_ms";
43
+ /** Time to first LLM token in milliseconds */
44
+ readonly LLM_TTFT: "llmrtc.llm.ttft_ms";
45
+ /** Total LLM inference duration in milliseconds */
46
+ readonly LLM_DURATION: "llmrtc.llm.duration_ms";
47
+ /** Number of tokens generated (when available from provider) */
48
+ readonly LLM_TOKENS: "llmrtc.llm.tokens";
49
+ /** Duration of TTS synthesis in milliseconds */
50
+ readonly TTS_DURATION: "llmrtc.tts.duration_ms";
51
+ /** Total turn duration (STT + LLM + TTS) in milliseconds */
52
+ readonly TURN_DURATION: "llmrtc.turn.duration_ms";
53
+ /** Session duration in milliseconds */
54
+ readonly SESSION_DURATION: "llmrtc.session.duration_ms";
55
+ /** Number of active sessions (gauge) */
56
+ readonly ACTIVE_SESSIONS: "llmrtc.sessions.active";
57
+ /** Total turns per session */
58
+ readonly SESSION_TURNS: "llmrtc.session.turns";
59
+ /** Error counter by type/component */
60
+ readonly ERRORS: "llmrtc.errors";
61
+ /** Number of active WebSocket connections */
62
+ readonly CONNECTIONS: "llmrtc.connections.active";
63
+ /** Reconnection attempts */
64
+ readonly RECONNECTIONS: "llmrtc.reconnections";
65
+ /** Tool execution duration in milliseconds */
66
+ readonly TOOL_DURATION: "llmrtc.tool.duration_ms";
67
+ /** Number of tool calls */
68
+ readonly TOOL_CALLS: "llmrtc.tool.calls";
69
+ /** Tool call errors */
70
+ readonly TOOL_ERRORS: "llmrtc.tool.errors";
71
+ /** Stage duration in milliseconds */
72
+ readonly STAGE_DURATION: "llmrtc.playbook.stage.duration_ms";
73
+ /** Number of stage transitions */
74
+ readonly STAGE_TRANSITIONS: "llmrtc.playbook.transitions";
75
+ /** Turns per stage */
76
+ readonly STAGE_TURNS: "llmrtc.playbook.stage.turns";
77
+ /** Current playbook stage (gauge) */
78
+ readonly CURRENT_STAGE: "llmrtc.playbook.stage.current";
79
+ /** Playbook completion counter */
80
+ readonly PLAYBOOK_COMPLETIONS: "llmrtc.playbook.completions";
81
+ };
82
+ export type MetricName = typeof MetricNames[keyof typeof MetricNames];
83
+ /**
84
+ * Interface for metrics adapters
85
+ *
86
+ * Implement this interface to integrate with your preferred metrics backend.
87
+ * All methods should be non-blocking and fail silently.
88
+ */
89
+ export interface MetricsAdapter {
90
+ /**
91
+ * Increment a counter metric
92
+ * @param name - Metric name (use MetricNames constants)
93
+ * @param value - Value to increment by (default: 1)
94
+ * @param tags - Optional key-value tags for dimensions
95
+ *
96
+ * @example
97
+ * ```typescript
98
+ * metrics.increment(MetricNames.ERRORS, 1, { component: 'stt', code: 'STT_ERROR' });
99
+ * ```
100
+ */
101
+ increment(name: string, value?: number, tags?: Record<string, string>): void;
102
+ /**
103
+ * Record a timing/histogram metric
104
+ * @param name - Metric name (use MetricNames constants)
105
+ * @param durationMs - Duration in milliseconds
106
+ * @param tags - Optional key-value tags for dimensions
107
+ *
108
+ * @example
109
+ * ```typescript
110
+ * metrics.timing(MetricNames.STT_DURATION, 150, { provider: 'whisper' });
111
+ * ```
112
+ */
113
+ timing(name: string, durationMs: number, tags?: Record<string, string>): void;
114
+ /**
115
+ * Set a gauge metric (current value)
116
+ * @param name - Metric name (use MetricNames constants)
117
+ * @param value - Current value
118
+ * @param tags - Optional key-value tags for dimensions
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * metrics.gauge(MetricNames.ACTIVE_SESSIONS, sessionCount);
123
+ * ```
124
+ */
125
+ gauge(name: string, value: number, tags?: Record<string, string>): void;
126
+ }
127
+ /**
128
+ * No-op metrics adapter (default)
129
+ *
130
+ * Use this when metrics collection is disabled or not needed.
131
+ * All methods are no-ops with minimal overhead.
132
+ */
133
+ export declare class NoopMetrics implements MetricsAdapter {
134
+ increment(): void;
135
+ timing(): void;
136
+ gauge(): void;
137
+ }
138
+ /**
139
+ * Console metrics adapter for debugging
140
+ *
141
+ * Logs all metrics to the console with timestamps.
142
+ * Useful during development to verify metrics are being emitted correctly.
143
+ *
144
+ * @example
145
+ * ```typescript
146
+ * const server = new LLMRTCServer({
147
+ * providers: { llm, stt, tts },
148
+ * metrics: new ConsoleMetrics({ prefix: 'myapp' })
149
+ * });
150
+ *
151
+ * // Output:
152
+ * // [metric] myapp.llmrtc.stt.duration_ms 150ms { provider: 'whisper' }
153
+ * // [metric] myapp.llmrtc.llm.duration_ms 320ms { model: 'gpt-4' }
154
+ * ```
155
+ */
156
+ export declare class ConsoleMetrics implements MetricsAdapter {
157
+ private prefix;
158
+ constructor(options?: {
159
+ prefix?: string;
160
+ });
161
+ increment(name: string, value?: number, tags?: Record<string, string>): void;
162
+ timing(name: string, durationMs: number, tags?: Record<string, string>): void;
163
+ gauge(name: string, value: number, tags?: Record<string, string>): void;
164
+ }
165
+ /**
166
+ * In-memory metrics collector for testing
167
+ *
168
+ * Stores all metrics in arrays for later inspection.
169
+ * Useful in unit tests to verify metrics are emitted correctly.
170
+ *
171
+ * @example
172
+ * ```typescript
173
+ * const metrics = new InMemoryMetrics();
174
+ * const orchestrator = new ConversationOrchestrator({
175
+ * providers: { llm, stt, tts },
176
+ * metrics
177
+ * });
178
+ *
179
+ * // Run orchestrator...
180
+ *
181
+ * // Verify metrics
182
+ * expect(metrics.timings.find(t => t.name === MetricNames.STT_DURATION)).toBeDefined();
183
+ * expect(metrics.getLatestTiming(MetricNames.STT_DURATION)?.durationMs).toBeLessThan(200);
184
+ * ```
185
+ */
186
+ export declare class InMemoryMetrics implements MetricsAdapter {
187
+ readonly counters: Array<{
188
+ name: string;
189
+ value: number;
190
+ tags?: Record<string, string>;
191
+ timestamp: number;
192
+ }>;
193
+ readonly timings: Array<{
194
+ name: string;
195
+ durationMs: number;
196
+ tags?: Record<string, string>;
197
+ timestamp: number;
198
+ }>;
199
+ readonly gauges: Array<{
200
+ name: string;
201
+ value: number;
202
+ tags?: Record<string, string>;
203
+ timestamp: number;
204
+ }>;
205
+ increment(name: string, value?: number, tags?: Record<string, string>): void;
206
+ timing(name: string, durationMs: number, tags?: Record<string, string>): void;
207
+ gauge(name: string, value: number, tags?: Record<string, string>): void;
208
+ /**
209
+ * Get the latest counter entry for a metric name
210
+ */
211
+ getLatestCounter(name: string): {
212
+ name: string;
213
+ value: number;
214
+ tags?: Record<string, string>;
215
+ timestamp: number;
216
+ } | undefined;
217
+ /**
218
+ * Get the latest timing entry for a metric name
219
+ */
220
+ getLatestTiming(name: string): {
221
+ name: string;
222
+ durationMs: number;
223
+ tags?: Record<string, string>;
224
+ timestamp: number;
225
+ } | undefined;
226
+ /**
227
+ * Get the latest gauge entry for a metric name
228
+ */
229
+ getLatestGauge(name: string): {
230
+ name: string;
231
+ value: number;
232
+ tags?: Record<string, string>;
233
+ timestamp: number;
234
+ } | undefined;
235
+ /**
236
+ * Get sum of all counter increments for a metric name
237
+ */
238
+ getCounterSum(name: string): number;
239
+ /**
240
+ * Clear all stored metrics
241
+ */
242
+ clear(): void;
243
+ }