@smarter.sh/ui-chat 0.3.0 → 0.7.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.
Files changed (58) hide show
  1. package/LICENSE +2 -2
  2. package/README.md +126 -173
  3. package/dist/smarter-chat-library.es.js +44286 -12088
  4. package/dist/smarter-chat-library.es.js.map +1 -1
  5. package/dist/smarter-chat-library.umd.js +340 -121
  6. package/dist/smarter-chat-library.umd.js.map +1 -1
  7. package/dist/types/src/components/AppTitle/Component.d.ts +13 -0
  8. package/dist/types/src/components/AppTitle/index.d.ts +2 -0
  9. package/dist/types/src/components/Console/Component.d.ts +17 -0
  10. package/dist/types/src/components/Console/ServerLogs.d.ts +8 -0
  11. package/dist/types/src/components/Console/data.d.ts +7 -0
  12. package/dist/types/src/components/Console/enums.d.ts +9 -0
  13. package/dist/types/src/components/Console/index.d.ts +2 -0
  14. package/dist/types/src/components/ErrorBoundary/Component.d.ts +17 -0
  15. package/dist/types/src/components/ErrorBoundary/index.d.ts +2 -0
  16. package/dist/types/src/components/SmarterChat/Component.d.ts +26 -0
  17. package/dist/types/src/components/SmarterChat/icons.d.ts +17 -0
  18. package/dist/types/src/components/SmarterChat/index.d.ts +2 -0
  19. package/dist/types/src/const.d.ts +5 -0
  20. package/dist/types/src/index.d.ts +12 -0
  21. package/dist/types/src/lib/ansi.d.ts +33 -0
  22. package/dist/types/src/lib/api.d.ts +73 -0
  23. package/dist/types/src/lib/code.d.ts +32 -0
  24. package/dist/types/src/lib/cookie.d.ts +17 -0
  25. package/dist/types/src/lib/enums.d.ts +26 -0
  26. package/dist/types/src/lib/layout.d.ts +42 -0
  27. package/dist/types/src/lib/logStream.d.ts +30 -0
  28. package/dist/types/src/lib/math.d.ts +18 -0
  29. package/dist/types/src/lib/mermaid.d.ts +27 -0
  30. package/dist/types/src/lib/messages.d.ts +52 -0
  31. package/dist/types/src/types.d.ts +143 -0
  32. package/dist/ui-chat.css +2 -1
  33. package/package.json +87 -59
  34. package/src/components/Console/Component.css +0 -47
  35. package/src/components/Console/Component.jsx +0 -186
  36. package/src/components/Console/HeadStyles.jsx +0 -20
  37. package/src/components/Console/enums.js +0 -7
  38. package/src/components/Console/index.js +0 -2
  39. package/src/components/ErrorModal/ErrorModal.css +0 -35
  40. package/src/components/ErrorModal/ErrorModal.jsx +0 -56
  41. package/src/components/ErrorModal/index.js +0 -3
  42. package/src/components/SmarterChat/Component.jsx +0 -431
  43. package/src/components/SmarterChat/ErrorBoundary.jsx +0 -32
  44. package/src/components/SmarterChat/Layout.js +0 -47
  45. package/src/components/SmarterChat/api.js +0 -200
  46. package/src/components/SmarterChat/enums.js +0 -17
  47. package/src/components/SmarterChat/index.js +0 -3
  48. package/src/components/SmarterChat/styles.css +0 -152
  49. package/src/components/SmarterChat/utils.jsx +0 -148
  50. package/src/components/enums.js +0 -1
  51. package/src/components/index.js +0 -3
  52. package/src/components/shared/cookie.js +0 -51
  53. package/src/constants.js +0 -2
  54. package/src/index.js +0 -4
  55. package/src/types/index.js +0 -2
  56. package/src/types/propTypes.js +0 -154
  57. package/src/version.js +0 -2
  58. package/types/index.d.ts +0 -17
