Trudy commited on
Commit
616f053
Β·
1 Parent(s): c4746a7

debugging the connect error on deployment

Browse files
src/components/control-tray/ControlTray.tsx CHANGED
@@ -84,6 +84,7 @@ function ControlTray({
84
 
85
  useEffect(() => {
86
  if (!connected && connectButtonRef.current) {
 
87
  connectButtonRef.current.focus();
88
  }
89
  }, [connected]);
@@ -91,13 +92,17 @@ function ControlTray({
91
  // Add iOS volume simulation effect
92
  useEffect(() => {
93
  if (isIOSDevice && connected && !muted) {
 
94
  const interval = setInterval(() => {
95
  // Create a smooth pulsing effect
96
  const pulse = (Math.sin(Date.now() / 500) + 1) / 2; // Values between 0 and 1
97
  setSimulatedVolume(0.02 + pulse * 0.03); // Small range for subtle effect
98
  }, 50);
99
 
100
- return () => clearInterval(interval);
 
 
 
101
  }
102
  }, [connected, muted, isIOSDevice]);
103
 
@@ -110,6 +115,7 @@ function ControlTray({
110
 
111
  useEffect(() => {
112
  const onData = (base64: string) => {
 
113
  client.sendRealtimeInput([
114
  {
115
  mimeType: "audio/pcm;rate=16000",
@@ -119,18 +125,22 @@ function ControlTray({
119
  };
120
 
121
  if (connected && !muted && audioRecorder) {
 
122
  audioRecorder.on("data", onData).on("volume", setInVolume).start();
123
  } else {
 
124
  audioRecorder.stop();
125
  }
126
 
127
  return () => {
 
128
  audioRecorder.off("data", onData).off("volume", setInVolume);
129
  };
130
  }, [connected, client, muted, audioRecorder]);
131
 
132
  useEffect(() => {
133
  if (videoRef.current) {
 
134
  videoRef.current.srcObject = activeVideoStream;
135
  }
136
 
@@ -141,6 +151,7 @@ function ControlTray({
141
  const canvas = renderCanvasRef.current;
142
 
143
  if (!video || !canvas) {
 
144
  return;
145
  }
146
 
@@ -151,6 +162,7 @@ function ControlTray({
151
  ctx.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height);
152
  const base64 = canvas.toDataURL("image/jpeg", 1.0);
153
  const data = base64.slice(base64.indexOf(",") + 1, Infinity);
 
154
  client.sendRealtimeInput([{ mimeType: "image/jpeg", data }]);
155
  }
156
  if (connected) {
@@ -158,9 +170,11 @@ function ControlTray({
158
  }
159
  }
160
  if (connected && activeVideoStream !== null) {
 
161
  requestAnimationFrame(sendVideoFrame);
162
  }
163
  return () => {
 
164
  clearTimeout(timeoutId);
165
  };
166
  }, [connected, activeVideoStream, client, videoRef]);
@@ -168,20 +182,20 @@ function ControlTray({
168
  //handler for swapping from one video-stream to the next
169
  const changeStreams = (next?: UseMediaStreamResult) => async () => {
170
  if (next) {
 
171
  const mediaStream = await next.start();
172
- if (mediaStream) {
173
- setActiveVideoStream(mediaStream);
174
- onVideoStreamChange(mediaStream);
175
- } else {
176
- setActiveVideoStream(null);
177
- onVideoStreamChange(null);
178
- }
179
  } else {
 
180
  setActiveVideoStream(null);
181
  onVideoStreamChange(null);
182
  }
183
 
184
- videoStreams.filter((msr) => msr !== next).forEach((msr) => msr.stop());
 
 
 
185
  };
186
 
187
  return (
 
84
 
85
  useEffect(() => {
86
  if (!connected && connectButtonRef.current) {
87
+ console.log('🎯 Setting focus on connect button - connection status:', connected);
88
  connectButtonRef.current.focus();
89
  }
90
  }, [connected]);
 
92
  // Add iOS volume simulation effect
93
  useEffect(() => {
94
  if (isIOSDevice && connected && !muted) {
95
+ console.log('πŸ“± Starting iOS volume simulation');
96
  const interval = setInterval(() => {
97
  // Create a smooth pulsing effect
98
  const pulse = (Math.sin(Date.now() / 500) + 1) / 2; // Values between 0 and 1
99
  setSimulatedVolume(0.02 + pulse * 0.03); // Small range for subtle effect
100
  }, 50);
101
 
102
+ return () => {
103
+ console.log('πŸ›‘ Stopping iOS volume simulation');
104
+ clearInterval(interval);
105
+ };
106
  }
107
  }, [connected, muted, isIOSDevice]);
108
 
 
115
 
116
  useEffect(() => {
117
  const onData = (base64: string) => {
118
+ console.log('🎀 Sending audio data chunk');
119
  client.sendRealtimeInput([
120
  {
121
  mimeType: "audio/pcm;rate=16000",
 
125
  };
126
 
127
  if (connected && !muted && audioRecorder) {
128
+ console.log('πŸŽ™οΈ Starting audio recorder');
129
  audioRecorder.on("data", onData).on("volume", setInVolume).start();
130
  } else {
131
+ console.log('⏹️ Stopping audio recorder');
132
  audioRecorder.stop();
133
  }
134
 
135
  return () => {
136
+ console.log('🧹 Cleaning up audio recorder listeners');
137
  audioRecorder.off("data", onData).off("volume", setInVolume);
138
  };
139
  }, [connected, client, muted, audioRecorder]);
140
 
141
  useEffect(() => {
142
  if (videoRef.current) {
143
+ console.log('πŸŽ₯ Setting video stream:', activeVideoStream ? 'active' : 'null');
144
  videoRef.current.srcObject = activeVideoStream;
145
  }
146
 
 
151
  const canvas = renderCanvasRef.current;
152
 
153
  if (!video || !canvas) {
154
+ console.log('⚠️ Missing video or canvas reference');
155
  return;
156
  }
157
 
 
162
  ctx.drawImage(videoRef.current, 0, 0, canvas.width, canvas.height);
163
  const base64 = canvas.toDataURL("image/jpeg", 1.0);
164
  const data = base64.slice(base64.indexOf(",") + 1, Infinity);
165
+ console.log('πŸ“Έ Sending video frame');
166
  client.sendRealtimeInput([{ mimeType: "image/jpeg", data }]);
167
  }
168
  if (connected) {
 
170
  }
171
  }
172
  if (connected && activeVideoStream !== null) {
173
+ console.log('🎬 Starting video frame capture');
174
  requestAnimationFrame(sendVideoFrame);
175
  }
176
  return () => {
177
+ console.log('⏹️ Stopping video frame capture');
178
  clearTimeout(timeoutId);
179
  };
180
  }, [connected, activeVideoStream, client, videoRef]);
 
182
  //handler for swapping from one video-stream to the next
183
  const changeStreams = (next?: UseMediaStreamResult) => async () => {
184
  if (next) {
185
+ console.log('πŸ”„ Starting new video stream');
186
  const mediaStream = await next.start();
187
+ setActiveVideoStream(mediaStream);
188
+ onVideoStreamChange(mediaStream);
 
 
 
 
 
189
  } else {
190
+ console.log('⏹️ Stopping video stream');
191
  setActiveVideoStream(null);
192
  onVideoStreamChange(null);
193
  }
194
 
195
+ videoStreams.filter((msr) => msr !== next).forEach((msr) => {
196
+ console.log('πŸ›‘ Stopping other video streams');
197
+ msr.stop();
198
+ });
199
  };
200
 
201
  return (
src/contexts/LiveAPIContext.tsx CHANGED
@@ -28,6 +28,7 @@ export const LiveAPIProvider: FC<LiveAPIProviderProps> = ({
28
  url = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`,
29
  children,
30
  }) => {
 
31
  const liveAPI = useLiveAPI({ url });
32
 
33
  return (
@@ -40,7 +41,9 @@ export const LiveAPIProvider: FC<LiveAPIProviderProps> = ({
40
  export const useLiveAPIContext = () => {
41
  const context = useContext(LiveAPIContext);
42
  if (!context) {
 
43
  throw new Error("useLiveAPIContext must be used within a LiveAPIProvider");
44
  }
 
45
  return context;
46
  };
 
28
  url = `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`,
29
  children,
30
  }) => {
31
+ console.log('🌐 Initializing LiveAPIProvider with URL:', url);
32
  const liveAPI = useLiveAPI({ url });
33
 
34
  return (
 
41
  export const useLiveAPIContext = () => {
42
  const context = useContext(LiveAPIContext);
43
  if (!context) {
44
+ console.error('❌ LiveAPIContext used outside of LiveAPIProvider');
45
  throw new Error("useLiveAPIContext must be used within a LiveAPIProvider");
46
  }
47
+ console.log('βœ… LiveAPIContext successfully retrieved');
48
  return context;
49
  };
src/hooks/use-media-stream-mux.ts CHANGED
@@ -16,7 +16,7 @@
16
 
17
  export type UseMediaStreamResult = {
18
  type: "webcam" | "screen";
19
- start: () => Promise<MediaStream | null>;
20
  stop: () => void;
21
  isStreaming: boolean;
22
  stream: MediaStream | null;
 
16
 
17
  export type UseMediaStreamResult = {
18
  type: "webcam" | "screen";
19
+ start: () => Promise<MediaStream>;
20
  stop: () => void;
21
  isStreaming: boolean;
22
  stream: MediaStream | null;
src/hooks/use-screen-capture.ts CHANGED
@@ -41,20 +41,15 @@ export function useScreenCapture(): UseMediaStreamResult {
41
  }, [stream]);
42
 
43
  const start = async () => {
44
- try {
45
- // const controller = new CaptureController();
46
- // controller.setFocusBehavior("no-focus-change");
47
- const mediaStream = await navigator.mediaDevices.getDisplayMedia({
48
- video: true,
49
- // controller
50
- });
51
- setStream(mediaStream);
52
- setIsStreaming(true);
53
- return mediaStream;
54
- } catch (err) {
55
- console.error('Failed to start screen capture:', err);
56
- return null;
57
- }
58
  };
59
 
60
  const stop = () => {
 
41
  }, [stream]);
42
 
43
  const start = async () => {
44
+ // const controller = new CaptureController();
45
+ // controller.setFocusBehavior("no-focus-change");
46
+ const mediaStream = await navigator.mediaDevices.getDisplayMedia({
47
+ video: true,
48
+ // controller
49
+ });
50
+ setStream(mediaStream);
51
+ setIsStreaming(true);
52
+ return mediaStream;
 
 
 
 
 
53
  };
54
 
55
  const stop = () => {
src/hooks/use-webcam.ts CHANGED
@@ -20,30 +20,6 @@ import { UseMediaStreamResult } from "./use-media-stream-mux";
20
  export function useWebcam(): UseMediaStreamResult {
21
  const [stream, setStream] = useState<MediaStream | null>(null);
22
  const [isStreaming, setIsStreaming] = useState(false);
23
- const [availableCameras, setAvailableCameras] = useState<MediaDeviceInfo[]>([]);
24
- const [currentCameraIndex, setCurrentCameraIndex] = useState(-1);
25
-
26
- // Get list of available cameras on mount
27
- useEffect(() => {
28
- async function getCameras() {
29
- try {
30
- // First request permission to ensure we can enumerate video devices
31
- await navigator.mediaDevices.getUserMedia({ video: true })
32
- .then(stream => {
33
- // Stop the stream immediately, we just needed permission
34
- stream.getTracks().forEach(track => track.stop());
35
- });
36
-
37
- const devices = await navigator.mediaDevices.enumerateDevices();
38
- const videoDevices = devices.filter(device => device.kind === 'videoinput');
39
- setAvailableCameras(videoDevices);
40
- console.log('Available cameras:', videoDevices);
41
- } catch (err) {
42
- console.error('Error getting cameras:', err);
43
- }
44
- }
45
- getCameras();
46
- }, []);
47
 
48
  useEffect(() => {
49
  const handleStreamEnded = () => {
@@ -65,41 +41,12 @@ export function useWebcam(): UseMediaStreamResult {
65
  }, [stream]);
66
 
67
  const start = async () => {
68
- // If we're already streaming, cycle to next camera
69
- if (isStreaming) {
70
- const nextIndex = (currentCameraIndex + 1) % (availableCameras.length);
71
- setCurrentCameraIndex(nextIndex);
72
-
73
- // Stop current stream
74
- if (stream) {
75
- stream.getTracks().forEach((track) => track.stop());
76
- }
77
-
78
- // If we've cycled through all cameras, stop streaming
79
- if (nextIndex === 0) {
80
- setStream(null);
81
- setIsStreaming(false);
82
- return null;
83
- }
84
-
85
- const deviceId = availableCameras[nextIndex].deviceId;
86
- const mediaStream = await navigator.mediaDevices.getUserMedia({
87
- video: { deviceId: { exact: deviceId } }
88
- });
89
- setStream(mediaStream);
90
- setIsStreaming(true);
91
- return mediaStream;
92
- } else {
93
- // Start with first camera
94
- setCurrentCameraIndex(0);
95
- const deviceId = availableCameras[0]?.deviceId;
96
- const mediaStream = await navigator.mediaDevices.getUserMedia({
97
- video: deviceId ? { deviceId: { exact: deviceId } } : true
98
- });
99
- setStream(mediaStream);
100
- setIsStreaming(true);
101
- return mediaStream;
102
- }
103
  };
104
 
105
  const stop = () => {
@@ -107,7 +54,6 @@ export function useWebcam(): UseMediaStreamResult {
107
  stream.getTracks().forEach((track) => track.stop());
108
  setStream(null);
109
  setIsStreaming(false);
110
- setCurrentCameraIndex(-1);
111
  }
112
  };
113
 
 
20
  export function useWebcam(): UseMediaStreamResult {
21
  const [stream, setStream] = useState<MediaStream | null>(null);
22
  const [isStreaming, setIsStreaming] = useState(false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
23
 
24
  useEffect(() => {
25
  const handleStreamEnded = () => {
 
41
  }, [stream]);
42
 
43
  const start = async () => {
44
+ const mediaStream = await navigator.mediaDevices.getUserMedia({
45
+ video: true,
46
+ });
47
+ setStream(mediaStream);
48
+ setIsStreaming(true);
49
+ return mediaStream;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
50
  };
51
 
52
  const stop = () => {
 
54
  stream.getTracks().forEach((track) => track.stop());
55
  setStream(null);
56
  setIsStreaming(false);
 
57
  }
58
  };
59
 
src/lib/multimodal-live-client.ts CHANGED
@@ -72,6 +72,7 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
72
 
73
  constructor({ url, apiKey }: MultimodalLiveAPIClientConnection = {}) {
74
  super();
 
75
  this.url = url || `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`;
76
  this.send = this.send.bind(this);
77
  }
@@ -86,19 +87,22 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
86
  }
87
 
88
  connect(config: LiveConfig): Promise<boolean> {
 
89
  this.config = config;
90
 
91
  const ws = new WebSocket(this.url);
92
 
93
  ws.addEventListener("message", async (evt: MessageEvent) => {
 
94
  if (evt.data instanceof Blob) {
95
  this.receive(evt.data);
96
  } else {
97
- console.log("non blob message", evt);
98
  }
99
  });
100
  return new Promise((resolve, reject) => {
101
  const onError = (ev: Event) => {
 
102
  this.disconnect(ws);
103
  const message = `Could not connect to "${this.url}"`;
104
  this.log(`server.${ev.type}`, message);
@@ -106,7 +110,9 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
106
  };
107
  ws.addEventListener("error", onError);
108
  ws.addEventListener("open", (ev: Event) => {
 
109
  if (!this.config) {
 
110
  reject("Invalid config sent to `connect(config)`");
111
  return;
112
  }
@@ -118,12 +124,13 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
118
  const setupMessage: SetupMessage = {
119
  setup: this.config,
120
  };
 
121
  this._sendDirect(setupMessage);
122
  this.log("client.send", "setup");
123
 
124
  ws.removeEventListener("error", onError);
125
  ws.addEventListener("close", (ev: CloseEvent) => {
126
- console.log(ev);
127
  this.disconnect(ws);
128
  let reason = ev.reason || "";
129
  if (reason.toLowerCase().includes("error")) {
@@ -136,6 +143,7 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
136
  );
137
  }
138
  }
 
139
  this.log(
140
  `server.${ev.type}`,
141
  `disconnected ${reason ? `with reason: ${reason}` : ``}`,
@@ -148,14 +156,17 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
148
  }
149
 
150
  disconnect(ws?: WebSocket) {
 
151
  // could be that this is an old websocket and theres already a new instance
152
  // only close it if its still the correct reference
153
  if ((!ws || this.ws === ws) && this.ws) {
 
154
  this.ws.close();
155
  this.ws = null;
156
  this.log("client.close", `Disconnected`);
157
  return true;
158
  }
 
159
  return false;
160
  }
161
 
@@ -163,6 +174,7 @@ export class MultimodalLiveClient extends EventEmitter<MultimodalLiveClientEvent
163
  const response: LiveIncomingMessage = (await blobToJSON(
164
  blob,
165
  )) as LiveIncomingMessage;
 
166
  if (isToolCallMessage(response)) {
167
  this.log("server.toolCall", response);
168
  this.emit("toolcall", response.toolCall);
 
72
 
73
  constructor({ url, apiKey }: MultimodalLiveAPIClientConnection = {}) {
74
  super();
75
+ console.log('πŸ”§ Initializing MultimodalLiveClient with URL:', url || `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`);
76
  this.url = url || `${window.location.protocol === 'https:' ? 'wss:' : 'ws:'}//${window.location.host}/ws`;
77
  this.send = this.send.bind(this);
78
  }
 
87
  }
88
 
89
  connect(config: LiveConfig): Promise<boolean> {
90
+ console.log('πŸ”Œ Attempting WebSocket connection to:', this.url);
91
  this.config = config;
92
 
93
  const ws = new WebSocket(this.url);
94
 
95
  ws.addEventListener("message", async (evt: MessageEvent) => {
96
+ console.log('πŸ“¨ Received WebSocket message:', evt.data instanceof Blob ? 'Blob data' : evt.data);
97
  if (evt.data instanceof Blob) {
98
  this.receive(evt.data);
99
  } else {
100
+ console.log("πŸ“¦ Non-blob message received:", evt);
101
  }
102
  });
103
  return new Promise((resolve, reject) => {
104
  const onError = (ev: Event) => {
105
+ console.error('❌ WebSocket connection error:', ev);
106
  this.disconnect(ws);
107
  const message = `Could not connect to "${this.url}"`;
108
  this.log(`server.${ev.type}`, message);
 
110
  };
111
  ws.addEventListener("error", onError);
112
  ws.addEventListener("open", (ev: Event) => {
113
+ console.log('βœ… WebSocket connection opened successfully');
114
  if (!this.config) {
115
+ console.error('❌ Invalid config provided to connect()');
116
  reject("Invalid config sent to `connect(config)`");
117
  return;
118
  }
 
124
  const setupMessage: SetupMessage = {
125
  setup: this.config,
126
  };
127
+ console.log('πŸ“€ Sending setup message:', setupMessage);
128
  this._sendDirect(setupMessage);
129
  this.log("client.send", "setup");
130
 
131
  ws.removeEventListener("error", onError);
132
  ws.addEventListener("close", (ev: CloseEvent) => {
133
+ console.log('πŸ”’ WebSocket connection closed:', ev);
134
  this.disconnect(ws);
135
  let reason = ev.reason || "";
136
  if (reason.toLowerCase().includes("error")) {
 
143
  );
144
  }
145
  }
146
+ console.log('πŸ“ Close reason:', reason || 'No reason provided');
147
  this.log(
148
  `server.${ev.type}`,
149
  `disconnected ${reason ? `with reason: ${reason}` : ``}`,
 
156
  }
157
 
158
  disconnect(ws?: WebSocket) {
159
+ console.log('πŸ”Œ Attempting to disconnect WebSocket');
160
  // could be that this is an old websocket and theres already a new instance
161
  // only close it if its still the correct reference
162
  if ((!ws || this.ws === ws) && this.ws) {
163
+ console.log('πŸ”’ Closing WebSocket connection');
164
  this.ws.close();
165
  this.ws = null;
166
  this.log("client.close", `Disconnected`);
167
  return true;
168
  }
169
+ console.log('⚠️ No active WebSocket to disconnect');
170
  return false;
171
  }
172
 
 
174
  const response: LiveIncomingMessage = (await blobToJSON(
175
  blob,
176
  )) as LiveIncomingMessage;
177
+ console.log('πŸ“₯ Received message:', response);
178
  if (isToolCallMessage(response)) {
179
  this.log("server.toolCall", response);
180
  this.emit("toolcall", response.toolCall);