@cognizant-ai-lab/ui-common 1.4.1 → 1.4.2
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/components/MultiAgentAccelerator/MultiAgentAccelerator.js +11 -30
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +1 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +21 -2
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +5 -0
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +7 -0
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +17 -2
- package/dist/components/MultiAgentAccelerator/const.d.ts +2 -0
- package/dist/components/MultiAgentAccelerator/const.js +4 -0
- package/dist/state/TemporaryNetworks.d.ts +1 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/File.d.ts +29 -0
- package/dist/utils/File.js +61 -0
- package/package.json +9 -10
|
@@ -16,7 +16,6 @@ limitations under the License.
|
|
|
16
16
|
*/
|
|
17
17
|
import StopCircle from "@mui/icons-material/StopCircle";
|
|
18
18
|
import Box from "@mui/material/Box";
|
|
19
|
-
import Collapse from "@mui/material/Collapse";
|
|
20
19
|
import Grid from "@mui/material/Grid";
|
|
21
20
|
import Slide from "@mui/material/Slide";
|
|
22
21
|
import { ReactFlowProvider } from "@xyflow/react";
|
|
@@ -26,7 +25,7 @@ import { getUpdatedAgentCounts } from "./AgentCounts.js";
|
|
|
26
25
|
import { AgentFlow } from "./AgentFlow.js";
|
|
27
26
|
import { TEMPORARY_NETWORK_FOLDER } from "./const.js";
|
|
28
27
|
import { Sidebar } from "./Sidebar/Sidebar.js";
|
|
29
|
-
import { extractReservations } from "./TemporaryNetworks.js";
|
|
28
|
+
import { extractNetworkHocon, extractReservations } from "./TemporaryNetworks.js";
|
|
30
29
|
import { getAgentIconSuggestions, getAgentNetworks, getConnectivity, getNetworkIconSuggestions, } from "../../controller/agent/Agent.js";
|
|
31
30
|
import { useSettingsStore } from "../../state/Settings.js";
|
|
32
31
|
import { useTempNetworksStore } from "../../state/TemporaryNetworks.js";
|
|
@@ -35,7 +34,6 @@ import { ChatCommon } from "../AgentChat/ChatCommon.js";
|
|
|
35
34
|
import { SmallLlmChatButton } from "../AgentChat/LlmChatButton.js";
|
|
36
35
|
import { chatMessageFromChunk, cleanUpAgentName, removeTrailingUuid } from "../AgentChat/Utils.js";
|
|
37
36
|
import { ConfirmationModal } from "../Common/ConfirmationModal.js";
|
|
38
|
-
import { MUIAlert } from "../Common/MUIAlert.js";
|
|
39
37
|
import { closeNotification, NotificationType, sendNotification } from "../Common/notification.js";
|
|
40
38
|
// Display expired temporary networks for this amount of time after they expire so users can see what happened
|
|
41
39
|
const GRACE_PERIOD_MS = 5 * 60 * 1000; // 5 minutes
|
|
@@ -45,9 +43,13 @@ const GROW_ANIMATION_TIME_MS = 800;
|
|
|
45
43
|
* Helper function to convert agent reservations received from the backend into temporary networks that can be displayed
|
|
46
44
|
* in the tree.
|
|
47
45
|
* @param agentReservations List of "agent reservations" (temporary networks) received from the backend
|
|
46
|
+
* @param networkHocon Optional network HOCON string that may be included in the same message as the
|
|
47
|
+
* reservations. Note: for now we assume that all reservations are associated with the same network definition.
|
|
48
|
+
* This will fail if ever we get multiple reservations for different networks in a single chat stream, but that is
|
|
49
|
+
* not a valid scenario currently; we are focusing on Agent Network Design which has a simple output.
|
|
48
50
|
* @returns List of TemporaryNetwork objects that can be displayed in the UI
|
|
49
51
|
*/
|
|
50
|
-
const convertReservationsToNetworks = (agentReservations) => {
|
|
52
|
+
const convertReservationsToNetworks = (agentReservations, networkHocon) => {
|
|
51
53
|
return agentReservations.map((reservation) => ({
|
|
52
54
|
reservation,
|
|
53
55
|
agentInfo: {
|
|
@@ -55,6 +57,7 @@ const convertReservationsToNetworks = (agentReservations) => {
|
|
|
55
57
|
origin: reservation.reservation_id,
|
|
56
58
|
status: "active",
|
|
57
59
|
},
|
|
60
|
+
networkHocon,
|
|
58
61
|
}));
|
|
59
62
|
};
|
|
60
63
|
/**
|
|
@@ -90,8 +93,6 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
90
93
|
const [networkToBeDeleted, setNetworkToBeDeleted] = useState(null);
|
|
91
94
|
// State to hold thought bubble edges - avoids duplicates across layout recalculations
|
|
92
95
|
const [thoughtBubbleEdges, setThoughtBubbleEdges] = useState(new Map());
|
|
93
|
-
// For controlling alert when temporary network is created
|
|
94
|
-
const [alertContents, setAlertContents] = useState(null);
|
|
95
96
|
const customURLCallback = useCallback((url) => {
|
|
96
97
|
setNeuroSanURL(url || backendNeuroSanApiUrl);
|
|
97
98
|
setCustomURLLocalStorage(url === "" ? null : url);
|
|
@@ -244,7 +245,9 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
244
245
|
const reservationsResult = extractReservations(chatMessage);
|
|
245
246
|
// Handle agent reservations (temporary networks) that come in through the chat stream.
|
|
246
247
|
if (reservationsResult?.length > 0) {
|
|
247
|
-
|
|
248
|
+
// Retrieve network definition, if present
|
|
249
|
+
const networkHocon = extractNetworkHocon(chatMessage);
|
|
250
|
+
const newTemporaryNetworks = convertReservationsToNetworks(reservationsResult, networkHocon);
|
|
248
251
|
const currentNetworks = useTempNetworksStore.getState().tempNetworks;
|
|
249
252
|
useTempNetworksStore.getState().setTempNetworks([...currentNetworks, ...newTemporaryNetworks]);
|
|
250
253
|
// record the new temporary networks so we can select them for the user. For now, we only
|
|
@@ -258,7 +261,6 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
258
261
|
agentCountsRef.current = new Map();
|
|
259
262
|
// Reset newly added temporary networks
|
|
260
263
|
setNewlyAddedTemporaryNetworks(new Set());
|
|
261
|
-
setAlertContents(null);
|
|
262
264
|
// Show info popup only once per session
|
|
263
265
|
if (!haveShownPopup) {
|
|
264
266
|
sendNotification(NotificationType.info, "Agents working", "Click the stop button or hit Escape to exit.");
|
|
@@ -268,31 +270,11 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
268
270
|
setIsStreaming(true);
|
|
269
271
|
}, [haveShownPopup]);
|
|
270
272
|
const onStreamingComplete = useCallback(() => {
|
|
271
|
-
const network = newlyAddedTemporaryNetworks?.values().next().value;
|
|
272
|
-
if (network?.length > 0) {
|
|
273
|
-
// We show an Alert after streaming completes (in case of Zen mode where the user might miss it)
|
|
274
|
-
const agentNameDisplay = cleanUpAgentName(removeTrailingUuid(network));
|
|
275
|
-
setAlertContents(`A temporary network "${agentNameDisplay}" has been created.`);
|
|
276
|
-
}
|
|
277
273
|
// When streaming is complete, clean up any refs and state
|
|
278
274
|
conversationsRef.current = null;
|
|
279
275
|
setCurrentConversations(null);
|
|
280
276
|
resetState();
|
|
281
277
|
}, [newlyAddedTemporaryNetworks]);
|
|
282
|
-
useEffect(() => {
|
|
283
|
-
if (alertContents?.length > 0) {
|
|
284
|
-
// Set a timer to clear the alert after a few seconds so it doesn't overstay its welcome
|
|
285
|
-
const timeoutId = window.setTimeout(() => {
|
|
286
|
-
setAlertContents(null);
|
|
287
|
-
}, 10_000);
|
|
288
|
-
return () => {
|
|
289
|
-
window.clearTimeout(timeoutId);
|
|
290
|
-
};
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
return undefined;
|
|
294
|
-
}
|
|
295
|
-
}, [alertContents]);
|
|
296
278
|
const [confirmationModalOpen, setConfirmationModalOpen] = useState(false);
|
|
297
279
|
const handleDeleteNetwork = (networkId, isExpired) => {
|
|
298
280
|
if (isExpired) {
|
|
@@ -354,8 +336,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
354
336
|
setNetworkToBeDeleted(null);
|
|
355
337
|
setConfirmationModalOpen(false);
|
|
356
338
|
}, title: "Delete Network" })) : null;
|
|
357
|
-
|
|
358
|
-
return (_jsxs(_Fragment, { children: [getAlert(), getConfirmationModal(), _jsxs(Grid, { id: "multi-agent-accelerator-grid", container: true, columns: 18, sx: {
|
|
339
|
+
return (_jsxs(_Fragment, { children: [getConfirmationModal(), _jsxs(Grid, { id: "multi-agent-accelerator-grid", container: true, columns: 18, sx: {
|
|
359
340
|
display: "flex",
|
|
360
341
|
flex: 1,
|
|
361
342
|
height: "85%",
|
|
@@ -6,6 +6,7 @@ export interface AgentNetworkNodeProps extends TreeItemProps {
|
|
|
6
6
|
readonly onDeleteNetwork?: (network: string, isExpired: boolean) => void;
|
|
7
7
|
readonly networkIconSuggestions: Record<string, string>;
|
|
8
8
|
readonly temporaryNetworkExpirationTimes?: Record<string, Date>;
|
|
9
|
+
readonly temporaryNetworkHoconStrings?: Record<string, string | null>;
|
|
9
10
|
}
|
|
10
11
|
/**
|
|
11
12
|
* Custom Tree Item for MUI RichTreeView to display agent networks with tags
|
|
@@ -4,14 +4,17 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
4
4
|
import * as MuiIcons from "@mui/icons-material";
|
|
5
5
|
import BookmarkIcon from "@mui/icons-material/Bookmark";
|
|
6
6
|
import Delete from "@mui/icons-material/Delete";
|
|
7
|
+
import DownloadIcon from "@mui/icons-material/Download";
|
|
7
8
|
import Box from "@mui/material/Box";
|
|
8
9
|
import Chip from "@mui/material/Chip";
|
|
10
|
+
import IconButton from "@mui/material/IconButton";
|
|
9
11
|
import { useTheme } from "@mui/material/styles";
|
|
10
12
|
import Tooltip from "@mui/material/Tooltip";
|
|
11
13
|
import { TreeItemContent, TreeItemGroupTransition, TreeItemLabel, TreeItemRoot, } from "@mui/x-tree-view/TreeItem";
|
|
12
14
|
import { TreeItemProvider } from "@mui/x-tree-view/TreeItemProvider";
|
|
13
15
|
import { useTreeItem } from "@mui/x-tree-view/useTreeItem";
|
|
14
16
|
import { useRef } from "react";
|
|
17
|
+
import { downloadFile, toSafeFilename } from "../../../utils/File.js";
|
|
15
18
|
import { cleanUpAgentName } from "../../AgentChat/Utils.js";
|
|
16
19
|
// Palette of colors we can use for tags
|
|
17
20
|
const TAG_COLORS = [
|
|
@@ -40,7 +43,7 @@ const isTemporaryNetworkExpired = (expirationDate) => {
|
|
|
40
43
|
* @param props - see AgentNetworkNode interface
|
|
41
44
|
* @returns JSX.Element containing the custom tree item
|
|
42
45
|
*/
|
|
43
|
-
export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networkIconSuggestions, nodeIndex, onDeleteNetwork, temporaryNetworkExpirationTimes, }) => {
|
|
46
|
+
export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networkIconSuggestions, nodeIndex, onDeleteNetwork, temporaryNetworkExpirationTimes, temporaryNetworkHoconStrings, }) => {
|
|
44
47
|
const theme = useTheme();
|
|
45
48
|
// We know all labels are strings because we set them that way in the tree view items
|
|
46
49
|
const labelString = label;
|
|
@@ -66,6 +69,7 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networ
|
|
|
66
69
|
const expirationTime = temporaryNetworkExpirationTimes?.[itemId];
|
|
67
70
|
const isTemporaryNetwork = Boolean(expirationTime);
|
|
68
71
|
const isExpired = isChild && isTemporaryNetwork && isTemporaryNetworkExpired(expirationTime);
|
|
72
|
+
const networkHocon = isTemporaryNetwork ? temporaryNetworkHoconStrings?.[itemId] : null;
|
|
69
73
|
const iconNameSuggestion = isTemporaryNetwork ? "HourglassTop" : isChild ? networkIconSuggestions?.[itemId] : null;
|
|
70
74
|
let muiIconElement = null;
|
|
71
75
|
if (iconNameSuggestion && MuiIcons[iconNameSuggestion]) {
|
|
@@ -93,7 +97,22 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networ
|
|
|
93
97
|
.map((tag) => (_jsx(Chip, { label: tag, style: {
|
|
94
98
|
margin: "0.25rem",
|
|
95
99
|
backgroundColor: `var(${tagsToColors.get(tag) || TAG_COLORS[0]})`,
|
|
96
|
-
} }, tag))), placement: "right", arrow: true, children: _jsx(BookmarkIcon, { sx: { fontSize: "0.75rem", color: "var(--bs-accent1-medium)" } }) })) : null
|
|
100
|
+
} }, tag))), placement: "right", arrow: true, children: _jsx(BookmarkIcon, { sx: { fontSize: "0.75rem", color: "var(--bs-accent1-medium)" } }) })) : null, isTemporaryNetwork && networkHocon && (_jsx(Tooltip, { title: isExpired ? "Network expired" : "Download network definition", children: _jsx("span", { children: _jsx(IconButton, { onClick: (e) => {
|
|
101
|
+
e.stopPropagation();
|
|
102
|
+
if (isExpired) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const fileName = `${toSafeFilename(labelString)}.hocon`;
|
|
106
|
+
downloadFile(networkHocon, fileName);
|
|
107
|
+
}, disabled: isExpired, "aria-label": "Download network definition", size: "small", sx: {
|
|
108
|
+
padding: 0,
|
|
109
|
+
color: "var(--bs-secondary)",
|
|
110
|
+
"&:hover": { color: "var(--bs-secondary-dark)" },
|
|
111
|
+
"&.Mui-disabled": {
|
|
112
|
+
color: "var(--bs-secondary)",
|
|
113
|
+
opacity: 0.3,
|
|
114
|
+
},
|
|
115
|
+
}, children: _jsx(DownloadIcon, { sx: { fontSize: "0.75rem" } }) }) }) }))] }), isChild && isTemporaryNetwork && (_jsx(Tooltip, { title: "Delete network", children: _jsx(Delete, { onClick: (e) => {
|
|
97
116
|
e.stopPropagation();
|
|
98
117
|
onDeleteNetwork?.(itemId, isExpired);
|
|
99
118
|
}, sx: {
|
|
@@ -203,6 +203,10 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
|
|
|
203
203
|
acc[tempNetwork.agentInfo.agent_name] = new Date(tempNetwork.reservation.expiration_time_in_seconds * 1000);
|
|
204
204
|
return acc;
|
|
205
205
|
}, {});
|
|
206
|
+
const temporaryNetworkHoconStrings = temporaryNetworks.reduce((acc, tempNetwork) => {
|
|
207
|
+
acc[tempNetwork.agentInfo.agent_name] = tempNetwork.networkHocon;
|
|
208
|
+
return acc;
|
|
209
|
+
}, {});
|
|
206
210
|
const [selectedItem, setSelectedItem] = useState(null);
|
|
207
211
|
const handleSelectedItemsChange = (_event, itemId) => {
|
|
208
212
|
if (!itemId) {
|
|
@@ -263,6 +267,7 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
|
|
|
263
267
|
nodeIndex,
|
|
264
268
|
onDeleteNetwork,
|
|
265
269
|
temporaryNetworkExpirationTimes,
|
|
270
|
+
temporaryNetworkHoconStrings,
|
|
266
271
|
},
|
|
267
272
|
} }, Object.keys(networkIconSuggestions || {}).length)] }), _jsxs(Popover, { id: "agent-network-settings-popover", open: settingsPopoverOpen, anchorEl: settingsAnchorEl, onClose: () => handleSettingsClose(true), anchorOrigin: {
|
|
268
273
|
vertical: "bottom",
|
|
@@ -24,3 +24,10 @@ export type AgentReservation = {
|
|
|
24
24
|
* if the message is not of the expected type.
|
|
25
25
|
*/
|
|
26
26
|
export declare const extractReservations: (message: ChatMessage) => AgentReservation[];
|
|
27
|
+
/**
|
|
28
|
+
* Extracts the agent network HOCON from a chat message, if it exists.
|
|
29
|
+
* @param message The chat message to extract network HOCON from.
|
|
30
|
+
* We expect the network definition to be present in messages of type AGENT_FRAMEWORK only.
|
|
31
|
+
* @return The network HOCON as a string, or null if not found or not the right type of message.
|
|
32
|
+
*/
|
|
33
|
+
export declare const extractNetworkHocon: (message: ChatMessage) => string | null;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
|
+
import { AGENT_NETWORK_HOCON, AGENT_RESERVATIONS_KEY } from "./const.js";
|
|
1
2
|
import { ChatMessageType } from "../../generated/neuro-san/NeuroSanClient.js";
|
|
2
|
-
// We expect the agent reservations to be stored in sly_data under this key
|
|
3
|
-
const AGENT_RESERVATIONS_KEY = "agent_reservations";
|
|
4
3
|
/**
|
|
5
4
|
* Extracts agent reservations from a chat message, if they exist.
|
|
6
5
|
* @param message The chat message to extract reservations from. We expect reservations to be present in messages of
|
|
@@ -18,3 +17,19 @@ export const extractReservations = (message) => {
|
|
|
18
17
|
return [];
|
|
19
18
|
}
|
|
20
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Extracts the agent network HOCON from a chat message, if it exists.
|
|
22
|
+
* @param message The chat message to extract network HOCON from.
|
|
23
|
+
* We expect the network definition to be present in messages of type AGENT_FRAMEWORK only.
|
|
24
|
+
* @return The network HOCON as a string, or null if not found or not the right type of message.
|
|
25
|
+
*/
|
|
26
|
+
export const extractNetworkHocon = (message) => {
|
|
27
|
+
// Check for agent network HOCON in sly_data
|
|
28
|
+
if (message?.type === ChatMessageType.AGENT_FRAMEWORK && message?.sly_data?.[AGENT_NETWORK_HOCON]) {
|
|
29
|
+
return message.sly_data[AGENT_NETWORK_HOCON];
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Not the type of message that would contain a network HOCON, or no network HOCON found, return null
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
@@ -3,3 +3,5 @@ export declare const DEFAULT_FRONTMAN_Y_POS = 450;
|
|
|
3
3
|
export declare const BASE_RADIUS = 100;
|
|
4
4
|
export declare const LEVEL_SPACING = 150;
|
|
5
5
|
export declare const TEMPORARY_NETWORK_FOLDER = "temporary";
|
|
6
|
+
export declare const AGENT_RESERVATIONS_KEY = "agent_reservations";
|
|
7
|
+
export declare const AGENT_NETWORK_HOCON = "agent_network_hocon_text";
|
|
@@ -23,3 +23,7 @@ export const LEVEL_SPACING = 150;
|
|
|
23
23
|
// they come from the backend, but we need to put them somewhere in the UI, and this makes it clear that they're
|
|
24
24
|
// temporary.
|
|
25
25
|
export const TEMPORARY_NETWORK_FOLDER = "temporary";
|
|
26
|
+
// We expect the agent reservations to be stored in sly_data under this key
|
|
27
|
+
export const AGENT_RESERVATIONS_KEY = "agent_reservations";
|
|
28
|
+
// We expect the agent network HOCON to be stored in sly_data under this key, if it is provided by the backend
|
|
29
|
+
export const AGENT_NETWORK_HOCON = "agent_network_hocon_text";
|
|
@@ -7,6 +7,7 @@ type AgentReservation = {
|
|
|
7
7
|
export type TemporaryNetwork = {
|
|
8
8
|
readonly reservation: AgentReservation;
|
|
9
9
|
readonly agentInfo: AgentInfo;
|
|
10
|
+
readonly networkHocon?: string | null;
|
|
10
11
|
};
|
|
11
12
|
/**
|
|
12
13
|
* Zustand state store for temporary networks, such as vibe coded networks created by the user.
|