@cognizant-ai-lab/ui-common 1.5.0 → 1.6.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 (81) hide show
  1. package/dist/Theme/Theme.js +3 -3
  2. package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +11 -1
  3. package/dist/components/AgentChat/ChatCommon/ChatCommon.js +284 -285
  4. package/dist/components/AgentChat/ChatCommon/ChatHistory.d.ts +1 -7
  5. package/dist/components/AgentChat/ChatCommon/ChatHistory.js +33 -22
  6. package/dist/components/AgentChat/ChatCommon/ControlButtons.js +2 -2
  7. package/dist/components/AgentChat/ChatCommon/Conversation.d.ts +13 -0
  8. package/dist/components/AgentChat/ChatCommon/Conversation.js +80 -0
  9. package/dist/components/AgentChat/ChatCommon/ConversationTurn.d.ts +23 -0
  10. package/dist/components/AgentChat/ChatCommon/ConversationTurn.js +11 -0
  11. package/dist/components/AgentChat/ChatCommon/FormattedMarkdown.js +5 -3
  12. package/dist/components/AgentChat/ChatCommon/SampleQueries.d.ts +3 -0
  13. package/dist/components/AgentChat/ChatCommon/SampleQueries.js +6 -3
  14. package/dist/components/AgentChat/ChatCommon/Thinking.d.ts +12 -0
  15. package/dist/components/AgentChat/ChatCommon/Thinking.js +51 -0
  16. package/dist/components/AgentChat/Common/LlmChatButton.d.ts +2 -2
  17. package/dist/components/AgentChat/Common/Types.d.ts +6 -5
  18. package/dist/components/AgentChat/Common/Types.js +5 -0
  19. package/dist/components/AgentChat/Common/Utils.d.ts +1 -1
  20. package/dist/components/AgentChat/Common/Utils.js +14 -9
  21. package/dist/components/Common/AccordionLite.d.ts +14 -0
  22. package/dist/components/Common/AccordionLite.js +25 -0
  23. package/dist/components/Common/ConfirmationModal.d.ts +1 -0
  24. package/dist/components/Common/ConfirmationModal.js +1 -1
  25. package/dist/components/Common/CustomerLogo.js +1 -1
  26. package/dist/components/Common/MUIAlert.d.ts +1 -0
  27. package/dist/components/Common/MUIAlert.js +3 -4
  28. package/dist/components/Common/Navbar.d.ts +2 -1
  29. package/dist/components/Common/Navbar.js +8 -4
  30. package/dist/components/Common/notification.d.ts +1 -1
  31. package/dist/components/Common/notification.js +17 -12
  32. package/dist/components/MultiAgentAccelerator/AgentFlow.d.ts +8 -0
  33. package/dist/components/MultiAgentAccelerator/AgentFlow.js +282 -82
  34. package/dist/components/MultiAgentAccelerator/AgentNode.d.ts +3 -1
  35. package/dist/components/MultiAgentAccelerator/AgentNode.js +64 -28
  36. package/dist/components/MultiAgentAccelerator/AgentNodePopup.d.ts +1 -4
  37. package/dist/components/MultiAgentAccelerator/AgentNodePopup.js +4 -5
  38. package/dist/components/MultiAgentAccelerator/GraphLayouts.js +19 -9
  39. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +2 -2
  40. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +268 -60
  41. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +1 -0
  42. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +28 -12
  43. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +1 -0
  44. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +21 -5
  45. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +4 -3
  46. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +8 -2
  47. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +19 -2
  48. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +40 -5
  49. package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.js +27 -14
  50. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.d.ts +7 -0
  51. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +88 -0
  52. package/dist/components/MultiAgentAccelerator/const.d.ts +7 -10
  53. package/dist/components/MultiAgentAccelerator/const.js +9 -10
  54. package/dist/const.d.ts +5 -1
  55. package/dist/const.js +5 -2
  56. package/dist/controller/agent/Agent.d.ts +10 -0
  57. package/dist/controller/agent/Agent.js +17 -1
  58. package/dist/controller/llm/LlmChat.js +2 -2
  59. package/dist/index.d.ts +0 -1
  60. package/dist/index.js +0 -1
  61. package/dist/state/TemporaryNetworks.d.ts +5 -15
  62. package/dist/state/TemporaryNetworks.js +15 -34
  63. package/dist/state/Tour.d.ts +29 -0
  64. package/dist/state/Tour.js +22 -0
  65. package/dist/state/UserInfo.d.ts +2 -1
  66. package/dist/tsconfig.build.tsbuildinfo +1 -1
  67. package/dist/utils/Authentication.js +12 -3
  68. package/dist/utils/File.d.ts +7 -0
  69. package/dist/utils/File.js +14 -3
  70. package/dist/utils/text.js +2 -2
  71. package/dist/utils/title.js +1 -1
  72. package/dist/utils/zIndexLayers.js +3 -0
  73. package/package.json +15 -11
  74. package/dist/components/AgentChat/ChatCommon/AgentConnectivity.d.ts +0 -14
  75. package/dist/components/AgentChat/ChatCommon/AgentConnectivity.js +0 -23
  76. package/dist/components/AgentChat/ChatCommon/Greetings.d.ts +0 -1
  77. package/dist/components/AgentChat/ChatCommon/Greetings.js +0 -38
  78. package/dist/components/AgentChat/ChatCommon/UserQueryDisplay.d.ts +0 -7
  79. package/dist/components/AgentChat/ChatCommon/UserQueryDisplay.js +0 -32
  80. package/dist/components/Common/LlmChatOptionsButton.d.ts +0 -6
  81. package/dist/components/Common/LlmChatOptionsButton.js +0 -31