@@ -1,431 +0,0 @@
1
- //---------------------------------------------------------------------------------
2
- // written by: Lawrence McDaniel
3
- // https://lawrencemcdaniel.com
4
- //
5
- // date: Mar-2023
6
- //---------------------------------------------------------------------------------
7
-
8
- // React stuff
9
- import React, { useRef, useState, useEffect } from "react";
10
- import PropTypes from "prop-types";
11
- import ConfigPropTypes from "../../types/propTypes.js";
12
-
13
- import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
14
- import { faCheckCircle, faTimesCircle, faRocket } from "@fortawesome/free-solid-svg-icons";
15
-
16
- // Chat UI stuff
17
- import "@chatscope/chat-ui-kit-styles/dist/default/styles.min.css";
18
- import {
19
- MainContainer,
20
- ChatContainer,
21
- MessageList,
22
- Message,
23
- MessageInput,
24
- TypingIndicator,
25
- ConversationHeader,
26
- InfoButton,
27
- AddUserButton,
28
- } from "@chatscope/chat-ui-kit-react";
29
-
30
- // this repo
31
- import { ErrorModal } from "../ErrorModal/ErrorModal.jsx";
32
- import { Console } from "../Console/index.js";
33
-
34
- // This component
35
- import "./styles.css";
36
- import { ContainerLayout, ContentLayout, WorkbenchLayout, ChatAppLayout, ConsoleLayout } from "./Layout.js";
37
- import { MessageDirectionEnum, SenderRoleEnum } from "./enums.js";
38
- import { fetchConfig, fetchPrompt } from "./api.js";
39
- import { setCookie, cookieMetaFactory } from "../shared/cookie.js";
40
- import { messageFactory, chatMessages2RequestMessages, chatInit } from "./utils.jsx";
41
- import { ErrorBoundary } from "./ErrorBoundary.jsx";
42
-
43
- // The main chat component. This is the top-level component that
44
- // is exported and used in the index.js file. It is responsible for
45
- // managing the chat message thread, sending messages to the backend
46
- // Api, and rendering the chat UI.
47
- function SmarterChat({
48
- apiUrl, // the URL of the Smarter chatbot API. example: https://smarter.3141-5926-5359.beta.api.smarter.sh/
49
- apiKey, // NOT USED. TO DELETE.
50
- toggleMetadata, // show/hide toggle button to show/hide the chat thread metadata
51
- csrfCookieName = "csrftoken", // the Django CSRF cookie.
52
- csrftoken, // the Django CSRF token. Passed from the Django template in the Smarter web console workbench.
53
- debugCookieName, // the Smarter chat debug cookie. Set here.
54
- debugCookieExpiration, // the Smarter chat debug cookie. Set here.
55
- debugMode = false, // if true then show the browser console log messages. Set in Smarter web console admin waffle switches: SmarterWaffleSwitches.SMARTER_WAFFLE_REACTAPP_DEBUG_MODE
56
- sessionCookieName = "session_key", // the Smarter chat session cookie. Set here, where the user creates a new chat session.
57
- sessionCookieExpiration, // the Smarter chat session cookie. Set here, where the user creates a new chat session.
58
- authSessionCookieName = "sessionid", // the Django session cookie. Set when the user logs in to the Smarter web console app.
59
- showConsole = true, // show the server console log component
60
- cookieDomain, // the domain of the cookie. This is added to the cookie meta data to restrict the domain that this component will read cookie data.
61
- }) {
62
- const [configApiUrl, setConfigApiUrl] = useState(apiUrl);
63
-
64
- const [showMetadata, setShowMetadata] = useState(toggleMetadata);
65
- const [isReady, setIsReady] = useState(false);
66
- const [config, setConfig] = useState({});
67
- const [placeholderText, setPlaceholderText] = useState("");
68
- const [assistantName, setAssistantName] = useState("");
69
- const [infoUrl, setInfoUrl] = useState("");
70
- const [fileAttachButton, setFileAttachButton] = useState(false);
71
-
72
- // header text
73
- const [title, setTitle] = useState("");
74
- const [info, setInfo] = useState("");
75
- const [isValid, setIsValid] = useState(true);
76
- const [isDeployed, setIsDeployed] = useState(false);
77
-
78
- const [debugModeState, setDebugMode] = useState(debugMode);
79
- const [messages, setMessages] = useState([]);
80
-
81
- // future use
82
- // const [backgroundImageUrl, setBackgroundImageUrl] = useState('');
83
- // const [sandboxMode, setSandboxMode] = useState(false);
84
-
85
- // component internal state
86
- const [isTyping, setIsTyping] = useState(false);
87
- const [isModalOpen, setIsModalOpen] = useState(false);
88
- const [modalMessage, setmodalMessage] = useState("");
89
- const [modalTitle, setmodalTitle] = useState("");
90
- const fileInputRef = useRef(null);
91
-
92
- // cookie management
93
- const csrfCookie = cookieMetaFactory(csrfCookieName, null, cookieDomain, csrftoken); // we read this but never set it.
94
- const authTokenCookie = cookieMetaFactory(authSessionCookieName, null, cookieDomain, null); // we read this but never set it.
95
- const sessionCookie = cookieMetaFactory(sessionCookieName, sessionCookieExpiration, cookieDomain, null);
96
- const debugCookie = cookieMetaFactory(debugCookieName, debugCookieExpiration, cookieDomain, null);
97
- const cookies = {
98
- authTokenCookie: authTokenCookie, // the Django session cookie. Set when the user logs in to the Smarter web console app.
99
- // typically this is not required for the chat app when running inside the
100
- // Smarter web console workbench, since it is already authenticated.
101
-
102
- csrfCookie: csrfCookie, // the Django CSRF cookie. This is required for requests to the Smarter web console workbench.
103
- sessionCookie: sessionCookie, // the Smarter chat session cookie. Set here, where the user creates a new chat session.
104
- debugCookie: debugCookie, // the Smarter chat debug cookie. Set here. Controls browser console logging.
105
- };
106
-
107
- const refetchConfig = async () => {
108
- const newConfig = await fetchConfig(configApiUrl, cookies);
109
- setDebugMode(newConfig?.debug_mode);
110
- setCookie(cookies.debugCookie, debugModeState);
111
-
112
- if (debugModeState) {
113
- console.log("refetchConfig() config:", newConfig);
114
- }
115
-
116
- PropTypes.checkPropTypes(ConfigPropTypes, newConfig, "prop", "SmarterChat");
117
- setConfig(newConfig);
118
- return newConfig;
119
- };
120
-
121
- const fetchAndSetConfig = async () => {
122
- try {
123
- const newConfig = await refetchConfig();
124
-
125
- if (debugModeState) {
126
- console.log("fetchAndSetConfig() config:", newConfig);
127
- }
128
-
129
- setPlaceholderText(newConfig.chatbot.app_placeholder);
130
- setConfigApiUrl(newConfig.chatbot.url_chatbot);
131
- setAssistantName(newConfig.chatbot.app_assistant);
132
- setInfoUrl(newConfig.chatbot.app_info_url);
133
- setFileAttachButton(newConfig.chatbot.app_file_attachment);
134
- setIsValid(newConfig.meta_data.is_valid);
135
- setIsDeployed(newConfig.meta_data.is_deployed);
136
- setDebugMode(newConfig.debug_mode);
137
-
138
- // wrap up the rest of the initialization
139
- const newHistory = newConfig.history?.chat_history || [];
140
- const newThread = chatInit(
141
- newConfig.chatbot.app_welcome_message,
142
- newConfig.chatbot.default_system_role,
143
- newConfig.chatbot.app_example_prompts,
144
- newConfig.session_key,
145
- newHistory,
146
- "BACKEND_CHAT_MOST_RECENT_RESPONSE",
147
- );
148
- setMessages(newThread);
149
-
150
- const newTitle = `${newConfig.chatbot.app_name} v${newConfig.chatbot.version || "1.0.0"}`;
151
- setTitle(newTitle);
152
- let newInfo = `${newConfig.chatbot.provider} ${newConfig.chatbot.default_model}`;
153
- if (newConfig.plugins.meta_data.total_plugins > 0) {
154
- newInfo += ` with ${newConfig.plugins.meta_data.total_plugins} additional plugins`;
155
- }
156
- setInfo(newInfo);
157
- setIsReady(true);
158
- setIsTyping(false);
159
-
160
- if (debugModeState) {
161
- console.log("fetchAndSetConfig() done!");
162
- }
163
- } catch (error) {
164
- console.error("Failed to fetch config:", error);
165
- }
166
- };
167
-
168
- // Lifecycle hooks
169
- useEffect(() => {
170
- if (debugModeState) {
171
- console.log("ChatApp() component mounted");
172
- }
173
-
174
- fetchAndSetConfig();
175
-
176
- return () => {
177
- if (debugModeState) {
178
- console.log("ChatApp() component unmounted");
179
- }
180
- };
181
- }, []);
182
-
183
- // Error modal state management
184
- function openErrorModal(title, msg) {
185
- setIsModalOpen(true);
186
- setmodalTitle(title);
187
- setmodalMessage(msg);
188
- }
189
-
190
- function closeChatModal() {
191
- setIsModalOpen(false);
192
- }
193
-
194
- const handleInfoButtonClick = () => {
195
- const newValue = !showMetadata;
196
- setShowMetadata(newValue);
197
- if (debugModeState) {
198
- console.log("showMetadata:", newValue);
199
- }
200
- const newMessages = messages.map((message) => {
201
- if (message.message === null) {
202
- return { ...message, display: false };
203
- }
204
- if (["smarter", "system", "tool"].includes(message.sender)) {
205
- // toggle backend messages
206
- return { ...message, display: newValue };
207
- } else {
208
- // always show user and assistant messages
209
- return { ...message, display: true };
210
- }
211
- });
212
- setMessages(newMessages);
213
- };
214
-
215
- const handleAddUserButtonClick = () => {
216
- setCookie(cookies.sessionCookie, "");
217
- fetchAndSetConfig();
218
- };
219
-
220
- async function handleApiRequest(input_text, base64_encode = false) {
221
- // Api request handler. This function is indirectly called by UI event handlers
222
- // inside this module. It asynchronously sends the user's input to the
223
- // backend Api using the fetch() function. The response from the Api is
224
- // then used to update the chat message thread and the UI via React state.
225
- const newMessage = messageFactory({}, input_text, MessageDirectionEnum.OUTGOING, SenderRoleEnum.USER);
226
- if (base64_encode) {
227
- console.error("base64 encoding not implemented yet.");
228
- }
229
-
230
- setMessages((prevMessages) => {
231
- const updatedMessages = [...prevMessages, newMessage];
232
- setIsTyping(true);
233
-
234
- (async () => {
235
- try {
236
- if (debugModeState) {
237
- console.log("handleApiRequest() messages:", updatedMessages);
238
- }
239
- const msgs = chatMessages2RequestMessages(updatedMessages);
240
- const response = await fetchPrompt(config, msgs, cookies);
241
-
242
- if (response) {
243
- const responseMessages = response.smarter.messages
244
- .filter((message) => message.content !== null)
245
- .map((message) => {
246
- return messageFactory(message, message.content, MessageDirectionEnum.INCOMING, message.role);
247
- });
248
- setMessages((prevMessages) => [...prevMessages, ...responseMessages]);
249
- setIsTyping(false);
250
- refetchConfig();
251
- }
252
- } catch (error) {
253
- setIsTyping(false);
254
- console.error("Api error: ", error);
255
- openErrorModal("Api error", error.message);
256
- }
257
- })();
258
-
259
- return updatedMessages;
260
- });
261
- }
262
-
263
- // file upload event handlers
264
- const handleAttachClick = async () => {
265
- fileInputRef.current.click();
266
- };
267
- function handleFileChange(event) {
268
- const file = event.target.files[0];
269
- const reader = new FileReader();
270
-
271
- reader.onload = (event) => {
272
- const fileContent = event.target.result;
273
- handleApiRequest(fileContent, true);
274
- };
275
- reader.readAsText(file);
276
- }
277
-
278
- // send button event handler
279
- const handleSend = (input_text) => {
280
- // remove any HTML tags from the input_text. Pasting text into the
281
- // input box (from any source) tends to result in HTML span tags being included
282
- // in the input_text. This is a problem because the Api doesn't know how to
283
- // handle HTML tags. So we remove them here.
284
- const sanitized_input_text = input_text.replace(/<[^>]+>/g, "");
285
-
286
- // check if the sanitized input text is empty or only contains whitespace
287
- if (!sanitized_input_text.trim()) {
288
- return;
289
- }
290
- handleApiRequest(sanitized_input_text, false);
291
- };
292
-
293
- // Creates a fancier title for the chat app which includes
294
- // fontawesome icons for validation and deployment status.
295
- function AppTitle({ title, isValid, isDeployed }) {
296
- return (
297
- <div>
298
- {isReady ? (
299
- <>
300
- {title}&nbsp;
301
- {isValid ? (
302
- <FontAwesomeIcon icon={faCheckCircle} style={{ color: "green" }} />
303
- ) : (
304
- <FontAwesomeIcon icon={faTimesCircle} style={{ color: "red" }} />
305
- )}
306
- {isDeployed ? (
307
- <span>
308
- &nbsp;
309
- <FontAwesomeIcon icon={faRocket} style={{ color: "orange" }} />
310
- </span>
311
- ) : (
312
- <span style={{ color: "brown" }}>&nbsp;(sandbox)</span>
313
- )}
314
- </>
315
- ) : (
316
- <div>Loading...</div>
317
- )}
318
- </div>
319
- );
320
- }
321
-
322
- function SmarterMessage({ i, message }) {
323
- let messageClassNames = "";
324
- if (message.sender === "smarter") {
325
- messageClassNames = "smarter-message";
326
- } else if (message.sender === "smarter_error") {
327
- messageClassNames = "smarter-error-message";
328
- } else if (["tool", "system"].includes(message.sender)) {
329
- messageClassNames = "system-message";
330
- }
331
- return <Message key={i} model={message} className={messageClassNames} />;
332
- }
333
-
334
- // UI widget styles
335
- // note that most styling is intended to be created in Component.css
336
- // these are outlying cases where inline styles are required in order to override the default styles
337
- const fullWidthStyle = {
338
- width: "100%",
339
- };
340
- const transparentBackgroundStyle = {
341
- backgroundColor: "rgba(0,0,0,0.10)",
342
- color: "lightgray",
343
- };
344
- const mainContainerStyleOverrides = {
345
- width: "100%",
346
- height: "100%",
347
- };
348
- const chatContainerStyleOverrides = {
349
- ...fullWidthStyle,
350
- ...transparentBackgroundStyle,
351
- };
352
-
353
- // render the chat app
354
- return (
355
- <div id="smarter_chat_component_container" className="SmarterChat">
356
- <ContainerLayout>
357
- <ContentLayout>
358
- <WorkbenchLayout>
359
- <ChatAppLayout>
360
- <div className="chat-app">
361
- <MainContainer style={mainContainerStyleOverrides}>
362
- <ErrorBoundary>
363
- <ErrorModal
364
- isModalOpen={isModalOpen}
365
- title={modalTitle}
366
- message={modalMessage}
367
- onCloseClick={closeChatModal}
368
- />
369
- </ErrorBoundary>
370
- <ChatContainer style={chatContainerStyleOverrides}>
371
- <ConversationHeader>
372
- <ConversationHeader.Content
373
- userName={
374
- isReady ? (
375
- <AppTitle title={title} isValid={isValid} isDeployed={isDeployed} />
376
- ) : (
377
- "Configuring workbench..."
378
- )
379
- }
380
- info={isReady ? info : ""}
381
- />
382
- <ConversationHeader.Actions>
383
- <AddUserButton onClick={handleAddUserButtonClick} title="Start a new chat" />
384
- {toggleMetadata && (
385
- <InfoButton onClick={handleInfoButtonClick} title="Toggle system meta data" />
386
- )}
387
- </ConversationHeader.Actions>
388
- </ConversationHeader>
389
- <MessageList
390
- style={transparentBackgroundStyle}
391
- scrollBehavior="auto"
392
- typingIndicator={isTyping ? <TypingIndicator content={assistantName + " is typing"} /> : null}
393
- >
394
- {messages
395
- .filter((message) => message.display)
396
- .map((message, i) => {
397
- return <SmarterMessage i={i} message={message} />;
398
- })}
399
- </MessageList>
400
- <MessageInput
401
- placeholder={placeholderText}
402
- onSend={handleSend}
403
- onAttachClick={handleAttachClick}
404
- attachButton={fileAttachButton}
405
- fancyScroll={false}
406
- />
407
- </ChatContainer>
408
- <input
409
- type="file"
410
- accept=".py"
411
- title="Select a Python file"
412
- ref={fileInputRef}
413
- style={{ display: "none" }}
414
- onChange={handleFileChange}
415
- />
416
- </MainContainer>
417
- </div>
418
- </ChatAppLayout>
419
- {showConsole && (
420
- <ConsoleLayout>
421
- <Console config={config} />
422
- </ConsoleLayout>
423
- )}
424
- </WorkbenchLayout>
425
- </ContentLayout>
426
- </ContainerLayout>
427
- </div>
428
- );
429
- }
430
-
431
- export default SmarterChat;
@@ -1,32 +0,0 @@
1
- import React from "react";
2
- import PropTypes from "prop-types";
3
-
4
- export class ErrorBoundary extends React.Component {
5
- constructor(props) {
6
- super(props);
7
- this.state = { hasError: false };
8
- }
9
-
10
- static getDerivedStateFromError() {
11
- // Update state so the next render will show the fallback UI.
12
- return { hasError: true };
13
- }
14
-
15
- componentDidCatch(error, errorInfo) {
16
- // You can also log the error to an error reporting service
17
- console.error(error, errorInfo);
18
- }
19
-
20
- render() {
21
- if (this.state.hasError) {
22
- // You can render any custom fallback UI
23
- return <h1>Something went wrong.</h1>;
24
- }
25
-
26
- return this.props.children;
27
- }
28
- }
29
-
30
- ErrorBoundary.propTypes = {
31
- children: PropTypes.node,
32
- };
@@ -1,47 +0,0 @@
1
- // see: https://www.npmjs.com/package/styled-components
2
- import styled from "styled-components";
3
-
4
-
5
- const BaseLayout = styled.div`
6
- margin: 0;
7
- padding: 5px;
8
- height: 100%;
9
- box-sizing: border-box;
10
- `;
11
-
12
- export const ContainerLayout = styled.div`
13
- height: 100%;
14
- display: flex;
15
- flex-direction: row;
16
- `;
17
-
18
- export const ContentLayout = styled.div`
19
- flex: 1;
20
- display: flex;
21
- flex-direction: row;
22
- margin: 0;
23
- padding: 0;
24
- height: 100%;
25
- `;
26
-
27
- export const WorkbenchLayout = styled(BaseLayout)`
28
- flex-basis: 100%;
29
- `;
30
-
31
- export const ChatAppLayout = styled(BaseLayout)`
32
- flex-basis: 33.33%;
33
- min-width: 400px;
34
- @media (max-width: 992px) {
35
- flex-basis: 100%;
36
- }
37
- `;
38
-
39
- export const ConsoleLayout = styled(BaseLayout)`
40
- flex-basis: 66.67%;
41
- min-width: 600px;
42
- // visible on only on large screens
43
- @media (max-width: 992px) {
44
- display: none;
45
- flex-basis: 0%;
46
- }
47
- `;