@@ -6,7 +6,6 @@ import { fetchVoiceToken } from '@/api/voice'
66import type { VoiceSession , VoiceSessionConfig , ConversationStatus , StatusCallback } from './types'
77import type { ApiClient } from '@/api/client'
88import type { Session } from '@/types/api'
9- import { DEFAULT_VOICE_ID } from '@/lib/voices'
109
1110// Debug logging
1211const DEBUG = import . meta. env . DEV
@@ -16,28 +15,6 @@ let conversationInstance: ReturnType<typeof useConversation> | null = null
1615
1716// Store reference for status updates
1817let statusCallback : StatusCallback | null = null
19- let telemetryApi : ApiClient | null = null
20- let activeVoiceContext : {
21- sessionId ?: string
22- voiceId ?: string
23- language ?: string
24- } = { }
25-
26- async function emitVoiceTelemetry ( event : {
27- stage : string
28- message : string
29- sessionId ?: string
30- voiceId ?: string
31- language ?: string
32- details ?: Record < string , unknown >
33- } ) : Promise < void > {
34- if ( ! telemetryApi ) return
35- try {
36- await telemetryApi . sendVoiceTelemetry ( event )
37- } catch {
38- // Telemetry must not break voice flows
39- }
40- }
4118
4219// Global voice session implementation
4320class RealtimeVoiceSessionImpl implements VoiceSession {
@@ -47,41 +24,10 @@ class RealtimeVoiceSessionImpl implements VoiceSession {
4724 this . api = api
4825 }
4926
50- private async sendTelemetry ( event : {
51- stage : string
52- message : string
53- sessionId ?: string
54- voiceId ?: string
55- language ?: string
56- details ?: Record < string , unknown >
57- } ) : Promise < void > {
58- await emitVoiceTelemetry ( event )
59- }
60-
6127 async startSession ( config : VoiceSessionConfig ) : Promise < void > {
62- activeVoiceContext = {
63- sessionId : config . sessionId ,
64- voiceId : config . voiceId ,
65- language : config . language
66- }
67- await this . sendTelemetry ( {
68- stage : 'start-session' ,
69- message : 'Voice start requested' ,
70- sessionId : config . sessionId ,
71- voiceId : config . voiceId ,
72- language : config . language
73- } )
74-
7528 if ( ! conversationInstance ) {
7629 const error = new Error ( 'Realtime voice session not initialized' )
7730 console . warn ( '[Voice] Realtime voice session not initialized' )
78- await this . sendTelemetry ( {
79- stage : 'init-missing' ,
80- message : error . message ,
81- sessionId : config . sessionId ,
82- voiceId : config . voiceId ,
83- language : config . language
84- } )
8531 statusCallback ?.( 'error' , 'Voice session not initialized' )
8632 throw error
8733 }
@@ -94,13 +40,6 @@ class RealtimeVoiceSessionImpl implements VoiceSession {
9440 permissionStream = await navigator . mediaDevices . getUserMedia ( { audio : true } )
9541 } catch ( error ) {
9642 console . error ( '[Voice] Failed to get microphone permission:' , error )
97- await this . sendTelemetry ( {
98- stage : 'mic-permission-denied' ,
99- message : error instanceof Error ? error . message : String ( error ) ,
100- sessionId : config . sessionId ,
101- voiceId : config . voiceId ,
102- language : config . language
103- } )
10443 statusCallback ?.( 'error' , 'Microphone permission denied' )
10544 throw error
10645 } finally {
@@ -115,29 +54,12 @@ class RealtimeVoiceSessionImpl implements VoiceSession {
11554 } )
11655 } catch ( error ) {
11756 console . error ( '[Voice] Failed to fetch voice token:' , error )
118- await this . sendTelemetry ( {
119- stage : 'token-fetch-error' ,
120- message : error instanceof Error ? error . message : String ( error ) ,
121- sessionId : config . sessionId ,
122- voiceId : config . voiceId ,
123- language : config . language
124- } )
12557 statusCallback ?.( 'error' , 'Network error' )
12658 throw error
12759 }
12860 if ( ! tokenResponse . allowed || ! tokenResponse . token ) {
12961 const error = new Error ( tokenResponse . error ?? 'Voice not allowed or no token' )
13062 console . error ( '[Voice] Voice not allowed or no token:' , tokenResponse . error )
131- await this . sendTelemetry ( {
132- stage : 'token-not-allowed' ,
133- message : tokenResponse . error ?? 'Voice not allowed' ,
134- sessionId : config . sessionId ,
135- voiceId : config . voiceId ,
136- language : config . language ,
137- details : {
138- agentId : tokenResponse . agentId
139- }
140- } )
14163 statusCallback ?.( 'error' , tokenResponse . error ?? 'Voice not allowed' )
14264 throw error
14365 }
@@ -158,32 +80,13 @@ class RealtimeVoiceSessionImpl implements VoiceSession {
15880 }
15981 }
16082
161- await this . sendTelemetry ( {
162- stage : 'override-decision' ,
163- message : 'Skipping runtime voice override; using token-selected agent voice' ,
164- sessionId : config . sessionId ,
165- voiceId : config . voiceId ,
166- language : config . language ,
167- details : {
168- defaultVoiceId : DEFAULT_VOICE_ID ,
169- selectedVoiceId : config . voiceId
170- }
171- } )
172-
17383 // Use conversation token from server (private agent flow)
17484 try {
17585 const conversationId = await conversationInstance . startSession ( baseSessionConfig )
17686
17787 if ( DEBUG ) {
17888 console . log ( '[Voice] Started conversation with ID:' , conversationId )
17989 }
180- await this . sendTelemetry ( {
181- stage : 'start-success' ,
182- message : 'Voice session started successfully' ,
183- sessionId : config . sessionId ,
184- voiceId : config . voiceId ,
185- language : config . language
186- } )
18790 } catch ( error ) {
18891 const errorMessage = error instanceof Error ? error . message : String ( error )
18992 console . error ( '[Voice] Failed to start realtime session:' , {
@@ -192,13 +95,6 @@ class RealtimeVoiceSessionImpl implements VoiceSession {
19295 language : config . language ,
19396 voiceId : config . voiceId
19497 } )
195- await this . sendTelemetry ( {
196- stage : 'start-failed' ,
197- message : errorMessage ,
198- sessionId : config . sessionId ,
199- voiceId : config . voiceId ,
200- language : config . language
201- } )
20298 statusCallback ?.( 'error' , `Failed to start voice session: ${ errorMessage } ` )
20399 throw error
204100 }
@@ -294,30 +190,12 @@ export function RealtimeVoiceSession({
294190 const handleDisconnect = useCallback ( ( ) => {
295191 if ( DEBUG ) console . log ( '[Voice] Realtime session disconnected' )
296192 resetRealtimeSessionState ( )
297- void emitVoiceTelemetry ( {
298- stage : 'disconnect' ,
299- message : 'Realtime voice session disconnected' ,
300- ...activeVoiceContext
301- } )
302193 onStatusChange ?.( 'disconnected' )
303194 } , [ onStatusChange ] )
304195
305196 const handleError = useCallback ( ( error : unknown ) => {
306197 if ( DEBUG ) console . error ( '[Voice] Realtime error:' , error )
307- const errorMessage = error instanceof Error
308- ? error . message
309- : ( ( ) => {
310- try {
311- return JSON . stringify ( error )
312- } catch {
313- return String ( error ?? 'Connection error' )
314- }
315- } ) ( )
316- void emitVoiceTelemetry ( {
317- stage : 'runtime-error' ,
318- message : errorMessage ,
319- ...activeVoiceContext
320- } )
198+ const errorMessage = error instanceof Error ? error . message : 'Connection error'
321199 onStatusChange ?.( 'error' , errorMessage )
322200 } , [ onStatusChange ] )
323201
@@ -355,7 +233,6 @@ export function RealtimeVoiceSession({
355233 } )
356234
357235 useEffect ( ( ) => {
358- telemetryApi = api
359236 // Store the conversation instance globally
360237 conversationInstance = conversation
361238
@@ -372,7 +249,6 @@ export function RealtimeVoiceSession({
372249 return ( ) => {
373250 // Clean up on unmount
374251 conversationInstance = null
375- telemetryApi = null
376252 }
377253 } , [ conversation , api ] )
378254
0 commit comments