@@ -1,14 +1,8 @@
1
1
  import { BaseMessage } from "@langchain/core/messages";
2
2
  import { FC } from "react";
3
3
  interface ChatHistoryProps {
4
- readonly agentDisplayName: string;
5
- readonly agentImage: string;
6
- readonly chatHistoryKey: string;
7
- readonly currentUser: string;
8
- readonly id: string;
9
4
  readonly messages: BaseMessage[];
10
- readonly targetAgent: string;
11
- readonly userImage: string;
5
+ readonly id: string;
12
6
  }
13
7
  /**
14
8
  * Component for displaying chat history from previous interactions with the agent.
@@ -1,27 +1,38 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import { UserQueryDisplay } from "./UserQueryDisplay.js";
3
- import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
4
- import { hashString } from "../../../utils/text.js";
5
- import { MUIAccordion } from "../../Common/MUIAccordion.js";
2
+ import { useTheme } from "@mui/material/styles";
3
+ import { Conversation } from "./Conversation.js";
4
+ import { MessageRole } from "./ConversationTurn.js";
5
+ import { adjustBrightness } from "../../../Theme/Theme.js";
6
+ import { AccordionLite } from "../../Common/AccordionLite.js";
6
7
  // #endregion: Types
8
+ /**
9
+ * Helper function to convert from BaseMessage format used in persisted chat history to ConversationTurn format used for
10
+ * rendering the conversation.
11
+ * @param chatHistory
12
+ */
13
+ const toTurns = (chatHistory) => chatHistory
14
+ .filter((message) => message.type === "human" || message.type === "ai")
15
+ .map((message) => {
16
+ const role = message.type === "human" ? MessageRole.User : MessageRole.Agent;
17
+ return {
18
+ id: message.id,
19
+ text: message.content.toString(),
20
+ role,
21
+ };
22
+ });
7
23
  /**
8
24
  * Component for displaying chat history from previous interactions with the agent.
9
25
  */
10
- export const ChatHistory = ({ agentDisplayName, agentImage, chatHistoryKey, currentUser, id, messages, targetAgent, userImage, }) => (_jsx(MUIAccordion, { id: id, sx: { marginBottom: "2rem", marginTop: "1rem" }, items: [
11
- {
12
- title: "Chat History",
13
- content: messages.map((message) => {
14
- const itemKey = hashString(message.text + message.type + message.id);
15
- if (message.type.toUpperCase() === ChatMessageType.HUMAN) {
16
- return (_jsx(UserQueryDisplay, { sx: { opacity: 0.5 }, title: currentUser, userImage: userImage, userQuery: message.text }, itemKey));
17
- }
18
- else if (message.type.toUpperCase() === ChatMessageType.AI) {
19
- return (_jsx(UserQueryDisplay, { sx: { opacity: 0.5 }, title: targetAgent, userImage: agentImage, userQuery: `${agentDisplayName}: ${message.text}` }, itemKey));
20
- }
21
- else {
22
- console.warn(`Unrecognized message type in chat history: ${message.type}`);
23
- return null;
24
- }
25
- }),
26
- },
27
- ] }, chatHistoryKey));
26
+ export const ChatHistory = ({ id, messages }) => {
27
+ const theme = useTheme();
28
+ const turns = toTurns(messages);
29
+ const conversation = (_jsx(Conversation, { id: `${id}-conversation`,
30
+ // Pass "true" here to include final answers, which get persisted as agent
31
+ // messages in chat history.
32
+ includeAgentMessages: true, shouldWrapOutput: true, turns: turns }, `${id}-conversation`));
33
+ return (_jsx(AccordionLite, { id: `${id}-history-items`, items: conversation, contentSx: {
34
+ // Slightly darker background to differentiate from main chat
35
+ backgroundColor: adjustBrightness(theme.palette.background.paper, -5),
36
+ opacity: 0.5,
37
+ }, title: "Chat History" }));
38
+ };
@@ -14,7 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  */
17
- import DeleteOutline from "@mui/icons-material/DeleteOutline";
17
+ import DeleteOutlined from "@mui/icons-material/DeleteOutlined";
18
18
  import Loop from "@mui/icons-material/Loop";
19
19
  import StopCircle from "@mui/icons-material/StopCircle";
20
20
  import { SmallLlmChatButton } from "../Common/LlmChatButton.js";
@@ -23,4 +23,4 @@ import { SmallLlmChatButton } from "../Common/LlmChatButton.js";
23
23
  * Generate the Control Buttons for a chat window.
24
24
  * @returns A fragment containing the Control Buttons.
25
25
  */
