@cmnd-ai/chatbot-react 1.10.0 → 1.14.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.
- package/Readme.md +113 -9
- package/dist/ChatProvider/index.d.ts +2 -2
- package/dist/ChatProvider/index.js +23 -0
- package/dist/ChatProvider/processStream/index.js +39 -13
- package/dist/CmndChatBot/index.d.ts +2 -1
- package/dist/CmndChatBot/index.js +2 -2
- package/dist/components/ConversationsPanel/index.js +38 -1
- package/dist/constants/socketEvents.d.ts +14 -0
- package/dist/constants/socketEvents.js +12 -0
- package/dist/hooks/use-cmnd-chat.d.ts +130 -0
- package/dist/hooks/use-cmnd-chat.js +432 -0
- package/dist/hooks/use-socket.d.ts +13 -0
- package/dist/hooks/use-socket.js +25 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +8 -0
- package/dist/services/deleteChatbotConversationMemory/index.d.ts +1 -1
- package/dist/services/getEmbedChatBotById.d.ts +1 -1
- package/dist/services/patchChatbotConversationMemory/index.d.ts +1 -1
- package/dist/services/socketService.d.ts +10 -0
- package/dist/services/socketService.js +40 -0
- package/dist/type.d.ts +136 -38
- package/dist/type.js +12 -0
- package/dist/utils/cleanMarkdown.d.ts +8 -0
- package/dist/utils/cleanMarkdown.js +31 -0
- package/package.json +3 -2
- package/readme.dev.md +49 -0
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
import { useState, useEffect, useCallback, useMemo, useRef } from "react";
|
|
2
|
+
import { v4 as uuidv4 } from "uuid";
|
|
3
|
+
import postUserConversation from "../services/postUserConversation.js";
|
|
4
|
+
import getChatBotConversationsList from "../services/getChatBotConversationsList/index.js";
|
|
5
|
+
import getEmbedChatBotById from "../services/getEmbedChatBotById.js";
|
|
6
|
+
import { MessageRole, FunctionType, } from "../type.js";
|
|
7
|
+
import getConversationLocalStorageKey from "../utils/getConversationLocalStorageKey/index.js";
|
|
8
|
+
import saveConversationIdToLocalStorage from "../utils/saveConversationIdToLocalStorage/index.js";
|
|
9
|
+
import getUTCDateTime from "../utils/getUTCDateTime/index.js";
|
|
10
|
+
import parseUITools from "../utils/parseUITools.js";
|
|
11
|
+
import getTools from "../utils/getTools/index.js";
|
|
12
|
+
import { cleanMarkdown } from "../utils/cleanMarkdown.js";
|
|
13
|
+
import { useSocket } from "./use-socket.js";
|
|
14
|
+
import { SOCKET_MESSAGE_TYPES } from "../constants/socketEvents.js";
|
|
15
|
+
/**
|
|
16
|
+
* A comprehensive hook to manage CMND.AI chatbot logic without being forced to use the default UI.
|
|
17
|
+
* This hook handles conversation state, message sending (including streaming), history management, and tool call orchestration.
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```tsx
|
|
21
|
+
* const { messages, sendMessage, input, setInput, submitToolResult } = useCMNDChat({
|
|
22
|
+
* chatbotId: 1,
|
|
23
|
+
* organizationId: 1,
|
|
24
|
+
* baseUrl: 'https://api.cmnd.ai',
|
|
25
|
+
* cleanResponse: true, // Optional: Returns assistant messages as plaintext
|
|
26
|
+
* onToolCall: async (toolDetails) => {
|
|
27
|
+
* // Backend tools are auto-confirmed by the hook
|
|
28
|
+
* if (toolDetails.name === 'get_playlists') {
|
|
29
|
+
* console.log('Fetching playlists from backend...');
|
|
30
|
+
* }
|
|
31
|
+
*
|
|
32
|
+
* // UI tools need manual execution and confirmation
|
|
33
|
+
* if (toolDetails.name === 'show_notification') {
|
|
34
|
+
* showNotification(toolDetails.args.message);
|
|
35
|
+
* submitToolResult('Notification displayed');
|
|
36
|
+
* }
|
|
37
|
+
* }
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export const useCMNDChat = (options) => {
|
|
42
|
+
const { chatbotId, organizationId, baseUrl, apiKey, chatHistoryStorageKey, initialMemory, UITools, onData: globalOnData, onToolCall, cleanResponse = false, } = options;
|
|
43
|
+
const [messages, setMessages] = useState([]);
|
|
44
|
+
const [input, setInput] = useState("");
|
|
45
|
+
const [isChatLoading, setIsChatLoading] = useState(false);
|
|
46
|
+
const [canSendMessage, setCanSendMessage] = useState(true);
|
|
47
|
+
const [chatbotConversationRef, setChatbotConversationRef] = useState(undefined);
|
|
48
|
+
const [conversations, setConversations] = useState([]);
|
|
49
|
+
const [selectedConversation, setSelectedConversation] = useState(null);
|
|
50
|
+
const [enabledTools, setEnabledTools] = useState([]);
|
|
51
|
+
const [error, setError] = useState(null);
|
|
52
|
+
const lastProcessedToolCallId = useRef(null);
|
|
53
|
+
const localStorageKey = useMemo(() => chatHistoryStorageKey ||
|
|
54
|
+
getConversationLocalStorageKey({ organizationId, chatbotId }), [chatHistoryStorageKey, organizationId, chatbotId]);
|
|
55
|
+
const tools = useMemo(() => getTools({
|
|
56
|
+
apiTools: enabledTools,
|
|
57
|
+
uiTools: parseUITools(UITools),
|
|
58
|
+
}), [enabledTools, UITools]);
|
|
59
|
+
// Fetch Chatbot Info for enabled tools
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!baseUrl || !organizationId || !chatbotId)
|
|
62
|
+
return;
|
|
63
|
+
getEmbedChatBotById(baseUrl, organizationId, chatbotId)
|
|
64
|
+
.then((response) => {
|
|
65
|
+
const { chatbot } = response.data;
|
|
66
|
+
setEnabledTools(chatbot.enabledTools || []);
|
|
67
|
+
})
|
|
68
|
+
.catch((err) => {
|
|
69
|
+
console.error("Error fetching chatbot tools:", err);
|
|
70
|
+
});
|
|
71
|
+
}, [baseUrl, organizationId, chatbotId]);
|
|
72
|
+
const refreshConversations = useCallback(async () => {
|
|
73
|
+
try {
|
|
74
|
+
const storedIds = JSON.parse(localStorage.getItem(localStorageKey) || "[]");
|
|
75
|
+
if (storedIds.length > 0) {
|
|
76
|
+
const response = await getChatBotConversationsList({
|
|
77
|
+
organizationId,
|
|
78
|
+
chatbotId,
|
|
79
|
+
conversationIds: storedIds,
|
|
80
|
+
baseUrl,
|
|
81
|
+
});
|
|
82
|
+
setConversations(response.chatbotConversations || []);
|
|
83
|
+
}
|
|
84
|
+
else {
|
|
85
|
+
setConversations([]);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
catch (err) {
|
|
89
|
+
console.error("Error fetching conversations:", err);
|
|
90
|
+
}
|
|
91
|
+
}, [baseUrl, chatbotId, localStorageKey, organizationId]);
|
|
92
|
+
useEffect(() => {
|
|
93
|
+
refreshConversations();
|
|
94
|
+
}, [refreshConversations]);
|
|
95
|
+
const handleSocketMessage = useCallback((notification) => {
|
|
96
|
+
if (notification.type === SOCKET_MESSAGE_TYPES.NEW_HUMAN_INBOX_MESSAGE &&
|
|
97
|
+
notification.chatbotConversationRef === chatbotConversationRef) {
|
|
98
|
+
setMessages((prev) => {
|
|
99
|
+
if (prev.some((m) => m.id === notification.message.id))
|
|
100
|
+
return prev;
|
|
101
|
+
return [...prev, notification.message];
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
else if (notification.type === SOCKET_MESSAGE_TYPES.HUMAN_AGENT_JOINED &&
|
|
105
|
+
notification.chatbotConversationRef === chatbotConversationRef) {
|
|
106
|
+
// Add a system message to indicate human agent joined
|
|
107
|
+
const agentName = notification.agentName || "A human agent";
|
|
108
|
+
const systemMessage = {
|
|
109
|
+
id: `system-${notification.timestamp}`,
|
|
110
|
+
role: MessageRole.ASSISTANT,
|
|
111
|
+
message: `${agentName} has joined the conversation`,
|
|
112
|
+
unuseful: false,
|
|
113
|
+
hiddenFromUser: false,
|
|
114
|
+
};
|
|
115
|
+
setMessages((prev) => [...prev, systemMessage]);
|
|
116
|
+
}
|
|
117
|
+
}, [chatbotConversationRef]);
|
|
118
|
+
useSocket({
|
|
119
|
+
baseUrl,
|
|
120
|
+
chatbotConversationRef,
|
|
121
|
+
onMessage: handleSocketMessage,
|
|
122
|
+
enabled: !!baseUrl,
|
|
123
|
+
});
|
|
124
|
+
const handleStreamData = useCallback(async (data, currentMessages, onDataCallback) => {
|
|
125
|
+
if (onDataCallback)
|
|
126
|
+
onDataCallback(data);
|
|
127
|
+
if (globalOnData)
|
|
128
|
+
globalOnData(data);
|
|
129
|
+
// Handle streaming assistant message
|
|
130
|
+
if (data.message && !data.finalResponseWithUsageData) {
|
|
131
|
+
// Clear loading state when assistant starts responding
|
|
132
|
+
setIsChatLoading(false);
|
|
133
|
+
setCanSendMessage(true);
|
|
134
|
+
const assistantMessage = {
|
|
135
|
+
role: MessageRole.ASSISTANT,
|
|
136
|
+
message: data.message,
|
|
137
|
+
id: "streaming-assistant-message",
|
|
138
|
+
unuseful: false,
|
|
139
|
+
hiddenFromUser: false,
|
|
140
|
+
};
|
|
141
|
+
setMessages([...currentMessages, assistantMessage]);
|
|
142
|
+
}
|
|
143
|
+
if (data.finalResponseWithUsageData) {
|
|
144
|
+
const { messages: updatedMessages, message, hasError } = data;
|
|
145
|
+
const newConversationId = data.chatbotConversationRef || data.conversationRef;
|
|
146
|
+
if (hasError) {
|
|
147
|
+
setError(message || "Oops! I ran into a problem.");
|
|
148
|
+
}
|
|
149
|
+
if (newConversationId) {
|
|
150
|
+
setChatbotConversationRef(newConversationId);
|
|
151
|
+
await saveConversationIdToLocalStorage({
|
|
152
|
+
chatbotConversationRef: newConversationId,
|
|
153
|
+
chatbotId,
|
|
154
|
+
organizationId,
|
|
155
|
+
chatHistoryStorageKey,
|
|
156
|
+
});
|
|
157
|
+
refreshConversations();
|
|
158
|
+
}
|
|
159
|
+
if (updatedMessages) {
|
|
160
|
+
setMessages(updatedMessages);
|
|
161
|
+
}
|
|
162
|
+
else if (message) {
|
|
163
|
+
// If we have a standalone message in the final response (e.g. an error)
|
|
164
|
+
const assistantMessage = {
|
|
165
|
+
role: MessageRole.ASSISTANT,
|
|
166
|
+
message: message,
|
|
167
|
+
id: uuidv4(),
|
|
168
|
+
unuseful: false,
|
|
169
|
+
hiddenFromUser: false,
|
|
170
|
+
};
|
|
171
|
+
setMessages([...currentMessages, assistantMessage]);
|
|
172
|
+
}
|
|
173
|
+
// Ensure loading state is cleared (in case no streaming message came)
|
|
174
|
+
setIsChatLoading(false);
|
|
175
|
+
setCanSendMessage(true);
|
|
176
|
+
}
|
|
177
|
+
}, [
|
|
178
|
+
globalOnData,
|
|
179
|
+
refreshConversations,
|
|
180
|
+
chatbotId,
|
|
181
|
+
organizationId,
|
|
182
|
+
chatHistoryStorageKey,
|
|
183
|
+
]);
|
|
184
|
+
const sendMessage = useCallback(async (text, onData) => {
|
|
185
|
+
const messageToSend = text !== undefined ? text : input;
|
|
186
|
+
if (!messageToSend.trim() || !canSendMessage)
|
|
187
|
+
return;
|
|
188
|
+
const userMessage = {
|
|
189
|
+
role: MessageRole.USER,
|
|
190
|
+
message: messageToSend,
|
|
191
|
+
id: uuidv4(),
|
|
192
|
+
unuseful: false,
|
|
193
|
+
hiddenFromUser: false,
|
|
194
|
+
};
|
|
195
|
+
const newMessages = [...messages, userMessage];
|
|
196
|
+
setMessages(newMessages);
|
|
197
|
+
if (text === undefined)
|
|
198
|
+
setInput("");
|
|
199
|
+
setIsChatLoading(true);
|
|
200
|
+
setCanSendMessage(false);
|
|
201
|
+
setError(null);
|
|
202
|
+
const payload = {
|
|
203
|
+
messages: newMessages,
|
|
204
|
+
uiTools: parseUITools(UITools),
|
|
205
|
+
};
|
|
206
|
+
if (chatbotConversationRef) {
|
|
207
|
+
payload["chatbotConversationRef"] = chatbotConversationRef;
|
|
208
|
+
}
|
|
209
|
+
else if (initialMemory) {
|
|
210
|
+
payload["initialMemory"] = initialMemory;
|
|
211
|
+
}
|
|
212
|
+
try {
|
|
213
|
+
await postUserConversation({
|
|
214
|
+
payload,
|
|
215
|
+
apikey: apiKey,
|
|
216
|
+
chatbotId,
|
|
217
|
+
baseUrl,
|
|
218
|
+
onData: (data) => handleStreamData(data, newMessages, onData),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
catch (err) {
|
|
222
|
+
console.error("Error sending message:", err);
|
|
223
|
+
setError(err.message || "Failed to send message");
|
|
224
|
+
setIsChatLoading(false);
|
|
225
|
+
setCanSendMessage(true);
|
|
226
|
+
}
|
|
227
|
+
}, [
|
|
228
|
+
input,
|
|
229
|
+
canSendMessage,
|
|
230
|
+
messages,
|
|
231
|
+
UITools,
|
|
232
|
+
chatbotConversationRef,
|
|
233
|
+
initialMemory,
|
|
234
|
+
chatbotId,
|
|
235
|
+
baseUrl,
|
|
236
|
+
apiKey,
|
|
237
|
+
handleStreamData,
|
|
238
|
+
]);
|
|
239
|
+
// Confirm a backend tool (sets confirmed: true, server will add output)
|
|
240
|
+
const confirmBackendTool = useCallback(async () => {
|
|
241
|
+
const lastMessage = messages[messages.length - 1];
|
|
242
|
+
if (!lastMessage || lastMessage.role !== MessageRole.FUNCTION) {
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
const updatedLastMessage = {
|
|
246
|
+
...lastMessage,
|
|
247
|
+
tool: {
|
|
248
|
+
...lastMessage.tool,
|
|
249
|
+
confirmed: true,
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
const newMessages = [...messages.slice(0, -1), updatedLastMessage];
|
|
253
|
+
setMessages(newMessages);
|
|
254
|
+
// Keep loading state true while backend tool executes
|
|
255
|
+
setIsChatLoading(true);
|
|
256
|
+
setCanSendMessage(false);
|
|
257
|
+
const payload = {
|
|
258
|
+
messages: newMessages,
|
|
259
|
+
uiTools: parseUITools(UITools),
|
|
260
|
+
chatbotConversationRef,
|
|
261
|
+
};
|
|
262
|
+
try {
|
|
263
|
+
await postUserConversation({
|
|
264
|
+
payload,
|
|
265
|
+
apikey: apiKey,
|
|
266
|
+
chatbotId,
|
|
267
|
+
baseUrl,
|
|
268
|
+
onData: (data) => handleStreamData(data, newMessages),
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
catch (err) {
|
|
272
|
+
console.error("Error confirming backend tool:", err);
|
|
273
|
+
setError(err.message || "Failed to confirm backend tool");
|
|
274
|
+
setIsChatLoading(false);
|
|
275
|
+
setCanSendMessage(true);
|
|
276
|
+
}
|
|
277
|
+
}, [
|
|
278
|
+
messages,
|
|
279
|
+
UITools,
|
|
280
|
+
chatbotConversationRef,
|
|
281
|
+
apiKey,
|
|
282
|
+
chatbotId,
|
|
283
|
+
baseUrl,
|
|
284
|
+
handleStreamData,
|
|
285
|
+
]);
|
|
286
|
+
const submitToolResult = useCallback(async (toolOutput, onData) => {
|
|
287
|
+
const lastMessage = messages[messages.length - 1];
|
|
288
|
+
if (!lastMessage || lastMessage.role !== MessageRole.FUNCTION) {
|
|
289
|
+
console.error("Last message is not a function call");
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
const updatedLastMessage = {
|
|
293
|
+
...lastMessage,
|
|
294
|
+
tool: {
|
|
295
|
+
...lastMessage.tool,
|
|
296
|
+
output: toolOutput,
|
|
297
|
+
confirmed: true,
|
|
298
|
+
runAt: new Date().toISOString(),
|
|
299
|
+
},
|
|
300
|
+
};
|
|
301
|
+
const newMessages = [...messages.slice(0, -1), updatedLastMessage];
|
|
302
|
+
setMessages(newMessages);
|
|
303
|
+
setIsChatLoading(true);
|
|
304
|
+
setCanSendMessage(false);
|
|
305
|
+
const payload = {
|
|
306
|
+
messages: newMessages,
|
|
307
|
+
uiTools: parseUITools(UITools),
|
|
308
|
+
chatbotConversationRef,
|
|
309
|
+
};
|
|
310
|
+
try {
|
|
311
|
+
await postUserConversation({
|
|
312
|
+
payload,
|
|
313
|
+
apikey: apiKey,
|
|
314
|
+
chatbotId,
|
|
315
|
+
baseUrl,
|
|
316
|
+
onData: (data) => handleStreamData(data, newMessages, onData),
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
catch (err) {
|
|
320
|
+
console.error("Error submitting tool result:", err);
|
|
321
|
+
setError(err.message || "Failed to submit tool result");
|
|
322
|
+
setIsChatLoading(false);
|
|
323
|
+
setCanSendMessage(true);
|
|
324
|
+
}
|
|
325
|
+
}, [
|
|
326
|
+
messages,
|
|
327
|
+
UITools,
|
|
328
|
+
chatbotConversationRef,
|
|
329
|
+
apiKey,
|
|
330
|
+
chatbotId,
|
|
331
|
+
baseUrl,
|
|
332
|
+
handleStreamData,
|
|
333
|
+
]);
|
|
334
|
+
// Handle tool call detection and auto-confirmation
|
|
335
|
+
useEffect(() => {
|
|
336
|
+
const lastMessage = messages[messages.length - 1];
|
|
337
|
+
if (lastMessage &&
|
|
338
|
+
lastMessage.role === MessageRole.FUNCTION &&
|
|
339
|
+
lastMessage.id !== lastProcessedToolCallId.current) {
|
|
340
|
+
const toolDetails = lastMessage.tool;
|
|
341
|
+
const toolDef = tools.find((t) => t.name === toolDetails.name);
|
|
342
|
+
// Notify consumer about the tool call
|
|
343
|
+
if (onToolCall)
|
|
344
|
+
onToolCall(toolDetails);
|
|
345
|
+
// Auto-confirm backend tools so the server can execute them
|
|
346
|
+
if (toolDef?.functionType === FunctionType.BACKEND &&
|
|
347
|
+
!toolDetails.confirmed) {
|
|
348
|
+
lastProcessedToolCallId.current = lastMessage.id;
|
|
349
|
+
confirmBackendTool();
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
}, [messages, tools, onToolCall, confirmBackendTool]);
|
|
353
|
+
const handleNewChat = useCallback(() => {
|
|
354
|
+
setSelectedConversation(null);
|
|
355
|
+
setChatbotConversationRef(undefined);
|
|
356
|
+
setMessages([]);
|
|
357
|
+
setInput("");
|
|
358
|
+
const dateString = getUTCDateTime();
|
|
359
|
+
const newConversation = {
|
|
360
|
+
chatbotConversationRef: uuidv4(),
|
|
361
|
+
messages: [],
|
|
362
|
+
chatbotId: chatbotId,
|
|
363
|
+
chatbotConversationTitle: "New Conversation",
|
|
364
|
+
createdAt: dateString,
|
|
365
|
+
updatedAt: dateString,
|
|
366
|
+
totalCostSoFar: 0,
|
|
367
|
+
totalTokensSoFar: 0,
|
|
368
|
+
};
|
|
369
|
+
setSelectedConversation(newConversation);
|
|
370
|
+
}, [chatbotId]);
|
|
371
|
+
const handleConversationSelect = useCallback((conversation) => {
|
|
372
|
+
setSelectedConversation(conversation);
|
|
373
|
+
setChatbotConversationRef(conversation.chatbotConversationRef);
|
|
374
|
+
setMessages(conversation.messages.map((m) => ({
|
|
375
|
+
...m,
|
|
376
|
+
id: m.id || uuidv4(),
|
|
377
|
+
})));
|
|
378
|
+
}, []);
|
|
379
|
+
const handleDeleteConversation = useCallback(async (conversationId) => {
|
|
380
|
+
try {
|
|
381
|
+
const storedIds = JSON.parse(localStorage.getItem(localStorageKey) || "[]");
|
|
382
|
+
const updatedIds = storedIds.filter((id) => id !== conversationId);
|
|
383
|
+
localStorage.setItem(localStorageKey, JSON.stringify(updatedIds));
|
|
384
|
+
if (selectedConversation?.chatbotConversationRef === conversationId) {
|
|
385
|
+
handleNewChat();
|
|
386
|
+
}
|
|
387
|
+
refreshConversations();
|
|
388
|
+
}
|
|
389
|
+
catch (err) {
|
|
390
|
+
console.error("Error deleting conversation:", err);
|
|
391
|
+
}
|
|
392
|
+
}, [
|
|
393
|
+
localStorageKey,
|
|
394
|
+
selectedConversation,
|
|
395
|
+
handleNewChat,
|
|
396
|
+
refreshConversations,
|
|
397
|
+
]);
|
|
398
|
+
const visibleMessages = useMemo(() => {
|
|
399
|
+
return messages
|
|
400
|
+
.filter((msg) => !msg.hiddenFromUser && msg.role !== MessageRole.FUNCTION)
|
|
401
|
+
.map((msg) => {
|
|
402
|
+
if (cleanResponse &&
|
|
403
|
+
msg.role === MessageRole.ASSISTANT &&
|
|
404
|
+
msg.message) {
|
|
405
|
+
return {
|
|
406
|
+
...msg,
|
|
407
|
+
message: cleanMarkdown(msg.message),
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
return msg;
|
|
411
|
+
});
|
|
412
|
+
}, [messages, cleanResponse]);
|
|
413
|
+
return {
|
|
414
|
+
messages: visibleMessages,
|
|
415
|
+
setMessages,
|
|
416
|
+
input,
|
|
417
|
+
setInput,
|
|
418
|
+
isChatLoading,
|
|
419
|
+
canSendMessage,
|
|
420
|
+
sendMessage,
|
|
421
|
+
submitToolResult,
|
|
422
|
+
chatbotConversationRef,
|
|
423
|
+
conversations,
|
|
424
|
+
selectedConversation,
|
|
425
|
+
handleNewChat,
|
|
426
|
+
handleConversationSelect,
|
|
427
|
+
handleDeleteConversation,
|
|
428
|
+
refreshConversations,
|
|
429
|
+
tools,
|
|
430
|
+
error,
|
|
431
|
+
};
|
|
432
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Socket } from "socket.io-client";
|
|
2
|
+
interface UseSocketOptions {
|
|
3
|
+
baseUrl?: string;
|
|
4
|
+
accessToken?: string | null;
|
|
5
|
+
chatbotConversationRef?: string;
|
|
6
|
+
onMessage?: (data: any) => void;
|
|
7
|
+
enabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const useSocket: ({ baseUrl, accessToken, chatbotConversationRef, onMessage, enabled, }: UseSocketOptions) => {
|
|
10
|
+
socket: Socket<import("@socket.io/component-emitter").DefaultEventsMap, import("@socket.io/component-emitter").DefaultEventsMap> | null;
|
|
11
|
+
isConnected: boolean;
|
|
12
|
+
};
|
|
13
|
+
export default useSocket;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { useEffect, useRef } from "react";
|
|
2
|
+
import socketService from "../services/socketService.js";
|
|
3
|
+
import { SOCKET_EVENTS } from "../constants/socketEvents.js";
|
|
4
|
+
export const useSocket = ({ baseUrl, accessToken, chatbotConversationRef, onMessage, enabled = true, }) => {
|
|
5
|
+
const socketRef = useRef(null);
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (!enabled || !baseUrl)
|
|
8
|
+
return;
|
|
9
|
+
const socket = socketService.connect(baseUrl, accessToken, chatbotConversationRef);
|
|
10
|
+
socketRef.current = socket;
|
|
11
|
+
const handleMessage = (data) => {
|
|
12
|
+
if (onMessage)
|
|
13
|
+
onMessage(data);
|
|
14
|
+
};
|
|
15
|
+
socket.on(SOCKET_EVENTS.MESSAGE, handleMessage);
|
|
16
|
+
return () => {
|
|
17
|
+
socket.off(SOCKET_EVENTS.MESSAGE, handleMessage);
|
|
18
|
+
};
|
|
19
|
+
}, [baseUrl, accessToken, chatbotConversationRef, enabled, onMessage]);
|
|
20
|
+
return {
|
|
21
|
+
socket: socketRef.current,
|
|
22
|
+
isConnected: socketService.isConnected(),
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
export default useSocket;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
|
+
export { default as CmndChatBot } from "./CmndChatBot/index.js";
|
|
1
2
|
export { default as ChatProvider } from "./ChatProvider/index.js";
|
|
2
3
|
export { ConversationsPanel } from "./components/ConversationsPanel/index.js";
|
|
3
|
-
export {
|
|
4
|
+
export { default as Conversation } from "./components/Conversation.js";
|
|
5
|
+
export { CmndChatContext, InputFieldProps, SendButtonProps, CustomStyles, CMNDChatMemory, ChatbotConversation, ChatbotConversationsResponse, Message, MessageRole, ConversationDataObject, ToolDetails, FunctionType, CMNDChatbotUITool, ChatbotConversationMessage, } from "./type.js";
|
|
4
6
|
export { setCurrentConversationMemory, deleteCurrentConversationMemory, } from "./ChatProvider/index.js";
|
|
5
7
|
export { useCMNDChatContext } from "./ChatProvider/useChatContext.js";
|
|
8
|
+
export { useCMNDChat, UseCMNDChatOptions, UseCMNDChatResult, } from "./hooks/use-cmnd-chat.js";
|
|
9
|
+
export { useSocket } from "./hooks/use-socket.js";
|
|
10
|
+
export { default as socketService } from "./services/socketService.js";
|
|
11
|
+
export { SOCKET_EVENTS, SOCKET_MESSAGE_TYPES, SocketEvent, SocketMessageType, } from "./constants/socketEvents.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
|
+
export { default as CmndChatBot } from "./CmndChatBot/index.js";
|
|
1
2
|
export { default as ChatProvider } from "./ChatProvider/index.js";
|
|
2
3
|
export { ConversationsPanel } from "./components/ConversationsPanel/index.js";
|
|
4
|
+
export { default as Conversation } from "./components/Conversation.js";
|
|
5
|
+
export { MessageRole, FunctionType, } from "./type.js";
|
|
3
6
|
export { setCurrentConversationMemory, deleteCurrentConversationMemory, } from "./ChatProvider/index.js";
|
|
4
7
|
export { useCMNDChatContext } from "./ChatProvider/useChatContext.js";
|
|
8
|
+
export { useCMNDChat, } from "./hooks/use-cmnd-chat.js";
|
|
9
|
+
// Socket exports
|
|
10
|
+
export { useSocket } from "./hooks/use-socket.js";
|
|
11
|
+
export { default as socketService } from "./services/socketService.js";
|
|
12
|
+
export { SOCKET_EVENTS, SOCKET_MESSAGE_TYPES, } from "./constants/socketEvents.js";
|
|
@@ -5,5 +5,5 @@ interface IDeleteChatbotConversationMemory {
|
|
|
5
5
|
memoryKeyToDelete: string;
|
|
6
6
|
baseUrl: string;
|
|
7
7
|
}
|
|
8
|
-
declare const deleteChatbotConversationMemory: ({ organizationId, chatbotId, chatbotConversationRef, memoryKeyToDelete, baseUrl, }: IDeleteChatbotConversationMemory) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
8
|
+
declare const deleteChatbotConversationMemory: ({ organizationId, chatbotId, chatbotConversationRef, memoryKeyToDelete, baseUrl, }: IDeleteChatbotConversationMemory) => Promise<import("axios").AxiosResponse<any, any, {}>>;
|
|
9
9
|
export default deleteChatbotConversationMemory;
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
declare const getEmbedChatBotById: (baseUrl: string, organizationId: number, chatbotId: number) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
1
|
+
declare const getEmbedChatBotById: (baseUrl: string, organizationId: number, chatbotId: number) => Promise<import("axios").AxiosResponse<any, any, {}>>;
|
|
2
2
|
export default getEmbedChatBotById;
|
|
@@ -7,5 +7,5 @@ interface IPatchChatbotConversationMemory {
|
|
|
7
7
|
};
|
|
8
8
|
baseUrl: string;
|
|
9
9
|
}
|
|
10
|
-
declare const patchChatbotConversationMemory: ({ organizationId, chatbotId, chatbotConversationRef, memory, baseUrl, }: IPatchChatbotConversationMemory) => Promise<import("axios").AxiosResponse<any, any>>;
|
|
10
|
+
declare const patchChatbotConversationMemory: ({ organizationId, chatbotId, chatbotConversationRef, memory, baseUrl, }: IPatchChatbotConversationMemory) => Promise<import("axios").AxiosResponse<any, any, {}>>;
|
|
11
11
|
export default patchChatbotConversationMemory;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Socket } from "socket.io-client";
|
|
2
|
+
declare class SocketService {
|
|
3
|
+
private socket;
|
|
4
|
+
connect(baseUrl: string, accessToken?: string | null, chatbotConversationRef?: string): Socket;
|
|
5
|
+
disconnect(): void;
|
|
6
|
+
getSocket(): Socket | null;
|
|
7
|
+
isConnected(): boolean;
|
|
8
|
+
}
|
|
9
|
+
export declare const socketService: SocketService;
|
|
10
|
+
export default socketService;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { io } from "socket.io-client";
|
|
2
|
+
import { SOCKET_EVENTS } from "../constants/socketEvents.js";
|
|
3
|
+
class SocketService {
|
|
4
|
+
socket = null;
|
|
5
|
+
connect(baseUrl, accessToken, chatbotConversationRef) {
|
|
6
|
+
if (this.socket?.connected) {
|
|
7
|
+
if (chatbotConversationRef) {
|
|
8
|
+
this.socket.emit(SOCKET_EVENTS.JOIN_CHATBOT_CONVERSATION, {
|
|
9
|
+
chatbotConversationRef,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
return this.socket;
|
|
13
|
+
}
|
|
14
|
+
this.socket = io(baseUrl, {
|
|
15
|
+
auth: {
|
|
16
|
+
accessToken,
|
|
17
|
+
chatbotConversationRef,
|
|
18
|
+
},
|
|
19
|
+
transports: ["websocket", "polling"],
|
|
20
|
+
reconnection: true,
|
|
21
|
+
reconnectionAttempts: 5,
|
|
22
|
+
reconnectionDelay: 1000,
|
|
23
|
+
});
|
|
24
|
+
return this.socket;
|
|
25
|
+
}
|
|
26
|
+
disconnect() {
|
|
27
|
+
if (this.socket) {
|
|
28
|
+
this.socket.disconnect();
|
|
29
|
+
this.socket = null;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
getSocket() {
|
|
33
|
+
return this.socket;
|
|
34
|
+
}
|
|
35
|
+
isConnected() {
|
|
36
|
+
return this.socket?.connected ?? false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
export const socketService = new SocketService();
|
|
40
|
+
export default socketService;
|