@cmnd-ai/chatbot-react 1.10.0 → 1.13.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/processStream/index.js +39 -13
- package/dist/CmndChatBot/index.d.ts +2 -1
- package/dist/CmndChatBot/index.js +2 -2
- package/dist/hooks/use-cmnd-chat.d.ts +130 -0
- package/dist/hooks/use-cmnd-chat.js +396 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +4 -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/type.d.ts +132 -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 +2 -2
- package/readme.dev.md +49 -0
package/Readme.md
CHANGED
|
@@ -117,6 +117,110 @@ const App = () => {
|
|
|
117
117
|
};
|
|
118
118
|
```
|
|
119
119
|
|
|
120
|
+
## Headless Usage with `useCMNDChat` Hook
|
|
121
|
+
|
|
122
|
+
If you want complete control over your UI while maintaining the powerful chatbot logic, you can use the `useCMNDChat` hook. It handles all state management, real-time streaming, conversation history, and tool orchestration.
|
|
123
|
+
|
|
124
|
+
```tsx
|
|
125
|
+
import { useCMNDChat, MessageRole } from "@cmnd-ai/chatbot-react";
|
|
126
|
+
|
|
127
|
+
const MyCustomChat = () => {
|
|
128
|
+
const {
|
|
129
|
+
messages,
|
|
130
|
+
input,
|
|
131
|
+
setInput,
|
|
132
|
+
isChatLoading,
|
|
133
|
+
canSendMessage,
|
|
134
|
+
sendMessage,
|
|
135
|
+
submitToolResult,
|
|
136
|
+
conversations,
|
|
137
|
+
handleNewChat,
|
|
138
|
+
handleConversationSelect,
|
|
139
|
+
error,
|
|
140
|
+
} = useCMNDChat({
|
|
141
|
+
chatbotId: 123,
|
|
142
|
+
organizationId: 456,
|
|
143
|
+
baseUrl: "https://api.cmnd.ai",
|
|
144
|
+
apiKey: "your-api-key",
|
|
145
|
+
cleanResponse: true, // Optional: Returns assistant messages as plaintext
|
|
146
|
+
onData: (data) => console.log("Stream update:", data),
|
|
147
|
+
onToolCall: async (tool) => {
|
|
148
|
+
console.log("AI called tool:", tool.name);
|
|
149
|
+
|
|
150
|
+
// For UI tools, you must execute and call submitToolResult
|
|
151
|
+
if (tool.name === "my_custom_tool") {
|
|
152
|
+
const result = await runMyTool(tool.args);
|
|
153
|
+
submitToolResult(JSON.stringify(result));
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return (
|
|
159
|
+
<div className="custom-chat">
|
|
160
|
+
<div className="message-list">
|
|
161
|
+
{messages.map((m) => (
|
|
162
|
+
<div key={m.id} className={`message ${m.role}`}>
|
|
163
|
+
{m.role === MessageRole.ASSISTANT ? "🤖" : "👤"}: {m.message}
|
|
164
|
+
{/* Handle tool calls */}
|
|
165
|
+
{m.role === MessageRole.FUNCTION && (
|
|
166
|
+
<div className="tool-ui">
|
|
167
|
+
Rendering Tool: {m.tool.name}
|
|
168
|
+
{/* Once done, call submitToolResult("result") */}
|
|
169
|
+
</div>
|
|
170
|
+
)}
|
|
171
|
+
</div>
|
|
172
|
+
))}
|
|
173
|
+
</div>
|
|
174
|
+
|
|
175
|
+
<div className="controls">
|
|
176
|
+
<input
|
|
177
|
+
value={input}
|
|
178
|
+
onChange={(e) => setInput(e.target.value)}
|
|
179
|
+
placeholder="Type here..."
|
|
180
|
+
/>
|
|
181
|
+
<button onClick={() => sendMessage()}>Send</button>
|
|
182
|
+
<button onClick={handleNewChat}>Clear Chat</button>
|
|
183
|
+
</div>
|
|
184
|
+
</div>
|
|
185
|
+
);
|
|
186
|
+
};
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### Hook API Reference
|
|
190
|
+
|
|
191
|
+
#### `UseCMNDChatOptions`
|
|
192
|
+
|
|
193
|
+
| Option | Type | Description |
|
|
194
|
+
| ---------------- | --------------------- | -------------------------------------------------------------------------- |
|
|
195
|
+
| `chatbotId` | `number` | **Required**. Your unique chatbot ID. |
|
|
196
|
+
| `organizationId` | `number` | **Required**. Your unique organization ID. |
|
|
197
|
+
| `baseUrl` | `string` | **Required**. The CMND API base URL. |
|
|
198
|
+
| `apiKey` | `string` | Optional API key for authentication. |
|
|
199
|
+
| `initialMemory` | `object` | Context to send when starting a new conversation. |
|
|
200
|
+
| `UITools` | `CMNDChatbotUITool[]` | Array of custom UI tools. |
|
|
201
|
+
| `onData` | `function` | Triggered for every response chunk or message update. |
|
|
202
|
+
| `onToolCall` | `function` | Triggered when the AI requests a tool execution. |
|
|
203
|
+
| `cleanResponse` | `boolean` | If `true`, assistant messages are returned as plaintext. Default: `false`. |
|
|
204
|
+
|
|
205
|
+
#### `UseCMNDChatResult`
|
|
206
|
+
|
|
207
|
+
| Property | Type | Description |
|
|
208
|
+
| -------------------------- | ----------------------- | --------------------------------------------------------- |
|
|
209
|
+
| `messages` | `Message[]` | Array of all messages in the current conversation thread. |
|
|
210
|
+
| `input` | `string` | The current text in the chat input. |
|
|
211
|
+
| `setInput` | `function` | Update the chat input text. |
|
|
212
|
+
| `isChatLoading` | `boolean` | True if a message is being generated or tool is running. |
|
|
213
|
+
| `canSendMessage` | `boolean` | True if the system is ready to receive the next message. |
|
|
214
|
+
| `sendMessage` | `(text?) => Promise` | Send a message. Uses `input` if `text` is omitted. |
|
|
215
|
+
| `submitToolResult` | `(output) => Promise` | Submit results from a UI tool back to the AI. |
|
|
216
|
+
| `conversations` | `ChatbotConversation[]` | List of historical conversations. |
|
|
217
|
+
| `selectedConversation` | `ChatbotConversation` | The currently active conversation metadata. |
|
|
218
|
+
| `handleNewChat` | `function` | Starts a fresh conversation thread. |
|
|
219
|
+
| `handleConversationSelect` | `function` | Loads a specific conversation from history. |
|
|
220
|
+
| `handleDeleteConversation` | `function` | Deletes a conversation from history. |
|
|
221
|
+
| `tools` | `any[]` | All available tools (API + UI). |
|
|
222
|
+
| `error` | `string` | Contains description of any API or processing errors. |
|
|
223
|
+
|
|
120
224
|
## Props
|
|
121
225
|
|
|
122
226
|
### Required Props
|
|
@@ -130,15 +234,15 @@ const App = () => {
|
|
|
130
234
|
|
|
131
235
|
### Optional Props
|
|
132
236
|
|
|
133
|
-
| Prop | Type | Default | Description
|
|
134
|
-
| ----------------------- | --------------------- | ----------- |
|
|
135
|
-
| `theme` | `"light" \| "dark"` | `"light"` | Theme for the chatbot
|
|
136
|
-
| `UITools` | `CMNDChatbotUITool[]` | `[]` | Array of UI tools
|
|
137
|
-
| `enabledTools` | `any[]` | `[]` | Array of enabled tools
|
|
138
|
-
| `initialMemory` | `CMNDChatMemory` | `undefined` | Initial conversation memory
|
|
139
|
-
| `customStyles` | `CustomStyles` | `undefined` | Custom CSS styles
|
|
140
|
-
| `Components` | `Components` | `undefined` | Custom component overrides
|
|
141
|
-
| `chatHistoryStorageKey` | `string` | `undefined` | The chat history key defined by the client
|
|
237
|
+
| Prop | Type | Default | Description |
|
|
238
|
+
| ----------------------- | --------------------- | ----------- | ------------------------------------------ |
|
|
239
|
+
| `theme` | `"light" \| "dark"` | `"light"` | Theme for the chatbot |
|
|
240
|
+
| `UITools` | `CMNDChatbotUITool[]` | `[]` | Array of UI tools |
|
|
241
|
+
| `enabledTools` | `any[]` | `[]` | Array of enabled tools |
|
|
242
|
+
| `initialMemory` | `CMNDChatMemory` | `undefined` | Initial conversation memory |
|
|
243
|
+
| `customStyles` | `CustomStyles` | `undefined` | Custom CSS styles |
|
|
244
|
+
| `Components` | `Components` | `undefined` | Custom component overrides |
|
|
245
|
+
| `chatHistoryStorageKey` | `string` | `undefined` | The chat history key defined by the client |
|
|
142
246
|
|
|
143
247
|
## Custom Components
|
|
144
248
|
|
|
@@ -5,7 +5,7 @@ export declare const ChatProviderContext: React.Context<CmndChatContext | undefi
|
|
|
5
5
|
export interface ChatProviderProps extends Omit<CmndChatBotProps, "postSessionMessage"> {
|
|
6
6
|
children?: React.ReactNode | ((props: CmndChatBotProps) => React.ReactNode);
|
|
7
7
|
}
|
|
8
|
-
export declare const setCurrentConversationMemory: (memory: CMNDChatMemory) => Promise<import("axios").AxiosResponse<any, any> | undefined>;
|
|
9
|
-
export declare const deleteCurrentConversationMemory: (memoryKeyToDelete: string) => Promise<import("axios").AxiosResponse<any, any> | undefined>;
|
|
8
|
+
export declare const setCurrentConversationMemory: (memory: CMNDChatMemory) => Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
|
|
9
|
+
export declare const deleteCurrentConversationMemory: (memoryKeyToDelete: string) => Promise<import("axios").AxiosResponse<any, any, {}> | undefined>;
|
|
10
10
|
declare function ChatProvider(props: ChatProviderProps): JSX.Element | null;
|
|
11
11
|
export default ChatProvider;
|
|
@@ -3,6 +3,7 @@ const processStream = async (reader, onData) => {
|
|
|
3
3
|
if (!onData)
|
|
4
4
|
return;
|
|
5
5
|
let fullAssistantMessage = "";
|
|
6
|
+
let functionName = "";
|
|
6
7
|
let buffer = ""; // buffer to store incomplete JSON string fragments
|
|
7
8
|
try {
|
|
8
9
|
while (true) {
|
|
@@ -44,17 +45,31 @@ const processStream = async (reader, onData) => {
|
|
|
44
45
|
});
|
|
45
46
|
}
|
|
46
47
|
else if (dataObject.function_call) {
|
|
47
|
-
|
|
48
|
+
const { name } = dataObject.function_call;
|
|
49
|
+
if (name)
|
|
50
|
+
functionName = name;
|
|
51
|
+
onData({
|
|
52
|
+
completionFinished: false,
|
|
53
|
+
finalResponseWithUsageData: false,
|
|
54
|
+
streamingFunctionCall: true,
|
|
55
|
+
functionName,
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
else if (dataObject.function_result) {
|
|
59
|
+
onData({
|
|
60
|
+
completionFinished: false,
|
|
61
|
+
finalResponseWithUsageData: false,
|
|
62
|
+
streamingFunctionCall: false,
|
|
63
|
+
functionResult: dataObject.function_result,
|
|
64
|
+
});
|
|
48
65
|
}
|
|
49
66
|
else {
|
|
50
|
-
|
|
51
|
-
//and it is completed
|
|
52
|
-
//get the last message from the dataObject to check if it is a function call
|
|
53
|
-
const { messages, completionFinished, finalResponseWithUsageData, chatbotConversationRef, totalTokens, totalCost, } = dataObject;
|
|
67
|
+
const { messages, completionFinished, finalResponseWithUsageData, conversationRef, chatbotConversationRef, totalTokens, totalCost, } = dataObject;
|
|
54
68
|
onData({
|
|
55
69
|
messages,
|
|
56
70
|
completionFinished,
|
|
57
71
|
finalResponseWithUsageData,
|
|
72
|
+
conversationRef,
|
|
58
73
|
chatbotConversationRef,
|
|
59
74
|
totalTokens,
|
|
60
75
|
totalCost,
|
|
@@ -68,13 +83,23 @@ const processStream = async (reader, onData) => {
|
|
|
68
83
|
continue;
|
|
69
84
|
}
|
|
70
85
|
console.error("StreamError caught", error);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
:
|
|
76
|
-
|
|
77
|
-
|
|
86
|
+
if (error instanceof StreamError) {
|
|
87
|
+
onData({
|
|
88
|
+
completionFinished: true,
|
|
89
|
+
message: error.message,
|
|
90
|
+
finalResponseWithUsageData: true,
|
|
91
|
+
hasError: true,
|
|
92
|
+
errorPath: error.path,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
onData({
|
|
97
|
+
completionFinished: true,
|
|
98
|
+
message: "Oops! I ran into a problem.",
|
|
99
|
+
finalResponseWithUsageData: true,
|
|
100
|
+
hasError: true,
|
|
101
|
+
});
|
|
102
|
+
}
|
|
78
103
|
}
|
|
79
104
|
}
|
|
80
105
|
}
|
|
@@ -84,11 +109,12 @@ const processStream = async (reader, onData) => {
|
|
|
84
109
|
}
|
|
85
110
|
}
|
|
86
111
|
catch (error) {
|
|
87
|
-
console.error("
|
|
112
|
+
console.error("Fatal error in processStream", error);
|
|
88
113
|
onData({
|
|
89
114
|
completionFinished: true,
|
|
90
115
|
message: "Oops! I ran into a problem.",
|
|
91
116
|
finalResponseWithUsageData: true,
|
|
117
|
+
hasError: true,
|
|
92
118
|
});
|
|
93
119
|
}
|
|
94
120
|
};
|
|
@@ -20,6 +20,7 @@ export interface CmndChatBotProps {
|
|
|
20
20
|
LoadingIndicator?: () => JSX.Element;
|
|
21
21
|
error?: any;
|
|
22
22
|
};
|
|
23
|
+
cleanResponse?: boolean;
|
|
23
24
|
}
|
|
24
|
-
declare function CmndChatBot({ chatbotId, organizationId, apiKey, baseUrl, theme, UITools, customStyles, chatHistoryStorageKey, enabledTools, Components, initialMemory, }: CmndChatBotProps): JSX.Element;
|
|
25
|
+
declare function CmndChatBot({ chatbotId, organizationId, apiKey, baseUrl, theme, UITools, customStyles, chatHistoryStorageKey, enabledTools, Components, initialMemory, cleanResponse, }: CmndChatBotProps): JSX.Element;
|
|
25
26
|
export default CmndChatBot;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import ChatProvider from "../ChatProvider/index.js";
|
|
3
|
-
function CmndChatBot({ chatbotId, organizationId, apiKey, baseUrl, theme, UITools, customStyles, chatHistoryStorageKey, enabledTools = [], Components, initialMemory, }) {
|
|
4
|
-
return (_jsx(ChatProvider, { chatbotId: chatbotId, organizationId: organizationId, chatHistoryStorageKey: chatHistoryStorageKey, apiKey: apiKey, baseUrl: baseUrl, theme: theme, UITools: UITools, customStyles: customStyles, enabledTools: enabledTools, Components: Components, initialMemory: initialMemory }));
|
|
3
|
+
function CmndChatBot({ chatbotId, organizationId, apiKey, baseUrl, theme, UITools, customStyles, chatHistoryStorageKey, enabledTools = [], Components, initialMemory, cleanResponse, }) {
|
|
4
|
+
return (_jsx(ChatProvider, { chatbotId: chatbotId, organizationId: organizationId, chatHistoryStorageKey: chatHistoryStorageKey, apiKey: apiKey, baseUrl: baseUrl, theme: theme, UITools: UITools, customStyles: customStyles, enabledTools: enabledTools, Components: Components, initialMemory: initialMemory, cleanResponse: cleanResponse }));
|
|
5
5
|
}
|
|
6
6
|
export default CmndChatBot;
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import { ChatbotConversation, Message, CMNDChatMemory, CMNDChatbotUITool, ConversationDataObject, ToolDetails } from "../type.js";
|
|
3
|
+
/**
|
|
4
|
+
* Options for configuring the useCMNDChat hook.
|
|
5
|
+
*/
|
|
6
|
+
export interface UseCMNDChatOptions {
|
|
7
|
+
/** The unique identifier for the chatbot. */
|
|
8
|
+
chatbotId: number;
|
|
9
|
+
/** The unique identifier for the organization. */
|
|
10
|
+
organizationId: number;
|
|
11
|
+
/** The base URL for the CMND API. */
|
|
12
|
+
baseUrl: string;
|
|
13
|
+
/** Optional API key for authentication. If provided, it will be sent in the 'x-api-key' header. */
|
|
14
|
+
apiKey?: string;
|
|
15
|
+
/** Optional key used for local message persistence. Defaults to a combined string of orgId and chatbotId. */
|
|
16
|
+
chatHistoryStorageKey?: string;
|
|
17
|
+
/** Initial memory/context to send when starting a new conversation. */
|
|
18
|
+
initialMemory?: CMNDChatMemory;
|
|
19
|
+
/** Array of custom UI tools that the AI can call. */
|
|
20
|
+
UITools?: CMNDChatbotUITool[];
|
|
21
|
+
/** Callback triggered whenever new data (chunks or finalized messages) is received from the stream. */
|
|
22
|
+
onData?: (data: ConversationDataObject) => void;
|
|
23
|
+
/**
|
|
24
|
+
* Callback triggered whenever the AI initiates a tool call.
|
|
25
|
+
*
|
|
26
|
+
* For **backend tools**: The hook automatically confirms them, which triggers your backend server
|
|
27
|
+
* to execute the tool and return the output. Use this callback for logging or UI updates.
|
|
28
|
+
*
|
|
29
|
+
* For **UI tools**: You must execute the tool in your frontend, then call submitToolResult with the output.
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```tsx
|
|
33
|
+
* onToolCall: async (toolDetails) => {
|
|
34
|
+
* if (toolDetails.name === 'show_ui_component') {
|
|
35
|
+
* // UI tool - execute in frontend
|
|
36
|
+
* displayComponent(toolDetails.args);
|
|
37
|
+
* submitToolResult('Component displayed');
|
|
38
|
+
* }
|
|
39
|
+
* // Backend tools are handled automatically by the server
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
onToolCall?: (toolCall: ToolDetails) => void;
|
|
44
|
+
/**
|
|
45
|
+
* Whether to clean the AI response by removing markdown characters.
|
|
46
|
+
* If true, assistant messages will be returned as plaintext.
|
|
47
|
+
* @default false
|
|
48
|
+
*/
|
|
49
|
+
cleanResponse?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* The object returned by the useCMNDChat hook.
|
|
53
|
+
*/
|
|
54
|
+
export interface UseCMNDChatResult {
|
|
55
|
+
/** The list of messages in the current active conversation thread. */
|
|
56
|
+
messages: Message[];
|
|
57
|
+
/** A setter function for the messages list. */
|
|
58
|
+
setMessages: React.Dispatch<React.SetStateAction<Message[]>>;
|
|
59
|
+
/** The current text value of the chat input field. */
|
|
60
|
+
input: string;
|
|
61
|
+
/** A setter function for the chat input field. */
|
|
62
|
+
setInput: React.Dispatch<React.SetStateAction<string>>;
|
|
63
|
+
/** Indicates if a message is currently being processed or streamed. */
|
|
64
|
+
isChatLoading: boolean;
|
|
65
|
+
/** Indicates if the user is allowed to send a message (e.g., not loading, not empty). */
|
|
66
|
+
canSendMessage: boolean;
|
|
67
|
+
/**
|
|
68
|
+
* Sends a message to the chatbot.
|
|
69
|
+
* @param text Optional text to send. If not provided, it uses the current 'input' state.
|
|
70
|
+
* @param onData Optional callback for this specific message stream.
|
|
71
|
+
*/
|
|
72
|
+
sendMessage: (text?: string, onData?: (data: ConversationDataObject) => void) => Promise<void>;
|
|
73
|
+
/**
|
|
74
|
+
* Submits the result of a tool call back to the AI.
|
|
75
|
+
* @param toolOutput The string result of the tool execution.
|
|
76
|
+
* @param onData Optional callback for the resulting message stream.
|
|
77
|
+
*/
|
|
78
|
+
submitToolResult: (toolOutput: string, onData?: (data: ConversationDataObject) => void) => Promise<void>;
|
|
79
|
+
/** The reference ID for the current active conversation. */
|
|
80
|
+
chatbotConversationRef: string | undefined;
|
|
81
|
+
/** A list of all historical conversations for the current chatbot/org. */
|
|
82
|
+
conversations: ChatbotConversation[];
|
|
83
|
+
/** The currently selected conversation object from history. */
|
|
84
|
+
selectedConversation: ChatbotConversation | null;
|
|
85
|
+
/** Resets the chat state and prepares for a new conversation thread. */
|
|
86
|
+
handleNewChat: () => void;
|
|
87
|
+
/**
|
|
88
|
+
* Switches to an existing conversation from history.
|
|
89
|
+
* @param conversation The conversation object to load.
|
|
90
|
+
*/
|
|
91
|
+
handleConversationSelect: (conversation: ChatbotConversation) => void;
|
|
92
|
+
/**
|
|
93
|
+
* Deletes a conversation from history and local storage.
|
|
94
|
+
* @param conversationId The ID of the conversation to delete.
|
|
95
|
+
*/
|
|
96
|
+
handleDeleteConversation: (conversationId: string) => Promise<void>;
|
|
97
|
+
/** Re-fetches the list of historical conversations. */
|
|
98
|
+
refreshConversations: () => Promise<void>;
|
|
99
|
+
/** Combined list of backend tools (from API) and UI tools (provided in options). */
|
|
100
|
+
tools: any[];
|
|
101
|
+
/** Holds any error message encountered during API calls. */
|
|
102
|
+
error: string | null;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* A comprehensive hook to manage CMND.AI chatbot logic without being forced to use the default UI.
|
|
106
|
+
* This hook handles conversation state, message sending (including streaming), history management, and tool call orchestration.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```tsx
|
|
110
|
+
* const { messages, sendMessage, input, setInput, submitToolResult } = useCMNDChat({
|
|
111
|
+
* chatbotId: 1,
|
|
112
|
+
* organizationId: 1,
|
|
113
|
+
* baseUrl: 'https://api.cmnd.ai',
|
|
114
|
+
* cleanResponse: true, // Optional: Returns assistant messages as plaintext
|
|
115
|
+
* onToolCall: async (toolDetails) => {
|
|
116
|
+
* // Backend tools are auto-confirmed by the hook
|
|
117
|
+
* if (toolDetails.name === 'get_playlists') {
|
|
118
|
+
* console.log('Fetching playlists from backend...');
|
|
119
|
+
* }
|
|
120
|
+
*
|
|
121
|
+
* // UI tools need manual execution and confirmation
|
|
122
|
+
* if (toolDetails.name === 'show_notification') {
|
|
123
|
+
* showNotification(toolDetails.args.message);
|
|
124
|
+
* submitToolResult('Notification displayed');
|
|
125
|
+
* }
|
|
126
|
+
* }
|
|
127
|
+
* });
|
|
128
|
+
* ```
|
|
129
|
+
*/
|
|
130
|
+
export declare const useCMNDChat: (options: UseCMNDChatOptions) => UseCMNDChatResult;
|
|
@@ -0,0 +1,396 @@
|
|
|
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
|
+
/**
|
|
14
|
+
* A comprehensive hook to manage CMND.AI chatbot logic without being forced to use the default UI.
|
|
15
|
+
* This hook handles conversation state, message sending (including streaming), history management, and tool call orchestration.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```tsx
|
|
19
|
+
* const { messages, sendMessage, input, setInput, submitToolResult } = useCMNDChat({
|
|
20
|
+
* chatbotId: 1,
|
|
21
|
+
* organizationId: 1,
|
|
22
|
+
* baseUrl: 'https://api.cmnd.ai',
|
|
23
|
+
* cleanResponse: true, // Optional: Returns assistant messages as plaintext
|
|
24
|
+
* onToolCall: async (toolDetails) => {
|
|
25
|
+
* // Backend tools are auto-confirmed by the hook
|
|
26
|
+
* if (toolDetails.name === 'get_playlists') {
|
|
27
|
+
* console.log('Fetching playlists from backend...');
|
|
28
|
+
* }
|
|
29
|
+
*
|
|
30
|
+
* // UI tools need manual execution and confirmation
|
|
31
|
+
* if (toolDetails.name === 'show_notification') {
|
|
32
|
+
* showNotification(toolDetails.args.message);
|
|
33
|
+
* submitToolResult('Notification displayed');
|
|
34
|
+
* }
|
|
35
|
+
* }
|
|
36
|
+
* });
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export const useCMNDChat = (options) => {
|
|
40
|
+
const { chatbotId, organizationId, baseUrl, apiKey, chatHistoryStorageKey, initialMemory, UITools, onData: globalOnData, onToolCall, cleanResponse = false, } = options;
|
|
41
|
+
const [messages, setMessages] = useState([]);
|
|
42
|
+
const [input, setInput] = useState("");
|
|
43
|
+
const [isChatLoading, setIsChatLoading] = useState(false);
|
|
44
|
+
const [canSendMessage, setCanSendMessage] = useState(true);
|
|
45
|
+
const [chatbotConversationRef, setChatbotConversationRef] = useState(undefined);
|
|
46
|
+
const [conversations, setConversations] = useState([]);
|
|
47
|
+
const [selectedConversation, setSelectedConversation] = useState(null);
|
|
48
|
+
const [enabledTools, setEnabledTools] = useState([]);
|
|
49
|
+
const [error, setError] = useState(null);
|
|
50
|
+
const lastProcessedToolCallId = useRef(null);
|
|
51
|
+
const localStorageKey = useMemo(() => chatHistoryStorageKey ||
|
|
52
|
+
getConversationLocalStorageKey({ organizationId, chatbotId }), [chatHistoryStorageKey, organizationId, chatbotId]);
|
|
53
|
+
const tools = useMemo(() => getTools({
|
|
54
|
+
apiTools: enabledTools,
|
|
55
|
+
uiTools: parseUITools(UITools),
|
|
56
|
+
}), [enabledTools, UITools]);
|
|
57
|
+
// Fetch Chatbot Info for enabled tools
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
if (!baseUrl || !organizationId || !chatbotId)
|
|
60
|
+
return;
|
|
61
|
+
getEmbedChatBotById(baseUrl, organizationId, chatbotId)
|
|
62
|
+
.then((response) => {
|
|
63
|
+
const { chatbot } = response.data;
|
|
64
|
+
setEnabledTools(chatbot.enabledTools || []);
|
|
65
|
+
})
|
|
66
|
+
.catch((err) => {
|
|
67
|
+
console.error("Error fetching chatbot tools:", err);
|
|
68
|
+
});
|
|
69
|
+
}, [baseUrl, organizationId, chatbotId]);
|
|
70
|
+
const refreshConversations = useCallback(async () => {
|
|
71
|
+
try {
|
|
72
|
+
const storedIds = JSON.parse(localStorage.getItem(localStorageKey) || "[]");
|
|
73
|
+
if (storedIds.length > 0) {
|
|
74
|
+
const response = await getChatBotConversationsList({
|
|
75
|
+
organizationId,
|
|
76
|
+
chatbotId,
|
|
77
|
+
conversationIds: storedIds,
|
|
78
|
+
baseUrl,
|
|
79
|
+
});
|
|
80
|
+
setConversations(response.chatbotConversations || []);
|
|
81
|
+
}
|
|
82
|
+
else {
|
|
83
|
+
setConversations([]);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (err) {
|
|
87
|
+
console.error("Error fetching conversations:", err);
|
|
88
|
+
}
|
|
89
|
+
}, [baseUrl, chatbotId, localStorageKey, organizationId]);
|
|
90
|
+
useEffect(() => {
|
|
91
|
+
refreshConversations();
|
|
92
|
+
}, [refreshConversations]);
|
|
93
|
+
const handleStreamData = useCallback(async (data, currentMessages, onDataCallback) => {
|
|
94
|
+
if (onDataCallback)
|
|
95
|
+
onDataCallback(data);
|
|
96
|
+
if (globalOnData)
|
|
97
|
+
globalOnData(data);
|
|
98
|
+
// Handle streaming assistant message
|
|
99
|
+
if (data.message && !data.finalResponseWithUsageData) {
|
|
100
|
+
// Clear loading state when assistant starts responding
|
|
101
|
+
setIsChatLoading(false);
|
|
102
|
+
setCanSendMessage(true);
|
|
103
|
+
const assistantMessage = {
|
|
104
|
+
role: MessageRole.ASSISTANT,
|
|
105
|
+
message: data.message,
|
|
106
|
+
id: "streaming-assistant-message",
|
|
107
|
+
unuseful: false,
|
|
108
|
+
hiddenFromUser: false,
|
|
109
|
+
};
|
|
110
|
+
setMessages([...currentMessages, assistantMessage]);
|
|
111
|
+
}
|
|
112
|
+
if (data.finalResponseWithUsageData) {
|
|
113
|
+
const { messages: updatedMessages, message, hasError } = data;
|
|
114
|
+
const newConversationId = data.chatbotConversationRef || data.conversationRef;
|
|
115
|
+
if (hasError) {
|
|
116
|
+
setError(message || "Oops! I ran into a problem.");
|
|
117
|
+
}
|
|
118
|
+
if (newConversationId) {
|
|
119
|
+
setChatbotConversationRef(newConversationId);
|
|
120
|
+
await saveConversationIdToLocalStorage({
|
|
121
|
+
chatbotConversationRef: newConversationId,
|
|
122
|
+
chatbotId,
|
|
123
|
+
organizationId,
|
|
124
|
+
chatHistoryStorageKey,
|
|
125
|
+
});
|
|
126
|
+
refreshConversations();
|
|
127
|
+
}
|
|
128
|
+
if (updatedMessages) {
|
|
129
|
+
setMessages(updatedMessages);
|
|
130
|
+
}
|
|
131
|
+
else if (message) {
|
|
132
|
+
// If we have a standalone message in the final response (e.g. an error)
|
|
133
|
+
const assistantMessage = {
|
|
134
|
+
role: MessageRole.ASSISTANT,
|
|
135
|
+
message: message,
|
|
136
|
+
id: uuidv4(),
|
|
137
|
+
unuseful: false,
|
|
138
|
+
hiddenFromUser: false,
|
|
139
|
+
};
|
|
140
|
+
setMessages([...currentMessages, assistantMessage]);
|
|
141
|
+
}
|
|
142
|
+
// Ensure loading state is cleared (in case no streaming message came)
|
|
143
|
+
setIsChatLoading(false);
|
|
144
|
+
setCanSendMessage(true);
|
|
145
|
+
}
|
|
146
|
+
}, [
|
|
147
|
+
globalOnData,
|
|
148
|
+
refreshConversations,
|
|
149
|
+
chatbotId,
|
|
150
|
+
organizationId,
|
|
151
|
+
chatHistoryStorageKey,
|
|
152
|
+
]);
|
|
153
|
+
const sendMessage = useCallback(async (text, onData) => {
|
|
154
|
+
const messageToSend = text !== undefined ? text : input;
|
|
155
|
+
if (!messageToSend.trim() || !canSendMessage)
|
|
156
|
+
return;
|
|
157
|
+
const userMessage = {
|
|
158
|
+
role: MessageRole.USER,
|
|
159
|
+
message: messageToSend,
|
|
160
|
+
id: uuidv4(),
|
|
161
|
+
unuseful: false,
|
|
162
|
+
hiddenFromUser: false,
|
|
163
|
+
};
|
|
164
|
+
const newMessages = [...messages, userMessage];
|
|
165
|
+
setMessages(newMessages);
|
|
166
|
+
if (text === undefined)
|
|
167
|
+
setInput("");
|
|
168
|
+
setIsChatLoading(true);
|
|
169
|
+
setCanSendMessage(false);
|
|
170
|
+
setError(null);
|
|
171
|
+
const payload = {
|
|
172
|
+
messages: newMessages,
|
|
173
|
+
uiTools: parseUITools(UITools),
|
|
174
|
+
};
|
|
175
|
+
if (chatbotConversationRef) {
|
|
176
|
+
payload["chatbotConversationRef"] = chatbotConversationRef;
|
|
177
|
+
}
|
|
178
|
+
else if (initialMemory) {
|
|
179
|
+
payload["initialMemory"] = initialMemory;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
await postUserConversation({
|
|
183
|
+
payload,
|
|
184
|
+
apikey: apiKey,
|
|
185
|
+
chatbotId,
|
|
186
|
+
baseUrl,
|
|
187
|
+
onData: (data) => handleStreamData(data, newMessages, onData),
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
catch (err) {
|
|
191
|
+
console.error("Error sending message:", err);
|
|
192
|
+
setError(err.message || "Failed to send message");
|
|
193
|
+
setIsChatLoading(false);
|
|
194
|
+
setCanSendMessage(true);
|
|
195
|
+
}
|
|
196
|
+
}, [
|
|
197
|
+
input,
|
|
198
|
+
canSendMessage,
|
|
199
|
+
messages,
|
|
200
|
+
UITools,
|
|
201
|
+
chatbotConversationRef,
|
|
202
|
+
initialMemory,
|
|
203
|
+
chatbotId,
|
|
204
|
+
baseUrl,
|
|
205
|
+
apiKey,
|
|
206
|
+
handleStreamData,
|
|
207
|
+
]);
|
|
208
|
+
// Confirm a backend tool (sets confirmed: true, server will add output)
|
|
209
|
+
const confirmBackendTool = useCallback(async () => {
|
|
210
|
+
const lastMessage = messages[messages.length - 1];
|
|
211
|
+
if (!lastMessage || lastMessage.role !== MessageRole.FUNCTION) {
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
const updatedLastMessage = {
|
|
215
|
+
...lastMessage,
|
|
216
|
+
tool: {
|
|
217
|
+
...lastMessage.tool,
|
|
218
|
+
confirmed: true,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
const newMessages = [...messages.slice(0, -1), updatedLastMessage];
|
|
222
|
+
setMessages(newMessages);
|
|
223
|
+
// Keep loading state true while backend tool executes
|
|
224
|
+
setIsChatLoading(true);
|
|
225
|
+
setCanSendMessage(false);
|
|
226
|
+
const payload = {
|
|
227
|
+
messages: newMessages,
|
|
228
|
+
uiTools: parseUITools(UITools),
|
|
229
|
+
chatbotConversationRef,
|
|
230
|
+
};
|
|
231
|
+
try {
|
|
232
|
+
await postUserConversation({
|
|
233
|
+
payload,
|
|
234
|
+
apikey: apiKey,
|
|
235
|
+
chatbotId,
|
|
236
|
+
baseUrl,
|
|
237
|
+
onData: (data) => handleStreamData(data, newMessages),
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
catch (err) {
|
|
241
|
+
console.error("Error confirming backend tool:", err);
|
|
242
|
+
setError(err.message || "Failed to confirm backend tool");
|
|
243
|
+
setIsChatLoading(false);
|
|
244
|
+
setCanSendMessage(true);
|
|
245
|
+
}
|
|
246
|
+
}, [
|
|
247
|
+
messages,
|
|
248
|
+
UITools,
|
|
249
|
+
chatbotConversationRef,
|
|
250
|
+
apiKey,
|
|
251
|
+
chatbotId,
|
|
252
|
+
baseUrl,
|
|
253
|
+
handleStreamData,
|
|
254
|
+
]);
|
|
255
|
+
const submitToolResult = useCallback(async (toolOutput, onData) => {
|
|
256
|
+
const lastMessage = messages[messages.length - 1];
|
|
257
|
+
if (!lastMessage || lastMessage.role !== MessageRole.FUNCTION) {
|
|
258
|
+
console.error("Last message is not a function call");
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
const updatedLastMessage = {
|
|
262
|
+
...lastMessage,
|
|
263
|
+
tool: {
|
|
264
|
+
...lastMessage.tool,
|
|
265
|
+
output: toolOutput,
|
|
266
|
+
confirmed: true,
|
|
267
|
+
runAt: new Date().toISOString(),
|
|
268
|
+
},
|
|
269
|
+
};
|
|
270
|
+
const newMessages = [...messages.slice(0, -1), updatedLastMessage];
|
|
271
|
+
setMessages(newMessages);
|
|
272
|
+
setIsChatLoading(true);
|
|
273
|
+
setCanSendMessage(false);
|
|
274
|
+
const payload = {
|
|
275
|
+
messages: newMessages,
|
|
276
|
+
uiTools: parseUITools(UITools),
|
|
277
|
+
chatbotConversationRef,
|
|
278
|
+
};
|
|
279
|
+
try {
|
|
280
|
+
await postUserConversation({
|
|
281
|
+
payload,
|
|
282
|
+
apikey: apiKey,
|
|
283
|
+
chatbotId,
|
|
284
|
+
baseUrl,
|
|
285
|
+
onData: (data) => handleStreamData(data, newMessages, onData),
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
console.error("Error submitting tool result:", err);
|
|
290
|
+
setError(err.message || "Failed to submit tool result");
|
|
291
|
+
setIsChatLoading(false);
|
|
292
|
+
setCanSendMessage(true);
|
|
293
|
+
}
|
|
294
|
+
}, [
|
|
295
|
+
messages,
|
|
296
|
+
UITools,
|
|
297
|
+
chatbotConversationRef,
|
|
298
|
+
apiKey,
|
|
299
|
+
chatbotId,
|
|
300
|
+
baseUrl,
|
|
301
|
+
handleStreamData,
|
|
302
|
+
]);
|
|
303
|
+
// Handle tool call detection and auto-confirmation
|
|
304
|
+
useEffect(() => {
|
|
305
|
+
const lastMessage = messages[messages.length - 1];
|
|
306
|
+
if (lastMessage &&
|
|
307
|
+
lastMessage.role === MessageRole.FUNCTION &&
|
|
308
|
+
lastMessage.id !== lastProcessedToolCallId.current) {
|
|
309
|
+
const toolDetails = lastMessage.tool;
|
|
310
|
+
const toolDef = tools.find((t) => t.name === toolDetails.name);
|
|
311
|
+
// Notify consumer about the tool call
|
|
312
|
+
if (onToolCall)
|
|
313
|
+
onToolCall(toolDetails);
|
|
314
|
+
// Auto-confirm backend tools so the server can execute them
|
|
315
|
+
if (toolDef?.functionType === FunctionType.BACKEND &&
|
|
316
|
+
!toolDetails.confirmed) {
|
|
317
|
+
lastProcessedToolCallId.current = lastMessage.id;
|
|
318
|
+
confirmBackendTool();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
}, [messages, tools, onToolCall, confirmBackendTool]);
|
|
322
|
+
const handleNewChat = useCallback(() => {
|
|
323
|
+
setSelectedConversation(null);
|
|
324
|
+
setChatbotConversationRef(undefined);
|
|
325
|
+
setMessages([]);
|
|
326
|
+
setInput("");
|
|
327
|
+
const dateString = getUTCDateTime();
|
|
328
|
+
const newConversation = {
|
|
329
|
+
chatbotConversationRef: uuidv4(),
|
|
330
|
+
messages: [],
|
|
331
|
+
chatbotId: chatbotId,
|
|
332
|
+
chatbotConversationTitle: "New Conversation",
|
|
333
|
+
createdAt: dateString,
|
|
334
|
+
updatedAt: dateString,
|
|
335
|
+
totalCostSoFar: 0,
|
|
336
|
+
totalTokensSoFar: 0,
|
|
337
|
+
};
|
|
338
|
+
setSelectedConversation(newConversation);
|
|
339
|
+
}, [chatbotId]);
|
|
340
|
+
const handleConversationSelect = useCallback((conversation) => {
|
|
341
|
+
setSelectedConversation(conversation);
|
|
342
|
+
setChatbotConversationRef(conversation.chatbotConversationRef);
|
|
343
|
+
setMessages(conversation.messages.map((m) => ({
|
|
344
|
+
...m,
|
|
345
|
+
id: m.id || uuidv4(),
|
|
346
|
+
})));
|
|
347
|
+
}, []);
|
|
348
|
+
const handleDeleteConversation = useCallback(async (conversationId) => {
|
|
349
|
+
try {
|
|
350
|
+
const storedIds = JSON.parse(localStorage.getItem(localStorageKey) || "[]");
|
|
351
|
+
const updatedIds = storedIds.filter((id) => id !== conversationId);
|
|
352
|
+
localStorage.setItem(localStorageKey, JSON.stringify(updatedIds));
|
|
353
|
+
if (selectedConversation?.chatbotConversationRef === conversationId) {
|
|
354
|
+
handleNewChat();
|
|
355
|
+
}
|
|
356
|
+
refreshConversations();
|
|
357
|
+
}
|
|
358
|
+
catch (err) {
|
|
359
|
+
console.error("Error deleting conversation:", err);
|
|
360
|
+
}
|
|
361
|
+
}, [localStorageKey, selectedConversation, handleNewChat, refreshConversations]);
|
|
362
|
+
const visibleMessages = useMemo(() => {
|
|
363
|
+
return messages
|
|
364
|
+
.filter((msg) => !msg.hiddenFromUser && msg.role !== MessageRole.FUNCTION)
|
|
365
|
+
.map((msg) => {
|
|
366
|
+
if (cleanResponse &&
|
|
367
|
+
msg.role === MessageRole.ASSISTANT &&
|
|
368
|
+
msg.message) {
|
|
369
|
+
return {
|
|
370
|
+
...msg,
|
|
371
|
+
message: cleanMarkdown(msg.message),
|
|
372
|
+
};
|
|
373
|
+
}
|
|
374
|
+
return msg;
|
|
375
|
+
});
|
|
376
|
+
}, [messages, cleanResponse]);
|
|
377
|
+
return {
|
|
378
|
+
messages: visibleMessages,
|
|
379
|
+
setMessages,
|
|
380
|
+
input,
|
|
381
|
+
setInput,
|
|
382
|
+
isChatLoading,
|
|
383
|
+
canSendMessage,
|
|
384
|
+
sendMessage,
|
|
385
|
+
submitToolResult,
|
|
386
|
+
chatbotConversationRef,
|
|
387
|
+
conversations,
|
|
388
|
+
selectedConversation,
|
|
389
|
+
handleNewChat,
|
|
390
|
+
handleConversationSelect,
|
|
391
|
+
handleDeleteConversation,
|
|
392
|
+
refreshConversations,
|
|
393
|
+
tools,
|
|
394
|
+
error,
|
|
395
|
+
};
|
|
396
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
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";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
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";
|
|
@@ -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;
|
package/dist/type.d.ts
CHANGED
|
@@ -1,46 +1,58 @@
|
|
|
1
1
|
import { CSSProperties } from "react";
|
|
2
2
|
import { ConversationProps } from "./components/Conversation.js";
|
|
3
|
+
/**
|
|
4
|
+
* Context interface for the CMND Chat system.
|
|
5
|
+
* This is used primarily by the internal provider but can be accessed via useCMNDChatContext.
|
|
6
|
+
*/
|
|
3
7
|
export interface CmndChatContext {
|
|
8
|
+
/** Internal props passed to the conversation view. */
|
|
4
9
|
props: Partial<ConversationProps>;
|
|
5
|
-
/**
|
|
6
|
-
* Create a new conversation (resets state, starts a new chat thread)
|
|
7
|
-
*/
|
|
10
|
+
/** Create a new conversation (resets state, starts a new chat thread). */
|
|
8
11
|
createNewConversation: () => void;
|
|
9
|
-
/**
|
|
10
|
-
* Toggle the sidebar open/collapsed state
|
|
11
|
-
*/
|
|
12
|
+
/** Toggle the sidebar open/collapsed state. */
|
|
12
13
|
toggleSidebar: () => void;
|
|
13
|
-
/**
|
|
14
|
-
* Open the sidebar (set collapsed to false)
|
|
15
|
-
*/
|
|
14
|
+
/** Open the sidebar (set collapsed to false). */
|
|
16
15
|
openSidePanel: () => void;
|
|
17
|
-
/**
|
|
18
|
-
* Close the sidebar (set collapsed to true)
|
|
19
|
-
*/
|
|
16
|
+
/** Close the sidebar (set collapsed to true). */
|
|
20
17
|
closeSidePanel: () => void;
|
|
21
|
-
/**
|
|
22
|
-
* Set the message text in the input box
|
|
23
|
-
*/
|
|
18
|
+
/** Set the message text in the input box. */
|
|
24
19
|
setMessageText: (text: string) => void;
|
|
25
|
-
/**
|
|
26
|
-
* Send the current message, or set and send if text is provided
|
|
27
|
-
*/
|
|
20
|
+
/** Send the current message, or set and send if text is provided. */
|
|
28
21
|
sendMessage: (text?: string) => void;
|
|
29
22
|
}
|
|
23
|
+
/**
|
|
24
|
+
* Props for a custom input field component.
|
|
25
|
+
*/
|
|
30
26
|
export interface InputFieldProps {
|
|
27
|
+
/** The current input text. */
|
|
31
28
|
input: string;
|
|
29
|
+
/** Setter for the input text. */
|
|
32
30
|
setInput: (input: string) => void;
|
|
31
|
+
/** Whether the send button should be enabled. */
|
|
33
32
|
canSendMessage: boolean;
|
|
33
|
+
/** Callback to trigger the send action. */
|
|
34
34
|
handleSendClick: () => void;
|
|
35
|
-
|
|
35
|
+
/** Ref for the input element. */
|
|
36
|
+
inputRef?: React.RefObject<HTMLInputElement> | React.RefObject<HTMLTextAreaElement>;
|
|
36
37
|
}
|
|
38
|
+
/**
|
|
39
|
+
* Props for a custom send button component.
|
|
40
|
+
*/
|
|
37
41
|
export interface SendButtonProps {
|
|
42
|
+
/** Callback to trigger the send action. */
|
|
38
43
|
handleSendClick: () => void;
|
|
44
|
+
/** Whether the button should be enabled. */
|
|
39
45
|
canSendMessage: boolean;
|
|
40
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Utility type for wrapping API responses that might contain an error.
|
|
49
|
+
*/
|
|
41
50
|
export type ErrorOrData<T> = {
|
|
42
51
|
error: unknown;
|
|
43
52
|
} | T;
|
|
53
|
+
/**
|
|
54
|
+
* Represents a conversation object from the server.
|
|
55
|
+
*/
|
|
44
56
|
export interface Conversation {
|
|
45
57
|
conversationId: number;
|
|
46
58
|
conversationTitle: string;
|
|
@@ -50,35 +62,54 @@ export interface Conversation {
|
|
|
50
62
|
costLimit: number;
|
|
51
63
|
systemPrompt: string;
|
|
52
64
|
enabledTools: any[];
|
|
53
|
-
/** Date string format */
|
|
65
|
+
/** Date string format (UTC). */
|
|
54
66
|
createdAt: string;
|
|
55
|
-
/** Date string format */
|
|
67
|
+
/** Date string format (UTC). */
|
|
56
68
|
updatedAt: string;
|
|
57
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* Data object received via streaming or finalized responses.
|
|
72
|
+
*/
|
|
58
73
|
export interface ConversationDataObject {
|
|
74
|
+
/** Indicates if the AI has finished generating the response text. */
|
|
59
75
|
completionFinished: boolean;
|
|
76
|
+
/** Indicates if this is the final message containing usage metadata. */
|
|
60
77
|
finalResponseWithUsageData: boolean;
|
|
78
|
+
/** The message text (full or chunk). */
|
|
61
79
|
message?: string;
|
|
80
|
+
/** The conversation reference ID. */
|
|
62
81
|
chatbotConversationRef?: string;
|
|
82
|
+
/** Alias for chatbotConversationRef used in some stream responses. */
|
|
83
|
+
conversationRef?: string;
|
|
84
|
+
/** Total tokens used in the conversation so far. */
|
|
63
85
|
totalTokens?: number;
|
|
86
|
+
/** Total cost of the conversation so far. */
|
|
64
87
|
totalCost?: number;
|
|
88
|
+
/** Updated list of all messages in the conversation. */
|
|
65
89
|
messages?: Message[];
|
|
90
|
+
/** Indicates if an error occurred during streaming. */
|
|
91
|
+
hasError?: boolean;
|
|
92
|
+
/** Path associated with the error if available. */
|
|
93
|
+
errorPath?: string[] | string;
|
|
94
|
+
/** Indicates if a function call is currently being streamed. */
|
|
95
|
+
streamingFunctionCall?: boolean;
|
|
96
|
+
/** Name of the function being called. */
|
|
97
|
+
functionName?: string;
|
|
98
|
+
/** Result of a function execution. */
|
|
99
|
+
functionResult?: any;
|
|
66
100
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
default?: any;
|
|
71
|
-
}
|
|
72
|
-
interface ToolArguments {
|
|
73
|
-
type: string;
|
|
74
|
-
default?: any;
|
|
75
|
-
properties: Record<string, ToolArgument>;
|
|
76
|
-
required?: string[];
|
|
77
|
-
}
|
|
101
|
+
/**
|
|
102
|
+
* Defines the type of tool function.
|
|
103
|
+
*/
|
|
78
104
|
export declare enum FunctionType {
|
|
105
|
+
/** Tool that renders a React component in the chat. */
|
|
79
106
|
UI = "ui",
|
|
107
|
+
/** Tool that runs on the backend. */
|
|
80
108
|
BACKEND = "backend"
|
|
81
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Metadata for a tool.
|
|
112
|
+
*/
|
|
82
113
|
export type ToolData = {
|
|
83
114
|
name: string;
|
|
84
115
|
description: string;
|
|
@@ -87,62 +118,105 @@ export type ToolData = {
|
|
|
87
118
|
subsubcategory?: string;
|
|
88
119
|
functionType: FunctionType;
|
|
89
120
|
dangerous: boolean;
|
|
90
|
-
arguments:
|
|
91
|
-
};
|
|
92
|
-
export type UIFunctionArguments<AIProvidedParams> = {
|
|
93
|
-
functionArgs: AIProvidedParams;
|
|
94
|
-
previousRunResults?: string;
|
|
95
|
-
captureResults: (result: string) => void;
|
|
121
|
+
arguments: any;
|
|
96
122
|
};
|
|
123
|
+
/**
|
|
124
|
+
* Represents a tool that can be used within the chatbot.
|
|
125
|
+
*/
|
|
97
126
|
export interface CMNDChatbotUITool {
|
|
127
|
+
/** Unique name of the tool. */
|
|
98
128
|
name: string;
|
|
129
|
+
/** Description of what the tool does. */
|
|
99
130
|
description: string;
|
|
131
|
+
/** Category for organizational purposes. */
|
|
100
132
|
category: string;
|
|
133
|
+
/** Subcategory for finer organization. */
|
|
101
134
|
subcategory?: string;
|
|
135
|
+
/** Indicates if the tool should be treated as dangerous (e.g., requires confirmation). */
|
|
102
136
|
dangerous?: boolean;
|
|
137
|
+
/** Command aliases that might trigger this tool. */
|
|
103
138
|
associatedCommands?: string[];
|
|
139
|
+
/** Prerequisites required before this tool can run. */
|
|
104
140
|
prerequisites?: string[];
|
|
141
|
+
/** JSON Schema for the tool's arguments. */
|
|
105
142
|
argumentSchema: any;
|
|
143
|
+
/** Whether the tool can be re-run after finishing. */
|
|
106
144
|
rerun?: boolean;
|
|
145
|
+
/** Whether the tool can be re-run with different parameters. */
|
|
107
146
|
rerunWithDifferentParameters?: boolean;
|
|
147
|
+
/** Whether the tool requires further user input. */
|
|
108
148
|
capturesUserInput?: boolean;
|
|
149
|
+
/** Explicitly set to 'ui' for UI-based tools. */
|
|
109
150
|
functionType?: "ui";
|
|
151
|
+
/** The function implementation to run when the tool is called. */
|
|
110
152
|
runCmd: (args: {
|
|
111
153
|
functionArgs?: Record<string, any>;
|
|
112
154
|
captureResults?: (result: any) => Promise<any>;
|
|
113
155
|
previousRunResults?: any;
|
|
114
156
|
}, ref?: React.RefObject<HTMLElement>) => void;
|
|
115
157
|
}
|
|
158
|
+
/**
|
|
159
|
+
* Roles for different message types.
|
|
160
|
+
*/
|
|
116
161
|
export declare enum MessageRole {
|
|
162
|
+
/** A tool call or result. */
|
|
117
163
|
FUNCTION = "function",
|
|
164
|
+
/** A message from the user. */
|
|
118
165
|
USER = "user",
|
|
166
|
+
/** A message from the AI assistant. */
|
|
119
167
|
ASSISTANT = "assistant",
|
|
168
|
+
/** A system instruction. */
|
|
120
169
|
SYSTEM = "system"
|
|
121
170
|
}
|
|
122
171
|
type MessageRoleExceptFunctions = Exclude<MessageRole, MessageRole.FUNCTION>;
|
|
172
|
+
/**
|
|
173
|
+
* Base message structure.
|
|
174
|
+
*/
|
|
123
175
|
interface GenericMessage {
|
|
124
176
|
id: string;
|
|
177
|
+
/** Whether the message should be hidden or ignored in some contexts. */
|
|
125
178
|
unuseful: boolean;
|
|
179
|
+
/** Whether the message is visible to the end user. */
|
|
126
180
|
hiddenFromUser: boolean;
|
|
181
|
+
/** The text content of the message. */
|
|
127
182
|
message?: string;
|
|
183
|
+
/** The role of the sender. */
|
|
128
184
|
role: MessageRoleExceptFunctions;
|
|
129
185
|
}
|
|
186
|
+
/**
|
|
187
|
+
* Details of a tool call or its execution result.
|
|
188
|
+
*/
|
|
130
189
|
export interface ToolDetails {
|
|
190
|
+
/** Arguments passed by the AI. */
|
|
131
191
|
args: any;
|
|
192
|
+
/** Name of the tool called. */
|
|
132
193
|
name: string;
|
|
194
|
+
/** Whether the tool run has been confirmed. */
|
|
133
195
|
confirmed?: boolean;
|
|
196
|
+
/** Timestamp of when the tool was run. */
|
|
134
197
|
runAt?: string;
|
|
198
|
+
/** The result/output of the tool execution. */
|
|
135
199
|
output?: string;
|
|
136
200
|
}
|
|
201
|
+
/**
|
|
202
|
+
* A message representing a tool call.
|
|
203
|
+
*/
|
|
137
204
|
interface ToolMessage {
|
|
138
205
|
id: string;
|
|
139
206
|
unuseful: boolean;
|
|
140
207
|
hiddenFromUser: boolean;
|
|
141
208
|
message?: string;
|
|
142
209
|
role: MessageRole.FUNCTION;
|
|
210
|
+
/** Details about the tool call. */
|
|
143
211
|
tool: ToolDetails;
|
|
144
212
|
}
|
|
213
|
+
/**
|
|
214
|
+
* Union type for all possible message types in a conversation.
|
|
215
|
+
*/
|
|
145
216
|
export type Message = GenericMessage | ToolMessage;
|
|
217
|
+
/**
|
|
218
|
+
* CSS properties for customizing various parts of the chatbot UI.
|
|
219
|
+
*/
|
|
146
220
|
export interface CustomStyles {
|
|
147
221
|
chatAvatarStyle?: CSSProperties;
|
|
148
222
|
inputStyle?: CSSProperties;
|
|
@@ -163,9 +237,15 @@ export interface CustomStyles {
|
|
|
163
237
|
dateStyle?: CSSProperties;
|
|
164
238
|
deleteButtonStyle?: CSSProperties;
|
|
165
239
|
}
|
|
240
|
+
/**
|
|
241
|
+
* KV store for conversation memory/context.
|
|
242
|
+
*/
|
|
166
243
|
export interface CMNDChatMemory {
|
|
167
244
|
[key: string]: any;
|
|
168
245
|
}
|
|
246
|
+
/**
|
|
247
|
+
* Simple message structure used in the ChatbotConversation summary.
|
|
248
|
+
*/
|
|
169
249
|
export interface ChatbotConversationMessage {
|
|
170
250
|
id?: string;
|
|
171
251
|
role: "user" | "assistant";
|
|
@@ -173,16 +253,30 @@ export interface ChatbotConversationMessage {
|
|
|
173
253
|
unuseful: boolean;
|
|
174
254
|
hiddenFromUser: boolean;
|
|
175
255
|
}
|
|
256
|
+
/**
|
|
257
|
+
* Full conversation metadata and history.
|
|
258
|
+
*/
|
|
176
259
|
export interface ChatbotConversation {
|
|
260
|
+
/** Unique reference for the conversation. */
|
|
177
261
|
chatbotConversationRef: string;
|
|
262
|
+
/** Snapshot of message history. */
|
|
178
263
|
messages: ChatbotConversationMessage[];
|
|
264
|
+
/** ID of the parent chatbot. */
|
|
179
265
|
chatbotId: number;
|
|
266
|
+
/** Human-readable title for the conversation. */
|
|
180
267
|
chatbotConversationTitle: string;
|
|
268
|
+
/** Creation timestamp. */
|
|
181
269
|
createdAt: string;
|
|
270
|
+
/** Last update timestamp. */
|
|
182
271
|
updatedAt: string;
|
|
272
|
+
/** Cumulative cost of this thread. */
|
|
183
273
|
totalCostSoFar: number;
|
|
274
|
+
/** Cumulative token count for this thread. */
|
|
184
275
|
totalTokensSoFar: number;
|
|
185
276
|
}
|
|
277
|
+
/**
|
|
278
|
+
* Response structure when listing conversations.
|
|
279
|
+
*/
|
|
186
280
|
export interface ChatbotConversationsResponse {
|
|
187
281
|
chatbotConversations: ChatbotConversation[];
|
|
188
282
|
}
|
package/dist/type.js
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Defines the type of tool function.
|
|
3
|
+
*/
|
|
1
4
|
export var FunctionType;
|
|
2
5
|
(function (FunctionType) {
|
|
6
|
+
/** Tool that renders a React component in the chat. */
|
|
3
7
|
FunctionType["UI"] = "ui";
|
|
8
|
+
/** Tool that runs on the backend. */
|
|
4
9
|
FunctionType["BACKEND"] = "backend";
|
|
5
10
|
})(FunctionType = FunctionType || (FunctionType = {}));
|
|
11
|
+
/**
|
|
12
|
+
* Roles for different message types.
|
|
13
|
+
*/
|
|
6
14
|
export var MessageRole;
|
|
7
15
|
(function (MessageRole) {
|
|
16
|
+
/** A tool call or result. */
|
|
8
17
|
MessageRole["FUNCTION"] = "function";
|
|
18
|
+
/** A message from the user. */
|
|
9
19
|
MessageRole["USER"] = "user";
|
|
20
|
+
/** A message from the AI assistant. */
|
|
10
21
|
MessageRole["ASSISTANT"] = "assistant";
|
|
22
|
+
/** A system instruction. */
|
|
11
23
|
MessageRole["SYSTEM"] = "system";
|
|
12
24
|
})(MessageRole = MessageRole || (MessageRole = {}));
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleans markdown characters from a string to return plaintext.
|
|
3
|
+
* This is a simple regex-based cleaner that handles common markdown patterns.
|
|
4
|
+
*
|
|
5
|
+
* @param markdown The markdown string to clean.
|
|
6
|
+
* @returns The cleaned plaintext string.
|
|
7
|
+
*/
|
|
8
|
+
export declare const cleanMarkdown: (markdown: string) => string;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cleans markdown characters from a string to return plaintext.
|
|
3
|
+
* This is a simple regex-based cleaner that handles common markdown patterns.
|
|
4
|
+
*
|
|
5
|
+
* @param markdown The markdown string to clean.
|
|
6
|
+
* @returns The cleaned plaintext string.
|
|
7
|
+
*/
|
|
8
|
+
export const cleanMarkdown = (markdown) => {
|
|
9
|
+
if (!markdown)
|
|
10
|
+
return "";
|
|
11
|
+
return (markdown
|
|
12
|
+
// Handle bold/italic
|
|
13
|
+
.replace(/(\*\*|__)(.*?)\1/g, "$2")
|
|
14
|
+
.replace(/(\*|_)(.*?)\1/g, "$2")
|
|
15
|
+
// Handle links: [text](url) -> text
|
|
16
|
+
.replace(/\[(.*?)\]\(.*?\)/g, "$1")
|
|
17
|
+
// Handle code blocks
|
|
18
|
+
.replace(/```[\s\S]*?```/g, (match) => {
|
|
19
|
+
return match.replace(/```(\w+)?\n?|```/g, "").trim();
|
|
20
|
+
})
|
|
21
|
+
// Handle inline code
|
|
22
|
+
.replace(/`(.*?)`/g, "$1")
|
|
23
|
+
// Handle headers: # Header -> Header
|
|
24
|
+
.replace(/^#+\s+/gm, "")
|
|
25
|
+
// Handle blockquotes: > quote -> quote
|
|
26
|
+
.replace(/^>\s+/gm, "")
|
|
27
|
+
// Handle horizontal rules
|
|
28
|
+
.replace(/^-{3,}|^\*{3,}|^\_{3,}/gm, "")
|
|
29
|
+
// Handle images:  -> alt
|
|
30
|
+
.replace(/!\[(.*?)\]\(.*?\)/g, "$1"));
|
|
31
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cmnd-ai/chatbot-react",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.13.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"description": "",
|
|
6
6
|
"type": "module",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"access": "public"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"axios": "^1.
|
|
34
|
+
"axios": "^1.13.1",
|
|
35
35
|
"react-error-boundary": "^4.0.13",
|
|
36
36
|
"react-icons": "^5.3.0",
|
|
37
37
|
"react-markdown": "^8.0.6",
|
package/readme.dev.md
CHANGED
|
@@ -28,3 +28,52 @@
|
|
|
28
28
|
```
|
|
29
29
|
|
|
30
30
|
This will allow you to test your local changes in another project without publishing to npm.
|
|
31
|
+
|
|
32
|
+
## Core Hook Implementation (`useCMNDChat`)
|
|
33
|
+
|
|
34
|
+
The `useCMNDChat` hook is designed to be the logic engine of the chatbot. Key implementation details:
|
|
35
|
+
|
|
36
|
+
- **State Management**: Uses native React `useState` for messages, input, and loading states.
|
|
37
|
+
- **Real-time Streaming**: Utilizes `postUserConversation` with a stream reader to process response chunks.
|
|
38
|
+
- **Tool Orchestration**:
|
|
39
|
+
- **Backend Tools**: Automatically confirms tool call by setting `confirmed: true`. This triggers the server to execute the tool and return the actual data.
|
|
40
|
+
- **UI Tools**: Fires the `onToolCall` callback. The developer must execute the tool logic and call `submitToolResult(output)`.
|
|
41
|
+
- **Message Cleaning**: When `cleanResponse: true` is enabled, assistant messages are passed through a `cleanMarkdown` utility to return plaintext.
|
|
42
|
+
- **Persistence**: Automatically saves and loads conversation IDs from `localStorage` based on the `organizationId` and `chatbotId`.
|
|
43
|
+
|
|
44
|
+
### Tool Call Flow
|
|
45
|
+
|
|
46
|
+
1. AI generates a `function_call`.
|
|
47
|
+
2. Hook receives the call and adds a message with `role: "function"` to the state (internally).
|
|
48
|
+
3. **Internal Behavior:**
|
|
49
|
+
- **Backend Tools**: The hook automatically calls `confirmBackendTool()`. This sends a confirmed message back to the server.
|
|
50
|
+
- **UI Tools**: The hook notifies the developer via `onToolCall`.
|
|
51
|
+
4. **Execution:**
|
|
52
|
+
- **Backend Tools**: The server receives the confirmation, executes the tool, and returns the real data in the next stream update.
|
|
53
|
+
- **UI Tools**: The developer manually calls `submitToolResult(data)` with the result of the frontend execution.
|
|
54
|
+
5. The conversation resumes with the tool results included in the history.
|
|
55
|
+
|
|
56
|
+
### Backend Tool Data Flow
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
User: "Show my playlists"
|
|
60
|
+
↓
|
|
61
|
+
AI calls: get_playlists() (role: function)
|
|
62
|
+
↓
|
|
63
|
+
Hook auto-confirms: internal confirmBackendTool() (sets confirmed: true)
|
|
64
|
+
↓
|
|
65
|
+
Server executes and returns: { playlists: [...] } (Actual data)
|
|
66
|
+
↓
|
|
67
|
+
AI receives data and responds: "Here are your playlists: ..."
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
**Note**: `FUNCTION` role messages are filtered out from the `messages` array returned by the hook to keep the UI clean. Only `USER` and `ASSISTANT` messages are exposed.
|
|
71
|
+
|
|
72
|
+
## Building and Releasing
|
|
73
|
+
|
|
74
|
+
1. **Build the package:**
|
|
75
|
+
```sh
|
|
76
|
+
npm run build
|
|
77
|
+
```
|
|
78
|
+
2. **Release:**
|
|
79
|
+
This project uses `semantic-release` for automated versioning and npm publishing. Ensure your commit messages follow the [Conventional Commits](https://www.conventionalcommits.org/) specification.
|