26
- export const ControlButtons = ({ handleClearChat, enableClearChatButton, isAwaitingLlm, handleSend, handleStop, previousUserQuery, shouldEnableRegenerateButton, }) => (_jsxs(_Fragment, { children: [!isAwaitingLlm && (_jsx(SmallLlmChatButton, { "aria-label": "Clear Chat", disabled: !enableClearChatButton, id: "clear-chat-button", onClick: handleClearChat, posBottom: 8, posRight: 65, children: _jsx(DeleteOutline, { fontSize: "small", id: "stop-button-icon", sx: { color: "var(--bs-white)" } }) })), isAwaitingLlm && (_jsx(SmallLlmChatButton, { "aria-label": "Stop", disabled: !isAwaitingLlm, id: "stop-output-button", onClick: () => handleStop(), posBottom: 8, posRight: 23, children: _jsx(StopCircle, { fontSize: "small", id: "stop-button-icon", sx: { color: "var(--bs-white)" } }) })), !isAwaitingLlm && (_jsx(SmallLlmChatButton, { "aria-label": "Regenerate", disabled: !shouldEnableRegenerateButton, id: "regenerate-output-button", onClick: () => handleSend(previousUserQuery), posBottom: 8, posRight: 23, children: _jsx(Loop, { fontSize: "small", id: "generate-icon", sx: { color: "var(--bs-white)" } }) }))] }));
26
+ export const ControlButtons = ({ handleClearChat, enableClearChatButton, isAwaitingLlm, handleSend, handleStop, previousUserQuery, shouldEnableRegenerateButton, }) => (_jsxs(_Fragment, { children: [!isAwaitingLlm && (_jsx(SmallLlmChatButton, { "aria-label": "Clear Chat", disabled: !enableClearChatButton, id: "clear-chat-button", onClick: handleClearChat, posBottom: 8, posRight: 65, children: _jsx(DeleteOutlined, { fontSize: "small", id: "stop-button-icon", sx: { color: "var(--bs-white)" } }) })), isAwaitingLlm && (_jsx(SmallLlmChatButton, { "aria-label": "Stop", disabled: !isAwaitingLlm, id: "stop-output-button", onClick: () => handleStop(), posBottom: 8, posRight: 23, children: _jsx(StopCircle, { fontSize: "small", id: "stop-button-icon", sx: { color: "var(--bs-white)" } }) })), !isAwaitingLlm && (_jsx(SmallLlmChatButton, { "aria-label": "Regenerate", disabled: !shouldEnableRegenerateButton, id: "regenerate-output-button", onClick: () => handleSend(previousUserQuery), posBottom: 8, posRight: 23, children: _jsx(Loop, { fontSize: "small", id: "generate-icon", sx: { color: "var(--bs-white)" } }) }))] }));
@@ -0,0 +1,13 @@
1
+ import { FC } from "react";
2
+ import { ConversationTurn } from "./ConversationTurn.js";
3
+ interface ConversationProps {
4
+ readonly id: string;
5
+ readonly includeAgentMessages?: boolean;
6
+ readonly shouldWrapOutput: boolean;
7
+ readonly turns: ConversationTurn[];
8
+ }
9
+ /**
10
+ * Component to render a conversation between the user and the agent.
11
+ */
12
+ export declare const Conversation: FC<ConversationProps>;
13
+ export {};
@@ -0,0 +1,80 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Box from "@mui/material/Box";
3
+ import { styled, useTheme } from "@mui/material/styles";
4
+ import { useMemo } from "react";
5
+ import { MessageRole } from "./ConversationTurn.js";
6
+ import { FormattedMarkdown } from "./FormattedMarkdown.js";
7
+ import { HLJS_THEMES } from "./SyntaxHighlighterThemes.js";
8
+ import { MUIAlert } from "../../Common/MUIAlert.js";
9
+ const { atelierDuneDark, a11yLight } = HLJS_THEMES;
10
+ /**
11
+ * Styled component for a user turn bubble.
12
+ */
13
+ const UserTurnBubble = styled(Box)(({ theme }) => ({
14
+ backgroundColor: theme.palette.background.paper,
15
+ borderRadius: "1rem",
16
+ marginBottom: "1rem",
17
+ marginLeft: "auto",
18
+ overflowWrap: "anywhere",
19
+ paddingLeft: "1rem",
20
+ paddingRight: "1rem",
21
+ paddingTop: "0.5rem",
22
+ paddingBottom: "0.5rem",
23
+ whiteSpace: "pre-wrap",
24
+ width: "60%",
25
+ }));
26
+ /**
27
+ * Format the content of a conversation turn for display. Pretty-print JSON.
28
+ * @param turn The conversation turn to format.
29
+ * @returns The formatted content of the turn with JSON fences as appropriate.
30
+ */
31
+ const formatTurnContent = (turn) => {
32
+ if (turn.text) {
33
+ return turn.text;
34
+ }
35
+ if (turn.structure != null) {
36
+ return `\`\`\`json\n${JSON.stringify(turn.structure, null, 2)}\n\`\`\``;
37
+ }
38
+ else {
39
+ return "";
40
+ }
41
+ };
42
+ /**
43
+ * Component to render a conversation between the user and the agent.
44
+ */
45
+ export const Conversation = ({ id, includeAgentMessages = false, shouldWrapOutput, turns }) => {
46
+ // MUI theme
47
+ const theme = useTheme();
48
+ const darkMode = theme.palette.mode === "dark";
49
+ /**
50
+ * Render the list of conversation turns.
51
+ */
52
+ const nodesList = useMemo(() => turns.flatMap((turn) => {
53
+ switch (turn.role) {
54
+ case MessageRole.User:
55
+ return [
56
+ _jsx(UserTurnBubble, { id: turn.id, children: turn.text }, turn.id),
57
+ ];
58
+ case MessageRole.Agent:
59
+ return includeAgentMessages ? [formatTurnContent(turn)] : [];
60
+ case MessageRole.FinalAnswer:
61
+ return [formatTurnContent(turn)];
62
+ case MessageRole.Warning:
63
+ return [
64
+ _jsx(MUIAlert, { id: `warning-${turn.id}-alert`, severity: "warning", children: turn.text }, turn.id),
65
+ ];
66
+ case MessageRole.Error:
67
+ return [
68
+ _jsx(MUIAlert, { id: `error-${turn.id}-alert`, severity: "error", children: turn.text }, turn.id),
69
+ ];
70
+ /* istanbul ignore next -- impossible to trigger this without subverting tsc */
71
+ default: {
72
+ // Exhaustiveness check. This way tsc will complain if a new MessageRole is added but
73
+ // not handled here.
74
+ const _exhaustive = turn.role;
75
+ throw new Error(`Unhandled message role: ${_exhaustive}`);
76
+ }
77
+ }
78
+ }), [includeAgentMessages, turns]);
79
+ return (_jsx(FormattedMarkdown, { id: `${id}-conversation`, nodesList: nodesList, style: darkMode ? atelierDuneDark : a11yLight, wrapLongLines: shouldWrapOutput }));
80
+ };
@@ -0,0 +1,23 @@
1
+ import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
2
+ /**
3
+ * The various messages roles in a conversation turn.
4
+ */
5
+ export declare enum MessageRole {
6
+ "Agent" = "Agent",
7
+ "Error" = "Error",
8
+ "FinalAnswer" = "FinalAnswer",
9
+ "User" = "User",
10
+ "Warning" = "Warning"
11
+ }
12
+ /**
13
+ * Represents a single turn in a conversation, which may include a message from the user or agent, or an error
14
+ */
15
+ export interface ConversationTurn {
16
+ readonly agentDisplayName?: string;
17
+ readonly agentName?: string;
18
+ readonly id: string;
19
+ readonly messageType?: ChatMessageType;
20
+ readonly role: MessageRole;
21
+ readonly structure?: Record<string, unknown>;
22
+ readonly text?: string;
23
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * The various messages roles in a conversation turn.
3
+ */
4
+ export var MessageRole;
5
+ (function (MessageRole) {
6
+ MessageRole["Agent"] = "Agent";
7
+ MessageRole["Error"] = "Error";
8
+ MessageRole["FinalAnswer"] = "FinalAnswer";
9
+ MessageRole["User"] = "User";
10
+ MessageRole["Warning"] = "Warning";
11
+ })(MessageRole || (MessageRole = {}));
@@ -14,11 +14,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
14
  See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  */
17
+ import Box from "@mui/material/Box";
17
18
  import { Fragment } from "react";
18
19
  import ReactMarkdown from "react-markdown";
19
20
  import SyntaxHighlighter from "react-syntax-highlighter";
20
21
  import rehypeRaw from "rehype-raw";
21
22
  import rehypeSlug from "rehype-slug";
23
+ import remarkGfm from "remark-gfm";
22
24
  import { hashString } from "../../../utils/text.js";
23
25
  /**
24
26
  * Format the output to ensure that text nodes are formatted as Markdown but other nodes are passed along as-is.
@@ -34,11 +36,11 @@ export const FormattedMarkdown = ({ id, nodesList, style, wrapLongLines = false,
34
36
  * @param index The index of the string in the nodes list. Used as "salt" to generate a unique key.
35
37
  * @returns The formatted Markdown.
36
38
  */
37
- const getFormattedMarkdown = (stringToFormat, index) => (_jsx(ReactMarkdown, { rehypePlugins: [rehypeRaw, rehypeSlug], components: {
39
+ const getFormattedMarkdown = (stringToFormat, index) => (_jsx(ReactMarkdown, { rehypePlugins: [rehypeRaw, rehypeSlug], remarkPlugins: [remarkGfm], components: {
38
40
  code: (codeProps) => {
39
41
  const { children, className, ...rest } = codeProps;
40
42
  const match = /language-(?<language>\w+)/u.exec(className || "");
41
- return match ? (_jsx(SyntaxHighlighter, { id: `syntax-highlighter-${match.groups["language"]}`, PreTag: "div", language: match.groups["language"], style: style, children: String(children).replace(/\n$/u, "") })) : (_jsx("code", { id: `code-${className}`, ...rest, className: className, style: wrapLongLines ? { whiteSpace: "pre-wrap", wordBreak: "break-word" } : {}, children: children }));
43
+ return match ? (_jsx(SyntaxHighlighter, { PreTag: "div", id: `syntax-highlighter-${match.groups["language"]}`, language: match.groups["language"], style: style, wrapLongLines: wrapLongLines, children: String(children).replace(/\n$/u, "") })) : (_jsx("code", { id: `code-${className}`, ...rest, className: className, style: wrapLongLines ? { whiteSpace: "pre-wrap", wordBreak: "break-word" } : {}, children: children }));
42
44
  },
43
45
  // Handle links specially since we want them to open in a new tab
44
46
  a: ({ ...codeProps }) => (_jsx("a", { ...codeProps, id: "reference-link", target: "_blank", rel: "noopener noreferrer", children: codeProps.children })),
@@ -76,5 +78,5 @@ export const FormattedMarkdown = ({ id, nodesList, style, wrapLongLines = false,
76
78
  const concatenatedText = getFormattedMarkdown(currentTextNodes.join(""), nodesList.length);
77
79
  formattedOutput.push(concatenatedText);
78
80
  }
79
- return _jsx("div", { id: id, children: formattedOutput });
81
+ return _jsx(Box, { id: id, children: formattedOutput });
80
82
  };
@@ -8,6 +8,9 @@ interface SampleQueriesProps {
8
8
  }
9
9
  /**
10
10
  * Render sample queries as clickable chips. Agents may or may not have sample queries defined.
11
+ * @param disabled Whether the chips should be disabled or not. Chips should be disabled when the agent is
12
+ * thinking/responding to prevent multiple simultaneous queries being sent to the agent.
13
+ * @param handleSend Function to handle sending a query to the agent when a chip is clicked.
11
14
  * @param sampleQueries The sample queries to render (from "connectivity" API)
12
15
  * @returns A ReactNode representing the sample queries as clickable chips. If a user clicks a chip, it will
13
16
  * send the query to the agent.
@@ -9,11 +9,14 @@ export const MAX_SAMPLE_QUERIES = 5;
9
9
  export const QUERY_TRUNCATE_LENGTH = 80;
10
10
  /**
11
11
  * Render sample queries as clickable chips. Agents may or may not have sample queries defined.
12
+ * @param disabled Whether the chips should be disabled or not. Chips should be disabled when the agent is
13
+ * thinking/responding to prevent multiple simultaneous queries being sent to the agent.
14
+ * @param handleSend Function to handle sending a query to the agent when a chip is clicked.
12
15
  * @param sampleQueries The sample queries to render (from "connectivity" API)
13
16
  * @returns A ReactNode representing the sample queries as clickable chips. If a user clicks a chip, it will
14
17
  * send the query to the agent.
15
18
  */
16
- export const SampleQueries = ({ disabled, handleSend, sampleQueries }) => sampleQueries?.length > 0 ? (_jsx(Box, { id: "sample-queries-box", sx: { marginTop: "2rem", marginBottom: "1rem" }, children: sampleQueries.slice(0, MAX_SAMPLE_QUERIES).map((query) => {
19
+ export const SampleQueries = ({ disabled, handleSend, sampleQueries }) => sampleQueries?.length > 0 ? (_jsx(Box, { id: "sample-queries-box", sx: { marginTop: "1rem" }, children: sampleQueries.slice(0, MAX_SAMPLE_QUERIES).map((query) => {
17
20
  const hashedQuery = hashString(query);
18
21
  return (_jsx(Tooltip, { title: `Click to send query: "${query}"`, children: _jsx(Chip, { disabled: disabled, label: query.length > QUERY_TRUNCATE_LENGTH
19
22
  ? `${query.slice(0, QUERY_TRUNCATE_LENGTH)}...`
@@ -23,7 +26,7 @@ export const SampleQueries = ({ disabled, handleSend, sampleQueries }) => sample
23
26
  color: "var(--bs-white)",
24
27
  marginRight: "1rem",
25
28
  marginBottom: "1rem",
26
- backgroundColor: "var(--bs-accent1-medium)",
27
- "&:hover": { backgroundColor: "var(--bs-accent1-dark)" },
29
+ backgroundColor: "var(--bs-accent2-medium)",
30
+ "&:hover": { backgroundColor: "var(--bs-accent2-dark)" },
28
31
  } }) }, `tooltip-${hashedQuery}`));
29
32
  }) })) : null;
@@ -0,0 +1,12 @@
1
+ import { FC } from "react";
2
+ import { ConversationTurn } from "./ConversationTurn.js";
3
+ interface ThinkingProps {
4
+ readonly id: string;
5
+ readonly turns: ConversationTurn[];
6
+ }
7
+ /**
8
+ * Component to render the "thinking" section of the chat, which includes all messages from the agent that are
9
+ * of a type included in THINKING_MESSAGE_TYPES.
10
+ */
11
+ export declare const Thinking: FC<ThinkingProps>;
12
+ export {};
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import Box from "@mui/material/Box";
3
+ import { isEmpty } from "lodash-es";
4
+ import { useMemo } from "react";
5
+ import ReactMarkdown from "react-markdown";
6
+ import { MessageRole } from "./ConversationTurn.js";
7
+ import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
8
+ import { AccordionLite } from "../../Common/AccordionLite.js";
9
+ /**
10
+ * Set of message types that should be included in the "thinking" section.
11
+ */
12
+ const THINKING_MESSAGE_TYPES = new Set([
13
+ ChatMessageType.AI,
14
+ ChatMessageType.AGENT,
15
+ ChatMessageType.AGENT_PROGRESS,
16
+ ChatMessageType.SYSTEM,
17
+ ]);
18
+ const DEFAULT_AGENT_NAME = "Agent";
19
+ const formatTurn = (turn) => {
20
+ const headerLine = `**${turn.agentName ?? DEFAULT_AGENT_NAME}**: ${turn.text || ""}`;
21
+ const structureLine = turn.structure != null ? `\n\`${JSON.stringify(turn.structure, null, 2)}\`` : "";
22
+ return `${headerLine}${structureLine}`;
23
+ };
24
+ /**
25
+ * Component to render the "thinking" section of the chat, which includes all messages from the agent that are
26
+ * of a type included in THINKING_MESSAGE_TYPES.
27
+ */
28
+ export const Thinking = ({ id, turns }) => {
29
+ const thinkingText = useMemo(() => {
30
+ // Find start of latest interaction (most recent user prompt)
31
+ const lastUserTurnIndex = turns.map((t) => t.role).lastIndexOf(MessageRole.User);
32
+ // Only include thinking emitted after latest user prompt
33
+ // Deliberately allow -1 result to fall through and include all thinking messages if there are no user turns
34
+ return turns
35
+ .slice(lastUserTurnIndex + 1)
36
+ .filter((turn) => THINKING_MESSAGE_TYPES.has(turn.messageType))
37
+ .filter((turn) => turn.text?.trim().length > 0 || !isEmpty(turn.structure))
38
+ .map((turn) => formatTurn(turn))
39
+ .join("\n\n");
40
+ }, [turns]);
41
+ return (thinkingText.length > 0 && (_jsx(AccordionLite, { id: `${id}-thinking`, items: _jsx(Box, { sx: {
42
+ marginLeft: "0.5rem",
43
+ fontSize: "smaller",
44
+ fontStyle: "italic",
45
+ }, children: _jsx(ReactMarkdown, { children: thinkingText }) }), contentSx: {
46
+ color: "text.secondary",
47
+ minWidth: 0,
48
+ padding: 0,
49
+ textTransform: "none",
50
+ }, title: "Show Thinking" })));
51
+ };
@@ -3,6 +3,6 @@ type LLMChatGroupConfigBtnProps = {
3
3
  posRight?: number;
4
4
  posBottom?: number;
5
5
  };
6
- export declare const LlmChatButton: import("@emotion/styled").StyledComponent<import("@mui/material/Button").ButtonOwnProps & Omit<import("@mui/material/ButtonBase").ButtonBaseOwnProps, "classes"> & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "className" | "style" | "classes" | "children" | "sx" | "variant" | "color" | "disabled" | "tabIndex" | "action" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | "href" | "size" | "disableElevation" | "disableFocusRipple" | "endIcon" | "fullWidth" | "loading" | "loadingIndicator" | "loadingPosition" | "startIcon"> & import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme> & LLMChatGroupConfigBtnProps, {}, {}>;
7
- export declare const SmallLlmChatButton: import("@emotion/styled").StyledComponent<import("@mui/material/Button").ButtonOwnProps & Omit<import("@mui/material/ButtonBase").ButtonBaseOwnProps, "classes"> & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "className" | "style" | "classes" | "children" | "sx" | "variant" | "color" | "disabled" | "tabIndex" | "action" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | "href" | "size" | "disableElevation" | "disableFocusRipple" | "endIcon" | "fullWidth" | "loading" | "loadingIndicator" | "loadingPosition" | "startIcon"> & import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme> & LLMChatGroupConfigBtnProps, {}, {}>;
6
+ export declare const LlmChatButton: import("@emotion/styled").StyledComponent<import("@mui/material/Button").ButtonOwnProps & Omit<import("@mui/material/ButtonBase").ButtonBaseOwnProps, keyof import("@mui/material/Button").ButtonOwnProps> & Omit<import("@mui/material/ButtonBase").ButtonBaseOwnProps, "tabIndex" | "type" | "action" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "nativeButton" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | keyof import("@mui/material/Button").ButtonOwnProps> & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "style" | "children" | "sx" | "className" | "tabIndex" | "color" | "href" | "type" | "classes" | "variant" | "action" | "size" | "disabled" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "nativeButton" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | "disableFocusRipple" | "loading" | "loadingIndicator" | "disableElevation" | "endIcon" | "fullWidth" | "loadingPosition" | "startIcon"> & import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme> & LLMChatGroupConfigBtnProps, {}, {}>;
7
+ export declare const SmallLlmChatButton: import("@emotion/styled").StyledComponent<import("@mui/material/Button").ButtonOwnProps & Omit<import("@mui/material/ButtonBase").ButtonBaseOwnProps, keyof import("@mui/material/Button").ButtonOwnProps> & Omit<import("@mui/material/ButtonBase").ButtonBaseOwnProps, "tabIndex" | "type" | "action" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "nativeButton" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | keyof import("@mui/material/Button").ButtonOwnProps> & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "style" | "children" | "sx" | "className" | "tabIndex" | "color" | "href" | "type" | "classes" | "variant" | "action" | "size" | "disabled" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "nativeButton" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | "disableFocusRipple" | "loading" | "loadingIndicator" | "disableElevation" | "endIcon" | "fullWidth" | "loadingPosition" | "startIcon"> & import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme> & LLMChatGroupConfigBtnProps, {}, {}>;
8
8
  export {};
@@ -6,12 +6,13 @@ export declare enum LegacyAgentType {
6
6
  ChatBot = "ChatBot"
7
7
  }
8
8
  export declare const isLegacyAgentType: (agent: string) => boolean;
9
+ export declare const givesFinalAnswer: (agent: CombinedAgentType) => boolean;
9
10
  export type CombinedAgentType = LegacyAgentType | string;
10
11
  /**
11
12
  * Models the error we receive from neuro-san agents.
12
13
  */
13
- export interface AgentErrorProps {
14
- error: string;
15
- traceback?: string;
16
- tool?: string;
17
- }
14
+ export type AgentErrorProps = {
15
+ readonly error: string;
16
+ readonly traceback?: string;
17
+ readonly tool?: string;
18
+ };
@@ -24,3 +24,8 @@ export var LegacyAgentType;
24
24
  export const isLegacyAgentType = (agent) => {
25
25
  return Object.keys(LegacyAgentType).includes(agent);
26
26
  };
27
+ // Agents which are known to provide a predictable "Final Answer" response
28
+ export const givesFinalAnswer = (agent) => {
29
+ return (!isLegacyAgentType(agent) ||
30
+ [LegacyAgentType.ChatBot, LegacyAgentType.DMSChat].includes(agent));
31
+ };
@@ -2,7 +2,7 @@ import { ChatMessage, ChatMessageType } from "../../../generated/neuro-san/Neuro
2
2
  export declare const KNOWN_MESSAGE_TYPES: ChatMessageType[];
3
3
  export declare const KNOWN_MESSAGE_TYPES_FOR_PLASMA: ChatMessageType[];
4
4
  export declare const chatMessageFromChunk: (chunk: string) => ChatMessage | null;
5
- export declare const checkError: (chatMessageJson: object) => string | null;
5
+ export declare const checkError: (chatMessageJson: Record<string, unknown>) => string | null;
6
6
  export declare const removeTrailingUuid: (agentName: string) => string;
7
7
  /**
8
8
  * Convert FOO_BAR to more human "Foo Bar".
@@ -13,7 +13,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
13
  See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
- import capitalize from "lodash-es/capitalize.js";
16
+ import { isEmpty } from "lodash-es";
17
17
  import startCase from "lodash-es/startCase.js";
18
18
  import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
19
19
  // We ignore any messages that are not of these types
@@ -44,16 +44,21 @@ export const chatMessageFromChunk = (chunk) => {
44
44
  }
45
45
  return chatResponse.response;
46
46
  };
47
+ /**
48
+ * Type guard to check if a value has the shape of an AgentErrorProps, which indicates it's an error message from
49
+ * an agent. Note: not all Neuro-san agents follow this convention.
50
+ * @param value The value to check.
51
+ * @returns True if the value is an AgentErrorProps, false otherwise.
52
+ */
53
+ const isAgentErrorLike = (value) => {
54
+ return typeof value?.error === "string";
55
+ };
47
56
  export const checkError = (chatMessageJson) => {
48
- if (chatMessageJson && "error" in chatMessageJson) {
49
- const agentError = chatMessageJson;
50
- return (`Error occurred. Error: "${agentError.error}", ` +
51
- `traceback: "${agentError?.traceback}", ` +
52
- `tool: "${agentError?.tool}"`);
53
- }
54
- else {
57
+ if (isEmpty(chatMessageJson) || !isAgentErrorLike(chatMessageJson)) {
55
58
  return null;
56
59
  }
60
+ const { error, traceback, tool } = chatMessageJson;
61
+ return `Error occurred. Error: "${error}", traceback: "${traceback}", tool: "${tool}"`;
57
62
  };
58
63
  export const removeTrailingUuid = (agentName) => {
59
64
  return agentName?.replace(/-[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/u, "");
@@ -64,5 +69,5 @@ export const removeTrailingUuid = (agentName) => {
64
69
  * @returns User-friendly agent name.
65
70
  */
66
71
  export const cleanUpAgentName = (agentName) => {
67
- return startCase(capitalize(agentName));
72
+ return startCase(agentName);
68
73
  };
@@ -0,0 +1,14 @@
1
+ import { SxProps } from "@mui/material/styles";
2
+ import { FC, ReactNode } from "react";
3
+ interface AccordionLiteProps {
4
+ readonly id: string;
5
+ readonly items: ReactNode;
6
+ readonly contentSx?: SxProps;
7
+ readonly title: ReactNode;
8
+ }
9
+ /**
10
+ * A lightweight accordion component that can be used to show/hide content.
11
+ * @see {MUIAccordion} for a more heavyweight accordion.
12
+ */
13
+ export declare const AccordionLite: FC<AccordionLiteProps>;
14
+ export {};
@@ -0,0 +1,25 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
3
+ import Box from "@mui/material/Box";
4
+ import Button from "@mui/material/Button";
5
+ import Collapse from "@mui/material/Collapse";
6
+ import { useState } from "react";
7
+ /**
8
+ * A lightweight accordion component that can be used to show/hide content.
9
+ * @see {MUIAccordion} for a more heavyweight accordion.
10
+ */
11
+ export const AccordionLite = ({ id, items, contentSx, title }) => {
12
+ const [isExpanded, setIsExpanded] = useState(false);
13
+ return (_jsxs(Box, { id: id, children: [_jsx(Button, { onClick: () => setIsExpanded((prev) => !prev), startIcon: _jsx(ExpandMoreIcon, { sx: {
14
+ fontSize: "1rem",
15
+ transform: isExpanded ? "rotate(0deg)" : "rotate(270deg)",
16
+ transition: "transform 150ms ease",
17
+ } }), sx: {
18
+ "&:hover": { backgroundColor: "transparent", textDecoration: "underline" },
19
+ color: "text.secondary",
20
+ fontSize: "smaller",
21
+ minWidth: 0,
22
+ padding: 0,
23
+ textTransform: "none",
24
+ }, children: title }), _jsx(Collapse, { id: `${id}-items`, in: isExpanded, timeout: "auto", unmountOnExit: false, children: _jsx(Box, { sx: [...(Array.isArray(contentSx) ? contentSx : [contentSx])], children: items }) })] }));
25
+ };
@@ -1,4 +1,5 @@
1
1
  import { FC, ReactNode } from "react";
2
+ export declare const StyledButton: import("@emotion/styled").StyledComponent<import("@mui/material/Button").ButtonOwnProps & Omit<import("@mui/material/ButtonBase").ButtonBaseOwnProps, keyof import("@mui/material/Button").ButtonOwnProps> & Omit<import("@mui/material/ButtonBase").ButtonBaseOwnProps, "tabIndex" | "type" | "action" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "nativeButton" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | keyof import("@mui/material/Button").ButtonOwnProps> & import("@mui/material/OverridableComponent").CommonProps & Omit<import("react").DetailedHTMLProps<import("react").ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>, "style" | "children" | "sx" | "className" | "tabIndex" | "color" | "href" | "type" | "classes" | "variant" | "action" | "size" | "disabled" | "centerRipple" | "disableRipple" | "disableTouchRipple" | "focusRipple" | "focusVisibleClassName" | "LinkComponent" | "nativeButton" | "onFocusVisible" | "TouchRippleProps" | "touchRippleRef" | "disableFocusRipple" | "loading" | "loadingIndicator" | "disableElevation" | "endIcon" | "fullWidth" | "loadingPosition" | "startIcon"> & import("@mui/system").MUIStyledCommonProps<import("@mui/material/styles").Theme>, {}, {}>;
2
3
  interface ConfirmationModalProps {
3
4
  cancelBtnLabel?: string;
4
5
  closeable?: boolean;
@@ -20,7 +20,7 @@ import { styled } from "@mui/material/styles";
20
20
  import { useState } from "react";
21
21
  import { MUIDialog } from "./MUIDialog.js";
22
22
  // #region: Styled Components
23
- const StyledButton = styled(Button)({
23
+ export const StyledButton = styled(Button)({
24
24
  fontSize: "0.8em",
25
25
  padding: "0px 7px",
26
26
  });
@@ -22,7 +22,7 @@ export const CustomerLogo = ({ fallbackElement, logoServiceToken }) => {
22
22
  const logoSource = useSettingsStore((state) => state.settings.branding.logoSource);
23
23
  // null: render Cognizant logo (default)
24
24
  if (logoSource === null) {
25
- return getCognizantLogoImage();
25
+ return customer?.trim().length ? null : getCognizantLogoImage();
26
26
  }
27
27
  // "none": explicitly render no logo
28
28
  if (logoSource === "none") {
@@ -5,6 +5,7 @@ interface MUIAlertProps {
5
5
  children: ReactNode;
6
6
  closeable?: boolean;
7
7
  id: string;
8
+ onClose?: () => void;
8
9
  severity: AlertColor;
9
10
  sx?: SxProps;
10
11
  }
@@ -30,14 +30,13 @@ const StyledAlert = styled(Alert)({
30
30
  },
31
31
  });
32
32
  // #endregion: Types
33
- export const MUIAlert = ({ children, closeable = false, id, severity, sx }) => {
33
+ export const MUIAlert = ({ children, closeable = false, id, onClose, severity, sx }) => {
34
34
  const [alertOpen, setAlertOpen] = useState(true);
35
35
  const handleClose = () => {
36
36
  setAlertOpen(false);
37
+ onClose?.();
37
38
  };
38
- return (_jsx(Collapse, { id: `${id}-collapse`, in: alertOpen, children: _jsx(StyledAlert, { action: closeable && (_jsx(IconButton, { "aria-label": "close", color: "inherit", id: `${id}-icon-button`, onClick: () => {
39
- setAlertOpen(false);
40
- }, size: "small", sx: {
39
+ return (_jsx(Collapse, { id: `${id}-collapse`, in: alertOpen, children: _jsx(StyledAlert, { action: closeable && (_jsx(IconButton, { "aria-label": "close", color: "inherit", id: `${id}-icon-button`, onClick: handleClose, size: "small", sx: {
41
40
  bottom: "2px",
42
41
  position: "relative",
43
42
  }, children: _jsx(CloseIcon, { fontSize: "inherit", id: `${id}-close-icon` }) })), id: id, onClose: closeable ? handleClose : undefined, severity: severity, sx: sx, children: children }) }));
@@ -13,5 +13,6 @@ export interface NavbarProps {
13
13
  readonly supportEmailAddress: string;
14
14
  readonly logoServiceToken?: string;
15
15
  readonly showSettingsButton?: boolean;
16
+ readonly onStartTour?: () => void;
16
17
  }
17
- export declare const Navbar: ({ authenticationType, id, logo, logoServiceToken, pathname, query, showSettingsButton, signOut, supportEmailAddress, userInfo, }: NavbarProps) => ReactJSX.Element;
18
+ export declare const Navbar: ({ authenticationType, id, logo, logoServiceToken, onStartTour, pathname, query, showSettingsButton, signOut, supportEmailAddress, userInfo, }: NavbarProps) => ReactJSX.Element;