@cognizant-ai-lab/ui-common 1.6.0 → 1.8.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/dist/Theme/Palettes.d.ts +0 -9
- package/dist/Theme/Palettes.js +0 -18
- package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +11 -7
- package/dist/components/AgentChat/ChatCommon/ChatCommon.js +119 -90
- package/dist/components/AgentChat/ChatCommon/Const.d.ts +1 -0
- package/dist/components/AgentChat/ChatCommon/Const.js +2 -0
- package/dist/components/AgentChat/ChatCommon/ControlButtons.d.ts +1 -1
- package/dist/components/AgentChat/ChatCommon/ControlButtons.js +7 -4
- package/dist/components/AgentChat/ChatCommon/FormattedMarkdown.js +3 -0
- package/dist/components/AgentChat/ChatCommon/SendButton.js +7 -6
- package/dist/components/AgentChat/ChatCommon/SyntaxHighlighterThemes.js +1 -1
- package/dist/components/AgentChat/ChatCommon/Thinking.d.ts +1 -0
- package/dist/components/AgentChat/ChatCommon/Thinking.js +10 -7
- package/dist/components/AgentChat/Common/LlmChatButton.d.ts +0 -2
- package/dist/components/AgentChat/Common/LlmChatButton.js +6 -9
- package/dist/components/AgentChat/VoiceChat/MicrophoneButton.js +12 -10
- package/dist/components/ChatBot/ChatBot.d.ts +0 -4
- package/dist/components/ChatBot/ChatBot.js +2 -2
- package/dist/components/Common/Navbar.js +24 -22
- package/dist/components/Common/StatusLight.d.ts +9 -0
- package/dist/components/Common/StatusLight.js +21 -0
- package/dist/components/ErrorPage/ErrorBoundary.js +1 -1
- package/dist/components/ErrorPage/ErrorPage.d.ts +3 -3
- package/dist/components/ErrorPage/ErrorPage.js +4 -3
- package/dist/components/Logo/Common.d.ts +4 -0
- package/dist/components/Logo/Common.js +8 -0
- package/dist/components/Logo/CustomerLogo.d.ts +23 -0
- package/dist/components/Logo/CustomerLogo.js +41 -0
- package/dist/components/MultiAgentAccelerator/AgentConversations.js +0 -1
- package/dist/components/MultiAgentAccelerator/AgentFlow.js +85 -53
- package/dist/components/MultiAgentAccelerator/AgentNode.js +23 -27
- package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +17 -10
- package/dist/components/MultiAgentAccelerator/GraphLayouts.js +4 -6
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +3 -9
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +102 -47
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +0 -5
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +30 -25
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +3 -4
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +84 -168
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +30 -13
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +157 -100
- package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +10 -8
- package/dist/components/MultiAgentAccelerator/const.d.ts +2 -0
- package/dist/components/MultiAgentAccelerator/const.js +4 -0
- package/dist/components/Settings/ApiKeyInput.d.ts +16 -0
- package/dist/components/Settings/ApiKeyInput.js +69 -0
- package/dist/components/Settings/InfoTip.d.ts +8 -0
- package/dist/components/Settings/InfoTip.js +14 -0
- package/dist/components/Settings/SettingsDialog.js +379 -178
- package/dist/components/Settings/SettingsRow.d.ts +18 -0
- package/dist/components/Settings/SettingsRow.js +35 -0
- package/dist/controller/agent/Agent.d.ts +3 -20
- package/dist/controller/agent/Agent.js +9 -43
- package/dist/controller/agent/IconSuggestions.d.ts +35 -0
- package/dist/controller/agent/IconSuggestions.js +85 -0
- package/dist/controller/llm/Providers.d.ts +2 -0
- package/dist/controller/llm/Providers.js +41 -0
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -2
- package/dist/state/IconSuggestions.d.ts +27 -0
- package/dist/state/IconSuggestions.js +50 -0
- package/dist/state/Settings.d.ts +19 -3
- package/dist/state/Settings.js +24 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/title.js +1 -1
- package/package.json +5 -1
- package/dist/components/Common/CustomerLogo.d.ts +0 -17
- package/dist/components/Common/CustomerLogo.js +0 -49
- package/dist/components/Common/MUIAccordion.d.ts +0 -17
- package/dist/components/Common/MUIAccordion.js +0 -66
- package/dist/utils/useLocalStorage.d.ts +0 -1
- package/dist/utils/useLocalStorage.js +0 -55
package/dist/Theme/Palettes.d.ts
CHANGED
|
@@ -7,12 +7,3 @@ export declare const PALETTES: {
|
|
|
7
7
|
coldToHot: string[];
|
|
8
8
|
visionImpaired: string[];
|
|
9
9
|
};
|
|
10
|
-
export type PaletteKey = keyof typeof PALETTES | "brand";
|
|
11
|
-
/**
|
|
12
|
-
* Custom hook to get the current color palette based on user settings.
|
|
13
|
-
* If the user has selected custom branding, it will return the palette for that.
|
|
14
|
-
* Otherwise, it will return one of the predefined palettes from the PALETTES object based on the user's selection.
|
|
15
|
-
*
|
|
16
|
-
* @returns An array of color hex codes representing the current color palette.
|
|
17
|
-
*/
|
|
18
|
-
export declare const usePalette: () => string[];
|
package/dist/Theme/Palettes.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
// Palettes for progressive coloring of nodes based on depth or heatmap value
|
|
2
|
-
import { useSettingsStore } from "../state/Settings.js";
|
|
3
2
|
export const PALETTES = {
|
|
4
3
|
blue: [
|
|
5
4
|
"#f7fbff",
|
|
@@ -75,20 +74,3 @@ export const PALETTES = {
|
|
|
75
74
|
"#b6dbff",
|
|
76
75
|
],
|
|
77
76
|
};
|
|
78
|
-
/**
|
|
79
|
-
* Custom hook to get the current color palette based on user settings.
|
|
80
|
-
* If the user has selected custom branding, it will return the palette for that.
|
|
81
|
-
* Otherwise, it will return one of the predefined palettes from the PALETTES object based on the user's selection.
|
|
82
|
-
*
|
|
83
|
-
* @returns An array of color hex codes representing the current color palette.
|
|
84
|
-
*/
|
|
85
|
-
export const usePalette = () => {
|
|
86
|
-
const brandPalette = useSettingsStore((state) => state.settings.branding.rangePalette);
|
|
87
|
-
const paletteKey = useSettingsStore((state) => state.settings.appearance.rangePalette);
|
|
88
|
-
if (paletteKey === "brand" && brandPalette) {
|
|
89
|
-
return brandPalette;
|
|
90
|
-
}
|
|
91
|
-
else {
|
|
92
|
-
return PALETTES[paletteKey];
|
|
93
|
-
}
|
|
94
|
-
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Dispatch, Ref, SetStateAction } from "react";
|
|
2
|
+
import { LLMProvider } from "../../../state/Settings.js";
|
|
2
3
|
import { CombinedAgentType } from "../Common/Types.js";
|
|
3
4
|
export interface ChatCommonProps {
|
|
4
5
|
/**
|
|
@@ -9,10 +10,6 @@ export interface ChatCommonProps {
|
|
|
9
10
|
* The current username of the logged-in user. Used for fetching things from APIs mainly
|
|
10
11
|
*/
|
|
11
12
|
readonly currentUser: string;
|
|
12
|
-
/**
|
|
13
|
-
* Path to image for user avatar
|
|
14
|
-
*/
|
|
15
|
-
readonly userImage: string;
|
|
16
13
|
/**
|
|
17
14
|
* Function to set the state of the component to indicate whether we are awaiting a response from the LLM
|
|
18
15
|
*/
|
|
@@ -22,9 +19,13 @@ export interface ChatCommonProps {
|
|
|
22
19
|
*/
|
|
23
20
|
readonly isAwaitingLlm: boolean;
|
|
24
21
|
/**
|
|
25
|
-
* The
|
|
22
|
+
* The network to send the request to.
|
|
23
|
+
*/
|
|
24
|
+
readonly selectedNetwork: string | null;
|
|
25
|
+
/**
|
|
26
|
+
* Setter for changing the selected network.
|
|
26
27
|
*/
|
|
27
|
-
readonly
|
|
28
|
+
readonly setSelectedNetwork?: (network: string | null) => void;
|
|
28
29
|
/**
|
|
29
30
|
* Special endpoint for legacy agents since they do not have a single unified endpoint like Neuro-san agents.
|
|
30
31
|
*/
|
|
@@ -94,12 +95,15 @@ export interface ChatCommonProps {
|
|
|
94
95
|
* Sample queries for the current network that the user can "click to send"
|
|
95
96
|
*/
|
|
96
97
|
readonly sampleQueries?: string[];
|
|
98
|
+
/**
|
|
99
|
+
* Array of LLM providers for which API keys are required but missing. Only applies to BYOK networks.
|
|
100
|
+
*/
|
|
101
|
+
readonly missingApiKeys?: LLMProvider[];
|
|
97
102
|
}
|
|
98
103
|
export type ChatCommonHandle = {
|
|
99
104
|
handleStop: () => void;
|
|
100
105
|
handleClearChat: () => void;
|
|
101
106
|
};
|
|
102
|
-
export declare const MAX_TURNS = 50;
|
|
103
107
|
/**
|
|
104
108
|
* Common chat component for agent chat. This component is used by all agent chat components to provide a consistent
|
|
105
109
|
* experience for users when chatting with agents. It handles user input as well as displaying and nicely formatting
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
Copyright 2025 Cognizant Technology Solutions Corp, www.cognizant.com.
|
|
4
4
|
|
|
@@ -18,6 +18,7 @@ limitations under the License.
|
|
|
18
18
|
* See main function description.
|
|
19
19
|
*/
|
|
20
20
|
import { AIMessage, HumanMessage } from "@langchain/core/messages";
|
|
21
|
+
import AddBoxRounded from "@mui/icons-material/AddBoxRounded";
|
|
21
22
|
import ClearIcon from "@mui/icons-material/Clear";
|
|
22
23
|
import CloseIcon from "@mui/icons-material/Close";
|
|
23
24
|
import TuneIcon from "@mui/icons-material/Tune";
|
|
@@ -32,12 +33,12 @@ import ListItemText from "@mui/material/ListItemText";
|
|
|
32
33
|
import Menu from "@mui/material/Menu";
|
|
33
34
|
import MenuItem from "@mui/material/MenuItem";
|
|
34
35
|
import { useTheme } from "@mui/material/styles";
|
|
35
|
-
import Tooltip from "@mui/material/Tooltip";
|
|
36
36
|
import Typography from "@mui/material/Typography";
|
|
37
37
|
import { isEmpty } from "lodash-es";
|
|
38
38
|
import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from "react";
|
|
39
39
|
import { v4 as uuid } from "uuid";
|
|
40
40
|
import { ChatHistory } from "./ChatHistory.js";
|
|
41
|
+
import { MAX_TURNS } from "./Const.js";
|
|
41
42
|
import { ControlButtons } from "./ControlButtons.js";
|
|
42
43
|
import { Conversation } from "./Conversation.js";
|
|
43
44
|
import { MessageRole } from "./ConversationTurn.js";
|
|
@@ -48,11 +49,16 @@ import { sendChatQuery } from "../../../controller/agent/Agent.js";
|
|
|
48
49
|
import { sendLlmRequest, StreamingUnit } from "../../../controller/llm/LlmChat.js";
|
|
49
50
|
import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
|
|
50
51
|
import { useAgentChatHistoryStore } from "../../../state/ChatHistory.js";
|
|
52
|
+
import { useSettingsStore } from "../../../state/Settings.js";
|
|
51
53
|
import { hasOnlyWhitespace } from "../../../utils/text.js";
|
|
54
|
+
import { getZIndex } from "../../../utils/zIndexLayers.js";
|
|
55
|
+
import { AGENT_NETWORK_DESIGNER_ID } from "../../MultiAgentAccelerator/const.js";
|
|
52
56
|
import { givesFinalAnswer, isLegacyAgentType } from "../Common/Types.js";
|
|
53
57
|
import { chatMessageFromChunk, checkError, cleanUpAgentName, removeTrailingUuid } from "../Common/Utils.js";
|
|
54
58
|
import { MicrophoneButton } from "../VoiceChat/MicrophoneButton.js";
|
|
55
59
|
import { cleanupAndStopSpeechRecognition, setupSpeechRecognition } from "../VoiceChat/VoiceChat.js";
|
|
60
|
+
//#endregion
|
|
61
|
+
//#region Constants
|
|
56
62
|
// Define fancy EMPTY constant to avoid linter error about using object literals as default props
|
|
57
63
|
const EMPTY = {};
|
|
58
64
|
// How many times to retry the entire agent interaction process. Some networks have a well-defined success condition.
|
|
@@ -64,15 +70,14 @@ const MAX_AGENT_RETRIES = 3;
|
|
|
64
70
|
* @returns The final answer from the agent, if it exists or undefined if it doesn't
|
|
65
71
|
*/
|
|
66
72
|
const extractFinalAnswer = (response) => /Final Answer: (?<finalAnswerText>.*)/su.exec(response)?.groups?.["finalAnswerText"];
|
|
67
|
-
|
|
68
|
-
export const MAX_TURNS = 50;
|
|
73
|
+
//#endregion
|
|
69
74
|
/**
|
|
70
75
|
* Common chat component for agent chat. This component is used by all agent chat components to provide a consistent
|
|
71
76
|
* experience for users when chatting with agents. It handles user input as well as displaying and nicely formatting
|
|
72
77
|
* agent responses. Customization for inputs and outputs is provided via event handlers-like props.
|
|
73
78
|
*/
|
|
74
79
|
export const ChatCommon = ({ ref, ...props }) => {
|
|
75
|
-
const {
|
|
80
|
+
const { agentPlaceholders = EMPTY, backgroundColor, currentUser, customAgentGreetings = EMPTY, extraParams, extraSlyData, id, isAwaitingLlm, legacyAgentEndpoint, missingApiKeys = [], networkDescription, neuroSanURL, onChunkReceived, onClose, onSend, onStreamingComplete, onStreamingStarted, sampleQueries, selectedNetwork, setIsAwaitingLlm, setPreviousResponse, setSelectedNetwork, title, } = props;
|
|
76
81
|
// MUI theme
|
|
77
82
|
const theme = useTheme();
|
|
78
83
|
// User LLM chat input
|
|
@@ -99,8 +104,10 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
99
104
|
const [optionsMenuOpen, setOptionsMenuOpen] = useState(false);
|
|
100
105
|
// Persistent agent chat history store, which is where we store both kinds of chat histories
|
|
101
106
|
// (see store implementation for details)
|
|
102
|
-
const storedChatHistory = useAgentChatHistoryStore((state) => state?.history?.[
|
|
107
|
+
const storedChatHistory = useAgentChatHistoryStore((state) => selectedNetwork ? state?.history?.[selectedNetwork] : undefined);
|
|
103
108
|
const agentChatHistory = useMemo(() => storedChatHistory ?? { chatHistory: [], chatContext: null, slyData: {} }, [storedChatHistory]);
|
|
109
|
+
// Display option for agent/network names
|
|
110
|
+
const useNativeNames = useSettingsStore((state) => state.settings.appearance.useNativeNames);
|
|
104
111
|
// Access store for context items
|
|
105
112
|
const updateChatContext = useAgentChatHistoryStore((state) => state.updateChatContext);
|
|
106
113
|
const updateChatHistory = useAgentChatHistoryStore((state) => state.updateChatHistory);
|
|
@@ -135,7 +142,7 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
135
142
|
};
|
|
136
143
|
// Keeps track of whether the agent completed its task
|
|
137
144
|
const succeeded = useRef(false);
|
|
138
|
-
const
|
|
145
|
+
const networkDisplayName = useMemo(() => (useNativeNames ? selectedNetwork : cleanUpAgentName(removeTrailingUuid(selectedNetwork))), [selectedNetwork, useNativeNames]);
|
|
139
146
|
useEffect(() => {
|
|
140
147
|
// Set up speech recognition
|
|
141
148
|
const handlers = setupSpeechRecognition(setChatInput, setVoiceInputState, speechRecognitionRef);
|
|
@@ -170,7 +177,11 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
170
177
|
const legacyTurnIdRef = useRef(null);
|
|
171
178
|
const handleLegacyAgentChunk = useCallback((chunk) => {
|
|
172
179
|
currentResponse.current += chunk;
|
|
173
|
-
if (
|
|
180
|
+
if (legacyTurnIdRef.current) {
|
|
181
|
+
// We already have a turn for this response, so just update the text of that turn.
|
|
182
|
+
setTurns((prev) => prev.map((t) => (t.id === legacyTurnIdRef.current ? { ...t, text: currentResponse.current } : t)));
|
|
183
|
+
}
|
|
184
|
+
else {
|
|
174
185
|
// We don't yet have a turn for this response, so create one. On subsequent chunks, we'll just
|
|
175
186
|
// update the text of this turn.
|
|
176
187
|
legacyTurnIdRef.current = uuid();
|
|
@@ -181,10 +192,6 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
181
192
|
text: currentResponse.current,
|
|
182
193
|
});
|
|
183
194
|
}
|
|
184
|
-
else {
|
|
185
|
-
// We already have a turn for this response, so just update the text of that turn.
|
|
186
|
-
setTurns((prev) => prev.map((t) => (t.id === legacyTurnIdRef.current ? { ...t, text: currentResponse.current } : t)));
|
|
187
|
-
}
|
|
188
195
|
}, [addTurn]);
|
|
189
196
|
const handleNeuroSanAgentChunk = useCallback((chunk) => {
|
|
190
197
|
// For Neuro-san agents, we expect a ChatMessage structure in the chunk.
|
|
@@ -196,13 +203,13 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
196
203
|
}
|
|
197
204
|
// Shallow merge existing slyData with incoming chatMessage.sly_data
|
|
198
205
|
if (chatMessage.sly_data) {
|
|
199
|
-
updateSlyData(
|
|
206
|
+
updateSlyData(selectedNetwork, chatMessage.sly_data);
|
|
200
207
|
}
|
|
201
208
|
// It's a ChatMessage. Does it have chat context? Only AGENT_FRAMEWORK messages can have chat context.
|
|
202
209
|
if (chatMessage.type === ChatMessageType.AGENT_FRAMEWORK && chatMessage.chat_context) {
|
|
203
210
|
// Save the chat context, potentially overwriting any previous ones we received during this session.
|
|
204
211
|
// We only care about the last one received.
|
|
205
|
-
updateChatContext(
|
|
212
|
+
updateChatContext(selectedNetwork, chatMessage.chat_context);
|
|
206
213
|
}
|
|
207
214
|
// Check if there is an error block in the "structure" field of the chat message.
|
|
208
215
|
const errorMessage = checkError(chatMessage.structure);
|
|
@@ -215,14 +222,12 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
215
222
|
});
|
|
216
223
|
succeeded.current = false;
|
|
217
224
|
}
|
|
218
|
-
else if (chatMessage?.text?.trim().length > 0 || chatMessage.structure) {
|
|
225
|
+
else if (chatMessage?.text?.trim().length > 0 || !isEmpty(chatMessage.structure)) {
|
|
219
226
|
// Not an error, so output it if it has text or a structure.
|
|
220
227
|
// This is the normal happy path for an incoming message.
|
|
221
228
|
// The backend sometimes sends messages with no text content, and we don't want to display those to the
|
|
222
229
|
// user. Agent name is the last tool in the origin array. If it's not there, use a default name.
|
|
223
|
-
const agentName = chatMessage.origin?.
|
|
224
|
-
? cleanUpAgentName(chatMessage.origin[chatMessage.origin.length - 1].tool)
|
|
225
|
-
: "Agent";
|
|
230
|
+
const agentName = chatMessage.origin?.at(-1)?.tool ?? "agent";
|
|
226
231
|
addTurn({
|
|
227
232
|
agentName,
|
|
228
233
|
id: uuid(),
|
|
@@ -236,7 +241,7 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
236
241
|
currentResponse.current += chatMessage.text;
|
|
237
242
|
}
|
|
238
243
|
}
|
|
239
|
-
}, [addTurn,
|
|
244
|
+
}, [addTurn, selectedNetwork, updateChatContext, updateSlyData]);
|
|
240
245
|
/**
|
|
241
246
|
* Handle a chunk of response from the server. Called each time the server streams a chunk.
|
|
242
247
|
*/
|
|
@@ -244,14 +249,14 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
244
249
|
// Give container a chance to process the chunk first
|
|
245
250
|
const onChunkReceivedResult = onChunkReceived?.(chunk) ?? true;
|
|
246
251
|
succeeded.current = succeeded.current || onChunkReceivedResult;
|
|
247
|
-
if (isLegacyAgentType(
|
|
252
|
+
if (isLegacyAgentType(selectedNetwork)) {
|
|
248
253
|
// For legacy agents, we either get plain text or Markdown. Just output it as-is.
|
|
249
254
|
handleLegacyAgentChunk(chunk);
|
|
250
255
|
}
|
|
251
256
|
else {
|
|
252
257
|
handleNeuroSanAgentChunk(chunk);
|
|
253
258
|
}
|
|
254
|
-
}, [onChunkReceived,
|
|
259
|
+
}, [onChunkReceived, selectedNetwork, handleNeuroSanAgentChunk, handleLegacyAgentChunk]);
|
|
255
260
|
/**
|
|
256
261
|
* Reset the state of the component. This is called after a request is completed, regardless of success or failure.
|
|
257
262
|
*/
|
|
@@ -259,10 +264,10 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
259
264
|
// Reset state, whatever happened during request
|
|
260
265
|
setIsAwaitingLlm(false);
|
|
261
266
|
setChatInput("");
|
|
262
|
-
setPreviousResponse?.(
|
|
267
|
+
setPreviousResponse?.(selectedNetwork, currentResponse.current);
|
|
263
268
|
currentResponse.current = "";
|
|
264
269
|
legacyTurnIdRef.current = null;
|
|
265
|
-
}, [setIsAwaitingLlm, setPreviousResponse,
|
|
270
|
+
}, [setIsAwaitingLlm, setPreviousResponse, selectedNetwork]);
|
|
266
271
|
/*
|
|
267
272
|
* The main logic for sending a query to the server, with retries on errors.
|
|
268
273
|
*/
|
|
@@ -275,7 +280,7 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
275
280
|
// Increment the attempt number and set the state to indicate we're awaiting a response
|
|
276
281
|
attemptNumber += 1;
|
|
277
282
|
// Check which agent type we are dealing with
|
|
278
|
-
if (isLegacyAgentType(
|
|
283
|
+
if (isLegacyAgentType(selectedNetwork)) {
|
|
279
284
|
// It's a legacy agent (these go directly to the LLM and are different from
|
|
280
285
|
// the Neuro-san agents).
|
|
281
286
|
// Send the chat query to the server. This will block until the stream ends from the server
|
|
@@ -284,8 +289,8 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
284
289
|
else {
|
|
285
290
|
// It's a Neuro-san agent.
|
|
286
291
|
// Some coded tools (data generator...) expect the username provided in slyData.
|
|
287
|
-
const
|
|
288
|
-
await sendChatQuery(neuroSanURL, controller?.current.signal, query,
|
|
292
|
+
const slyDataWithUsername = { ...agentChatHistory?.slyData, ...extraSlyData, login: currentUser };
|
|
293
|
+
await sendChatQuery(neuroSanURL, controller?.current.signal, query, selectedNetwork, handleChunk, agentChatHistory.chatContext, slyDataWithUsername, currentUser, StreamingUnit.Line);
|
|
289
294
|
}
|
|
290
295
|
}
|
|
291
296
|
catch (error) {
|
|
@@ -317,7 +322,7 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
317
322
|
handleChunk,
|
|
318
323
|
legacyAgentEndpoint,
|
|
319
324
|
neuroSanURL,
|
|
320
|
-
|
|
325
|
+
selectedNetwork,
|
|
321
326
|
]);
|
|
322
327
|
const getFinalAnswerErrorTurn = () => ({
|
|
323
328
|
id: uuid(),
|
|
@@ -329,7 +334,7 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
329
334
|
// Prefer the most recent matching turn
|
|
330
335
|
const idx = currentTurns.reduceRight((foundIndex, turn, i) => foundIndex !== -1 || extractFinalAnswer(turn.text) === undefined ? foundIndex : i, -1);
|
|
331
336
|
if (idx === -1) {
|
|
332
|
-
if (givesFinalAnswer(
|
|
337
|
+
if (givesFinalAnswer(selectedNetwork)) {
|
|
333
338
|
// This agent is supposed to give final answers, but didn't this time. An error.
|
|
334
339
|
setTurns((prev) => [...prev, getFinalAnswerErrorTurn()]);
|
|
335
340
|
return;
|
|
@@ -342,13 +347,13 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
342
347
|
// Just set the last turn as the final answer
|
|
343
348
|
setTurns((prev) => prev.map((turn) => (turn.id === lastTurn.id ? { ...turn, role: MessageRole.FinalAnswer } : turn)));
|
|
344
349
|
// Save it to chat history
|
|
345
|
-
updateChatHistory(
|
|
350
|
+
updateChatHistory(selectedNetwork, [new AIMessage({ content: lastTurn.text, id: uuid() })]);
|
|
346
351
|
return;
|
|
347
352
|
}
|
|
348
353
|
}
|
|
349
354
|
const sourceTurn = currentTurns[idx];
|
|
350
355
|
// Save item to chat history (same as original behavior)
|
|
351
|
-
updateChatHistory(
|
|
356
|
+
updateChatHistory(selectedNetwork, [new AIMessage({ content: sourceTurn.text, id: uuid() })]);
|
|
352
357
|
// Extract the final answer from the turn.
|
|
353
358
|
const finalAnswer = extractFinalAnswer(sourceTurn.text)?.trim();
|
|
354
359
|
// Update the turn to be a final answer turn, and add a new final answer turn with just the final answer text.
|
|
@@ -365,7 +370,7 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
365
370
|
});
|
|
366
371
|
return updated;
|
|
367
372
|
});
|
|
368
|
-
}, [
|
|
373
|
+
}, [selectedNetwork, updateChatHistory]);
|
|
369
374
|
/**
|
|
370
375
|
* Extract the final answer from the turns for a Neuro-san agent. For Neuro-san agents, we expect the final answer
|
|
371
376
|
* to be the most recent turn messageType === ChatMessageType.AGENT_FRAMEWORK.
|
|
@@ -384,23 +389,16 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
384
389
|
}
|
|
385
390
|
// Extract final answer from that turn
|
|
386
391
|
const finalAnswerTurn = currentTurns[idx];
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
updateChatHistory(targetAgent, [new AIMessage({ content: finalAnswerContent, id: uuid() })]);
|
|
394
|
-
}
|
|
395
|
-
else {
|
|
396
|
-
// No final answer found, display error
|
|
397
|
-
setTurns((prev) => [...prev, getFinalAnswerErrorTurn()]);
|
|
398
|
-
}
|
|
399
|
-
}, [targetAgent, updateChatHistory]);
|
|
392
|
+
// Update relevant turn to be the final answer
|
|
393
|
+
setTurns((prev) => prev.map((turn, i) => (i === idx ? { ...turn, role: MessageRole.FinalAnswer } : turn)));
|
|
394
|
+
// Save final answer to chat history
|
|
395
|
+
const finalAnswerContent = finalAnswerTurn.text || JSON.stringify(finalAnswerTurn.structure, null, 2);
|
|
396
|
+
updateChatHistory(selectedNetwork, [new AIMessage({ content: finalAnswerContent, id: uuid() })]);
|
|
397
|
+
}, [selectedNetwork, updateChatHistory]);
|
|
400
398
|
const handleSend = useCallback(async (query) => {
|
|
401
399
|
// Record user query in chat history. Discard anything beyond MAX_CHAT_HISTORY_ITEMS
|
|
402
400
|
const userQueryMessage = new HumanMessage({ content: query, id: uuid() });
|
|
403
|
-
updateChatHistory(
|
|
401
|
+
updateChatHistory(selectedNetwork, [userQueryMessage]);
|
|
404
402
|
// Allow parent to intercept and modify the query before sending if needed
|
|
405
403
|
const queryToSend = onSend?.(query) ?? query;
|
|
406
404
|
// Save query for "regenerate" use. Again we save the real user input, not the modified query. It will again
|
|
@@ -428,7 +426,7 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
428
426
|
if (!wasAborted) {
|
|
429
427
|
if (succeeded.current) {
|
|
430
428
|
// Success: infer final answer depending on agent type
|
|
431
|
-
if (isLegacyAgentType(
|
|
429
|
+
if (isLegacyAgentType(selectedNetwork)) {
|
|
432
430
|
handleFinalAnswerLegacyAgent();
|
|
433
431
|
}
|
|
434
432
|
else {
|
|
@@ -460,7 +458,7 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
460
458
|
onStreamingStarted,
|
|
461
459
|
resetState,
|
|
462
460
|
setIsAwaitingLlm,
|
|
463
|
-
|
|
461
|
+
selectedNetwork,
|
|
464
462
|
updateChatHistory,
|
|
465
463
|
]);
|
|
466
464
|
const handleStop = useCallback(() => {
|
|
@@ -485,29 +483,34 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
485
483
|
const shouldEnableRegenerateButton = previousUserQuery && !isAwaitingLlm;
|
|
486
484
|
// Enable Clear Chat button if not awaiting response and there is chat output to clear
|
|
487
485
|
const enableClearChatButton = !isAwaitingLlm && (turns.length > 0 || agentChatHistory?.chatHistory?.length > 0);
|
|
488
|
-
const getPlaceholder = () =>
|
|
486
|
+
const getPlaceholder = () => selectedNetwork ? agentPlaceholders[selectedNetwork] || `Chat with ${networkDisplayName}` : null;
|
|
489
487
|
const handleClearChat = useCallback(() => {
|
|
490
488
|
setTurns([]);
|
|
491
|
-
resetHistory(
|
|
489
|
+
resetHistory(selectedNetwork);
|
|
492
490
|
setPreviousUserQuery("");
|
|
493
491
|
currentResponse.current = "";
|
|
494
|
-
}, [resetHistory,
|
|
492
|
+
}, [resetHistory, selectedNetwork]);
|
|
495
493
|
// Expose the handleStop and handleClearChat methods to parent components via ref for external control
|
|
496
494
|
useImperativeHandle(ref, () => ({
|
|
497
495
|
handleStop,
|
|
498
496
|
handleClearChat,
|
|
499
497
|
}), [handleStop, handleClearChat]);
|
|
500
|
-
const
|
|
498
|
+
const getErrorOverlay = (errorText) => (_jsx(Box, { id: "chat-disabled-overlay", sx: {
|
|
499
|
+
position: "absolute",
|
|
500
|
+
top: 0,
|
|
501
|
+
left: 0,
|
|
502
|
+
right: 0,
|
|
503
|
+
bottom: 0,
|
|
504
|
+
zIndex: getZIndex(2, theme),
|
|
505
|
+
cursor: "not-allowed",
|
|
506
|
+
// Capture all pointer events to prevent interaction with the chat when no agent is selected
|
|
507
|
+
pointerEvents: "all",
|
|
508
|
+
}, children: _jsx(Typography, { sx: {
|
|
501
509
|
position: "absolute",
|
|
502
|
-
top:
|
|
503
|
-
left:
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
zIndex: theme.zIndex.modal - 1,
|
|
507
|
-
cursor: "not-allowed",
|
|
508
|
-
// Capture all pointer events to prevent interaction with the chat when no agent is selected
|
|
509
|
-
pointerEvents: "all",
|
|
510
|
-
} }) }));
|
|
510
|
+
top: "50%",
|
|
511
|
+
left: "50%",
|
|
512
|
+
transform: "translate(-50%, -50%)",
|
|
513
|
+
}, children: errorText }) }));
|
|
511
514
|
const getTitle = () => (_jsxs(Box, { id: `llm-chat-title-container-${id}`, sx: {
|
|
512
515
|
alignItems: "center",
|
|
513
516
|
borderTopLeftRadius: "var(--bs-border-radius)",
|
|
@@ -519,15 +522,18 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
519
522
|
paddingTop: "0.25rem",
|
|
520
523
|
paddingBottom: "0.25rem",
|
|
521
524
|
}, children: [_jsx(Typography, { id: `llm-chat-title-${id}-text`, sx: { fontSize: "0.9rem" }, children: title }), onClose && (_jsx(IconButton, { "data-testid": `close-button-${id}`, id: `close-button-${id}`, onClick: onClose, children: _jsx(CloseIcon, { id: `close-icon-${id}` }) }))] }));
|
|
522
|
-
const getOptionsMenuButton = () => (_jsx(Box, { sx: {
|
|
525
|
+
const getOptionsMenuButton = () => (_jsx(Box, { id: `${id}-options-menu-button-container`, sx: {
|
|
526
|
+
backgroundColor: theme.palette.background.paper,
|
|
527
|
+
borderRadius: "var(--bs-border-radius)",
|
|
523
528
|
position: "absolute",
|
|
524
|
-
|
|
525
|
-
|
|
529
|
+
right: theme.spacing(2.5),
|
|
530
|
+
top: theme.spacing(1),
|
|
531
|
+
zIndex: getZIndex(2, theme),
|
|
526
532
|
}, children: _jsx(IconButton, { onClick: (e) => {
|
|
527
533
|
setOptionsMenuAnchorEl(e.currentTarget);
|
|
528
534
|
setOptionsMenuOpen(true);
|
|
529
|
-
}, children: _jsx(TuneIcon, {
|
|
530
|
-
const agentGreeting = customAgentGreetings[
|
|
535
|
+
}, children: _jsx(TuneIcon, { fontSize: "small" }) }) }));
|
|
536
|
+
const agentGreeting = customAgentGreetings[selectedNetwork] ?? "Hi, how can I help?";
|
|
531
537
|
const handleOptionsMenuClose = () => {
|
|
532
538
|
setOptionsMenuAnchorEl(null);
|
|
533
539
|
setOptionsMenuOpen(false);
|
|
@@ -549,7 +555,22 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
549
555
|
},
|
|
550
556
|
},
|
|
551
557
|
}, children: [_jsxs(MenuItem, { onClick: handleToggleAutoScroll, children: [_jsx(ListItemIcon, { children: _jsx(Checkbox, { checked: autoScrollEnabled, tabIndex: -1, sx: { pointerEvents: "none" } }) }), _jsx(ListItemText, { primary: "Auto-scroll output" })] }), _jsxs(MenuItem, { onClick: handleToggleWrapOutput, children: [_jsx(ListItemIcon, { children: _jsx(Checkbox, { checked: shouldWrapOutput, tabIndex: -1, sx: { pointerEvents: "none" } }) }), _jsx(ListItemText, { primary: "Wrap output" })] })] }));
|
|
552
|
-
const
|
|
558
|
+
const getNetworkIntro = () => (_jsxs(Box, { sx: { marginBottom: "0.5rem", marginTop: "1rem" }, children: [_jsxs(Typography, { component: "span", sx: { fontWeight: 700 }, variant: "inherit", children: [networkDisplayName, networkDescription && ":"] }), networkDescription && (_jsxs(Typography, { component: "span", sx: { ml: 0.5 }, variant: "inherit", children: [" ", networkDescription] }))] }));
|
|
559
|
+
const getChatHistory = () => (_jsx(_Fragment, { children: agentChatHistory?.chatHistory?.length > 0 && (_jsx(Box, { sx: { my: "0.5rem" }, children: _jsx(ChatHistory, { id: id, messages: agentChatHistory.chatHistory }) })) }));
|
|
560
|
+
const getAgentGreeting = () => _jsx(Box, { sx: { marginBottom: "0.5rem", marginTop: "1rem" }, children: agentGreeting });
|
|
561
|
+
const getControlButtons = () => (_jsx(Box, { sx: {
|
|
562
|
+
bottom: theme.spacing(3),
|
|
563
|
+
right: theme.spacing(3),
|
|
564
|
+
position: "absolute",
|
|
565
|
+
zIndex: getZIndex(2, theme),
|
|
566
|
+
}, children: _jsx(ControlButtons, { enableClearChatButton: enableClearChatButton, handleClearChat: handleClearChat, handleSend: handleSend, handleStop: handleStop, isAwaitingLlm: isAwaitingLlm, previousUserQuery: previousUserQuery, shouldEnableRegenerateButton: shouldEnableRegenerateButton }) }));
|
|
567
|
+
const getWorkingSpinner = () => (_jsx(_Fragment, { children: isAwaitingLlm && (_jsxs(Box, { id: "awaitingOutputContainer", sx: { display: "flex", alignItems: "center", fontSize: "smaller" }, children: [_jsx("span", { id: "working-span", style: { marginRight: "1rem" }, children: "Working..." }), _jsx(CircularProgress, { id: "awaitingOutputSpinner", sx: {
|
|
568
|
+
color: "var(--bs-primary)",
|
|
569
|
+
}, size: "1rem" })] })) }));
|
|
570
|
+
const getThinking = () => (_jsx(_Fragment, { children: !isAwaitingLlm && turns.length > 0 && (
|
|
571
|
+
// Only show thinking once streaming is complete
|
|
572
|
+
_jsx(Thinking, { id: id, turns: turns, useNativeNames: useNativeNames })) }));
|
|
573
|
+
const getResponseBox = () => (_jsx(Box, { id: "llm-response-div", sx: {
|
|
553
574
|
...divStyle,
|
|
554
575
|
border: "var(--bs-border-width) var(--bs-border-style)",
|
|
555
576
|
borderRadius: "var(--bs-border-radius)",
|
|
@@ -558,27 +579,23 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
558
579
|
marginLeft: "10px",
|
|
559
580
|
position: "relative",
|
|
560
581
|
overflowY: "auto",
|
|
561
|
-
}, children:
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
// Only show thinking once streaming is complete
|
|
574
|
-
_jsx(Thinking, { id: id, turns: turns })), isAwaitingLlm && (_jsxs(Box, { id: "awaitingOutputContainer", sx: { display: "flex", alignItems: "center", fontSize: "smaller" }, children: [_jsx("span", { id: "working-span", style: { marginRight: "1rem" }, children: "Working..." }), _jsx(CircularProgress, { id: "awaitingOutputSpinner", sx: {
|
|
575
|
-
color: "var(--bs-primary)",
|
|
576
|
-
}, size: "1rem" })] }))] }), _jsx(ControlButtons, { enableClearChatButton: enableClearChatButton, handleClearChat: handleClearChat, handleSend: handleSend, handleStop: handleStop, isAwaitingLlm: isAwaitingLlm, previousUserQuery: previousUserQuery, shouldEnableRegenerateButton: shouldEnableRegenerateButton })] }));
|
|
582
|
+
}, children: _jsxs(Box, { id: "llm-responses", ref: chatOutputRef, sx: {
|
|
583
|
+
backgroundColor,
|
|
584
|
+
borderRadius: "0.5rem",
|
|
585
|
+
fontSize: "16px",
|
|
586
|
+
overflowY: "auto",
|
|
587
|
+
paddingBottom: "60px",
|
|
588
|
+
paddingLeft: "15px",
|
|
589
|
+
paddingRight: "15px",
|
|
590
|
+
paddingTop: "7.5px",
|
|
591
|
+
scrollbarGutter: "stable",
|
|
592
|
+
width: "100%",
|
|
593
|
+
}, tabIndex: -1, children: [getOptionsMenu(), getOptionsMenuButton(), getChatHistory(), getNetworkIntro(), getAgentGreeting(), _jsx(SampleQueries, { disabled: isAwaitingLlm, handleSend: handleSend, sampleQueries: sampleQueries }), _jsx(Conversation, { id: id, includeAgentMessages: !givesFinalAnswer(selectedNetwork), shouldWrapOutput: shouldWrapOutput, turns: turns }), getThinking(), getWorkingSpinner(), getControlButtons()] }) }));
|
|
577
594
|
const getUserInputBox = () => (_jsxs(Box, { id: "user-input-div", sx: {
|
|
578
595
|
...divStyle,
|
|
596
|
+
alignItems: "center",
|
|
579
597
|
display: "flex",
|
|
580
598
|
margin: "10px",
|
|
581
|
-
alignItems: "flex-end",
|
|
582
599
|
position: "relative",
|
|
583
600
|
}, children: [_jsx(Input, { autoComplete: "off", id: "user-input", multiline: true, placeholder: getPlaceholder(), ref: chatInputRef, sx: {
|
|
584
601
|
border: "var(--bs-border-style) var(--bs-border-width) var(--bs-gray-light)",
|
|
@@ -607,21 +624,33 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
607
624
|
}, sx: {
|
|
608
625
|
color: "var(--bs-primary)",
|
|
609
626
|
opacity: userInputEmpty ? "25%" : "100%",
|
|
610
|
-
}, disabled: userInputEmpty, tabIndex: -1, edge: "end", children: _jsx(ClearIcon, { id: "clear-input-icon" }) })] }) }), _jsx(MicrophoneButton, { isMicOn: isMicOn, onMicToggle: setIsMicOn, speechRecognitionRef: speechRecognitionRef, voiceInputState: voiceInputState
|
|
627
|
+
}, disabled: userInputEmpty, tabIndex: -1, edge: "end", children: _jsx(ClearIcon, { id: "clear-input-icon" }) })] }) }), _jsxs(Box, { sx: { display: "flex", gap: "0.25rem", position: "relative", alignItems: "center" }, children: [_jsx(MicrophoneButton, { isMicOn: isMicOn, onMicToggle: setIsMicOn, setVoiceInputState: setVoiceInputState, speechRecognitionRef: speechRecognitionRef, voiceInputState: voiceInputState }), _jsx(SendButton, { enableSendButton: shouldEnableSendButton, id: "submit-query-button", onClickCallback: () => handleSend(chatInput) })] })] }));
|
|
611
628
|
const getChatBox = () => (_jsxs(Box, { id: `llm-chat-${id}`, sx: {
|
|
612
629
|
display: "flex",
|
|
613
630
|
flexDirection: "column",
|
|
614
631
|
flexGrow: 1,
|
|
615
632
|
height: "100%",
|
|
616
|
-
opacity:
|
|
617
|
-
pointerEvents:
|
|
633
|
+
opacity: selectedNetwork ? 1 : 0.4,
|
|
634
|
+
pointerEvents: selectedNetwork ? "auto" : "none",
|
|
618
635
|
position: "relative",
|
|
619
636
|
}, children: [title && getTitle(), getResponseBox(), getUserInputBox()] }));
|
|
637
|
+
const getSelectNetworkOverlayBody = () => (_jsxs(Typography, { component: "span", children: ["Please select a Network from the list to start the chat, or click", _jsx(IconButton, { onClick: () => setSelectedNetwork?.(AGENT_NETWORK_DESIGNER_ID), "aria-label": "select-network-designer", sx: {
|
|
638
|
+
px: 0.5,
|
|
639
|
+
verticalAlign: "middle",
|
|
640
|
+
mb: "4px",
|
|
641
|
+
}, children: _jsx(AddBoxRounded, { sx: {
|
|
642
|
+
color: "var(--bs-secondary)",
|
|
643
|
+
fontSize: "1rem",
|
|
644
|
+
} }) }), "to design your own network!"] }));
|
|
645
|
+
const allApiKeysPresent = missingApiKeys?.length === 0;
|
|
646
|
+
const getMissingApiKeysOverlayBody = () => (_jsx(Typography, { component: "span", children: `API key(s) required for: ${missingApiKeys.join(", ")}. Please add the required key(s) in
|
|
647
|
+
"Settings" to use this Network.` }));
|
|
620
648
|
return (_jsx(Box, { id: `llm-chat-${id}`, sx: {
|
|
621
|
-
display: "flex",
|
|
622
|
-
flexDirection: "column",
|
|
623
|
-
flexGrow: 1,
|
|
624
649
|
height: "100%",
|
|
625
650
|
position: "relative",
|
|
626
|
-
}, children:
|
|
651
|
+
}, children: selectedNetwork
|
|
652
|
+
? allApiKeysPresent
|
|
653
|
+
? getChatBox()
|
|
654
|
+
: getErrorOverlay(getMissingApiKeysOverlayBody())
|
|
655
|
+
: getErrorOverlay(getSelectNetworkOverlayBody()) }));
|
|
627
656
|
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const MAX_TURNS = 50;
|
|
@@ -9,7 +9,7 @@ interface ControlButtonsProps {
|
|
|
9
9
|
shouldEnableRegenerateButton: boolean;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
* Generate the Control Buttons for a chat window
|
|
12
|
+
* Generate the Control Buttons for a chat window (resend, clear chat, stop)
|
|
13
13
|
* @returns A fragment containing the Control Buttons.
|
|
14
14
|
*/
|
|
15
15
|
export declare const ControlButtons: FC<ControlButtonsProps>;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { jsx as _jsx,
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
/*
|
|
3
3
|
Copyright 2025 Cognizant Technology Solutions Corp, www.cognizant.com.
|
|
4
4
|
|
|
@@ -17,10 +17,13 @@ limitations under the License.
|
|
|
17
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
|
+
import Box from "@mui/material/Box";
|
|
20
21
|
import { SmallLlmChatButton } from "../Common/LlmChatButton.js";
|
|
21
|
-
|
|
22
|
+
//#endregion: Types
|
|
22
23
|
/**
|
|
23
|
-
* Generate the Control Buttons for a chat window
|
|
24
|
+
* Generate the Control Buttons for a chat window (resend, clear chat, stop)
|
|
24
25
|
* @returns A fragment containing the Control Buttons.
|
|
25
26
|
*/
|
|
26
|
-
export const ControlButtons = ({ handleClearChat, enableClearChatButton, isAwaitingLlm, handleSend, handleStop, previousUserQuery, shouldEnableRegenerateButton, }) => (
|
|
27
|
+
export const ControlButtons = ({ handleClearChat, enableClearChatButton, isAwaitingLlm, handleSend, handleStop, previousUserQuery, shouldEnableRegenerateButton, }) => (_jsx(_Fragment, { children: isAwaitingLlm ? (
|
|
28
|
+
// Stop Button
|
|
29
|
+
_jsx(SmallLlmChatButton, { "aria-label": "Stop", disabled: !isAwaitingLlm, id: "stop-output-button", onClick: () => handleStop(), children: _jsx(StopCircle, { fontSize: "small", id: "stop-button-icon" }) })) : (_jsxs(Box, { sx: { display: "flex", gap: 1 }, children: [_jsx(SmallLlmChatButton, { "aria-label": "Clear Chat", disabled: !enableClearChatButton, id: "clear-chat-button", onClick: handleClearChat, children: _jsx(DeleteOutlined, { fontSize: "small", id: "stop-button-icon" }) }), _jsx(SmallLlmChatButton, { "aria-label": "Regenerate", disabled: !shouldEnableRegenerateButton, id: "regenerate-output-button", onClick: () => handleSend(previousUserQuery), children: _jsx(Loop, { fontSize: "small", id: "generate-icon" }) })] })) }));
|
|
@@ -53,6 +53,9 @@ export const FormattedMarkdown = ({ id, nodesList, style, wrapLongLines = false,
|
|
|
53
53
|
// If not, do our best to synthesize a key
|
|
54
54
|
return `${hashString(node?.toString() || "")}-${index}`;
|
|
55
55
|
};
|
|
56
|
+
if (!nodesList || nodesList.length === 0) {
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
56
59
|
// Walk through the nodes list. If we encounter a string node, we'll aggregate it with other string nodes.
|
|
57
60
|
const formattedOutput = [];
|
|
58
61
|
let currentTextNodes = [];
|