Skip to content

Commit 8fd6c7c

Browse files
committed
update @layercode/js-sdk to version 2.1.4 and refactor LayercodeClient initialization for improved connection handling
1 parent eb61b19 commit 8fd6c7c

File tree

3 files changed

+111
-61
lines changed

3 files changed

+111
-61
lines changed

bun.lock

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@
3333
"react"
3434
],
3535
"dependencies": {
36-
"@layercode/js-sdk": "^2.1.3"
36+
"@layercode/js-sdk": "^2.1.4"
3737
},
3838
"peerDependencies": {
3939
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"

src/index.ts

Lines changed: 108 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -29,75 +29,89 @@ const useLayercodeAgent = (
2929
) => {
3030
// Extract public options
3131
const { agentId, conversationId, authorizeSessionEndpoint, metadata = {}, onConnect, onDisconnect, onError, onDataMessage, onMessage, onMuteStateChange } = options;
32+
const websocketUrlOverride = options['_websocketUrl'];
3233

3334
const [status, setStatus] = useState('initializing');
3435
const [userAudioAmplitude, setUserAudioAmplitude] = useState(0);
3536
const [agentAudioAmplitude, setAgentAudioAmplitude] = useState(0);
3637
const [isMuted, setIsMuted] = useState(false);
38+
const [internalConversationId, setInternalConversationId] = useState<string | null | undefined>(conversationId);
39+
const conversationIdRef = useRef<string | undefined>(conversationId);
3740
// Reference to the LayercodeClient instance
3841
const clientRef = useRef<LayercodeClient | null>(null);
3942

40-
// Initialize the client on component mount
4143
useEffect(() => {
42-
// Create a new LayercodeClient instance
43-
console.log('Creating LayercodeClient instance');
44-
clientRef.current = new LayercodeClient({
45-
agentId,
46-
conversationId,
47-
authorizeSessionEndpoint,
48-
metadata,
49-
onConnect: ({ conversationId }: { conversationId: string | null }) => {
50-
onConnect?.({ conversationId });
51-
},
52-
onDisconnect: () => {
53-
onDisconnect?.();
54-
},
55-
onError: (error: Error) => {
56-
onError?.(error);
57-
},
58-
onDataMessage: (data: any) => {
59-
onDataMessage?.(data);
60-
},
61-
onMessage: (data: any) => {
62-
onMessage?.(data);
63-
},
64-
onStatusChange: (newStatus: string) => {
65-
setStatus(newStatus);
66-
},
67-
onUserAmplitudeChange: (amplitude: number) => {
68-
setUserAudioAmplitude(amplitude);
69-
},
70-
onAgentAmplitudeChange: (amplitude: number) => {
71-
setAgentAudioAmplitude(amplitude);
72-
},
73-
onMuteStateChange: (muted: boolean) => {
74-
setIsMuted(muted);
75-
onMuteStateChange?.(muted);
76-
},
77-
});
78-
79-
// Pass the override websocket URL if provided. Use for local development.
80-
if (options['_websocketUrl']) {
81-
clientRef.current._websocketUrl = options['_websocketUrl'];
44+
conversationIdRef.current = conversationId;
45+
if (conversationId !== undefined) {
46+
setInternalConversationId(conversationId);
47+
} else {
48+
setInternalConversationId(undefined);
8249
}
50+
}, [conversationId]);
8351

84-
// Set initial mute state from JS SDK
85-
setIsMuted(clientRef.current.isMuted);
86-
87-
// Connect to the agent
88-
clientRef.current.connect().catch((error: Error) => {
89-
console.error('Failed to connect to agent:', error);
90-
onError?.(error);
91-
});
52+
const createClient = useCallback(
53+
(initialConversationId: string | null) => {
54+
console.log('Creating LayercodeClient instance');
55+
const client = new LayercodeClient({
56+
agentId,
57+
conversationId: initialConversationId,
58+
authorizeSessionEndpoint,
59+
metadata,
60+
onConnect: ({ conversationId }: { conversationId: string | null }) => {
61+
setInternalConversationId((current) => {
62+
if (conversationIdRef.current === undefined) {
63+
return conversationId;
64+
}
65+
return conversationId ?? current ?? null;
66+
});
67+
onConnect?.({ conversationId });
68+
},
69+
onDisconnect: () => {
70+
onDisconnect?.();
71+
},
72+
onError: (error: Error) => {
73+
onError?.(error);
74+
},
75+
onDataMessage: (data: any) => {
76+
onDataMessage?.(data);
77+
},
78+
onMessage: (data: any) => {
79+
onMessage?.(data);
80+
},
81+
onStatusChange: (newStatus: string) => {
82+
setStatus(newStatus);
83+
},
84+
onUserAmplitudeChange: (amplitude: number) => {
85+
setUserAudioAmplitude(amplitude);
86+
},
87+
onAgentAmplitudeChange: (amplitude: number) => {
88+
setAgentAudioAmplitude(amplitude);
89+
},
90+
onMuteStateChange: (muted: boolean) => {
91+
setIsMuted(muted);
92+
onMuteStateChange?.(muted);
93+
},
94+
});
95+
96+
if (websocketUrlOverride) {
97+
client._websocketUrl = websocketUrlOverride;
98+
}
9299

93-
// Cleanup function to disconnect when component unmounts
100+
setIsMuted(client.isMuted);
101+
clientRef.current = client;
102+
return client;
103+
},
104+
[agentId, authorizeSessionEndpoint, metadata, onConnect, onDataMessage, onDisconnect, onError, onMessage, onMuteStateChange, websocketUrlOverride]
105+
);
106+
107+
useEffect(() => {
94108
return () => {
95109
if (clientRef.current) {
96110
clientRef.current.disconnect();
111+
clientRef.current = null;
97112
}
98113
};
99-
// Add the internal override URL to the dependency array
100-
}, [agentId, conversationId, authorizeSessionEndpoint]); // Make sure metadata isn't causing unnecessary re-renders if it changes often
114+
}, []);
101115

102116
// Class methods
103117
const mute = useCallback(() => {
@@ -114,12 +128,47 @@ const useLayercodeAgent = (
114128
const triggerUserTurnFinished = useCallback(() => {
115129
clientRef.current?.triggerUserTurnFinished();
116130
}, []);
117-
const connect = useCallback(() => {
118-
clientRef.current?.connect();
119-
}, []);
120-
const disconnect = useCallback(() => {
121-
clientRef.current?.disconnect();
122-
}, []);
131+
const connect = useCallback(async () => {
132+
if (clientRef.current) {
133+
try {
134+
await clientRef.current.disconnect();
135+
} catch (error) {
136+
console.error('Failed to disconnect existing client before reconnect:', error);
137+
}
138+
clientRef.current = null;
139+
}
140+
141+
const nextConversationId =
142+
conversationIdRef.current !== undefined
143+
? conversationIdRef.current
144+
: internalConversationId ?? null;
145+
146+
const client = createClient(nextConversationId ?? null);
147+
148+
try {
149+
await client.connect();
150+
} catch (error) {
151+
console.error('Failed to connect to agent:', error);
152+
onError?.(error as Error);
153+
throw error;
154+
}
155+
}, [createClient, internalConversationId, onError]);
156+
const disconnect = useCallback(async () => {
157+
if (!clientRef.current) {
158+
return;
159+
}
160+
161+
const client = clientRef.current;
162+
clientRef.current = null;
163+
164+
try {
165+
await client.disconnect();
166+
} catch (error) {
167+
console.error('Failed to disconnect from agent:', error);
168+
onError?.(error as Error);
169+
throw error;
170+
}
171+
}, [onError]);
123172

124173
// Return methods and state
125174
return {
@@ -136,6 +185,7 @@ const useLayercodeAgent = (
136185
userAudioAmplitude,
137186
agentAudioAmplitude,
138187
isMuted,
188+
conversationId: internalConversationId,
139189
};
140190
};
141191

0 commit comments

Comments
 (0)