Spaces:
Sleeping
Sleeping
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 () =>
|
|
|
|
|
|
|
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 |
-
|
173 |
-
|
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) =>
|
|
|
|
|
|
|
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
|
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 |
-
|
45 |
-
|
46 |
-
|
47 |
-
|
48 |
-
|
49 |
-
|
50 |
-
|
51 |
-
|
52 |
-
|
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 |
-
|
69 |
-
|
70 |
-
|
71 |
-
|
72 |
-
|
73 |
-
|
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("
|
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);
|