@cognizant-ai-lab/ui-common 1.9.0 → 1.10.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 (28) hide show
  1. package/dist/components/AgentChat/ChatCommon/ChatCommon.js +5 -4
  2. package/dist/components/AgentChat/ChatCommon/Thinking.js +2 -2
  3. package/dist/components/AgentChat/Common/Utils.d.ts +0 -11
  4. package/dist/components/AgentChat/Common/Utils.js +0 -16
  5. package/dist/components/Common/Footer.js +4 -0
  6. package/dist/components/MultiAgentAccelerator/AgentFlow/AgentFlow.js +56 -24
  7. package/dist/components/MultiAgentAccelerator/AgentFlow/AgentNode.js +7 -5
  8. package/dist/components/MultiAgentAccelerator/AgentFlow/GraphLayouts.js +10 -7
  9. package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.d.ts +1 -1
  10. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +9 -56
  11. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +2 -0
  12. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +23 -31
  13. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +0 -2
  14. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +7 -6
  15. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +2 -2
  16. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +13 -13
  17. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +3 -17
  18. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +17 -40
  19. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +2 -2
  20. package/dist/components/MultiAgentAccelerator/const.d.ts +11 -11
  21. package/dist/components/MultiAgentAccelerator/const.js +22 -22
  22. package/dist/state/TemporaryNetworks.js +12 -21
  23. package/dist/tsconfig.build.tsbuildinfo +1 -1
  24. package/dist/utils/AgentName.d.ts +17 -0
  25. package/dist/utils/AgentName.js +46 -0
  26. package/package.json +1 -1
  27. package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.d.ts +0 -67
  28. package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.js +0 -585
@@ -9,14 +9,18 @@ import Edit from "@mui/icons-material/Edit";
9
9
  import Box from "@mui/material/Box";
10
10
  import Chip from "@mui/material/Chip";
11
11
  import IconButton from "@mui/material/IconButton";
12
+ import { styled } from "@mui/material/styles";
12
13
  import Tooltip from "@mui/material/Tooltip";
13
14
  import { useTreeItemModel } from "@mui/x-tree-view/hooks";
14
15
  import { TreeItemContent, TreeItemGroupTransition, TreeItemLabel, TreeItemRoot, } from "@mui/x-tree-view/TreeItem";
15
16
  import { TreeItemProvider } from "@mui/x-tree-view/TreeItemProvider";
16
17
  import { useTreeItem } from "@mui/x-tree-view/useTreeItem";
17
18
  import { useRef } from "react";
18
- import { downloadFile, toSafeFilename } from "../../../utils/File.js";
19
- import { removeTrailingUuid } from "../../AgentChat/Common/Utils.js";
19
+ import { downloadFile } from "../../../utils/File.js";
20
+ //#region Constants
21
+ // Accessible names shared between each action's tooltip title and its aria-label.
22
+ export const EDIT_NETWORK_LABEL = "Edit this network";
23
+ export const DOWNLOAD_NETWORK_DEFINITION_LABEL = "Download network definition";
20
24
  // Palette of colors we can use for tags
21
25
  const TAG_COLORS = [
22
26
  "--bs-accent2-light",
@@ -29,6 +33,17 @@ const TAG_COLORS = [
29
33
  "--bs-secondary",
30
34
  "--bs-yellow",
31
35
  ];
36
+ // Shared styling for the row's action buttons (download, edit, ...). Disabled state keeps the base
37
+ // color and only dims via opacity.
38
+ const ActionIconButton = styled(IconButton)({
39
+ padding: 0,
40
+ color: "var(--bs-secondary)",
41
+ "&:hover": { color: "var(--bs-secondary-dark)" },
42
+ "&.Mui-disabled": {
43
+ opacity: 0.3,
44
+ },
45
+ });
46
+ //#endregion Types and Interfaces
32
47
  // Keep track of which tags have which colors so that the same tag always has the same color
33
48
  const tagsToColors = new Map();
34
49
  /**
@@ -46,8 +61,6 @@ const isTemporaryNetworkExpired = (expirationDate) => {
46
61
  */
47
62
  export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, onDeleteNetwork, onEditNetwork, }) => {
48
63
  const item = useTreeItemModel(itemId);
49
- // We know all labels are strings because we set them that way in the tree view items
50
- const labelString = label;
51
64
  const { getContextProviderProps, getRootProps, getContentProps, getLabelProps, getGroupTransitionProps } = useTreeItem({ itemId, children, label, disabled });
52
65
  const rootRef = useRef(null);
53
66
  const isParent = Array.isArray(children) && children.length > 0;
@@ -64,7 +77,7 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, onDele
64
77
  const expirationTime = item?.temporaryNetworkExpirationTime;
65
78
  const isTemporaryNetwork = Boolean(expirationTime);
66
79
  const isExpired = isChild && isTemporaryNetwork && isTemporaryNetworkExpired(expirationTime);
67
- const networkDefinition = item?.temporaryNetworkDefinition ?? null;
80
+ const networkHocon = item?.temporaryNetworkHocon ?? null;
68
81
  const iconNameSuggestion = item.iconSuggestion;
69
82
  let muiIconElement;
70
83
  // If the item is a child (i.e., a network), we want to render an icon next to it.
@@ -106,41 +119,20 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, onDele
106
119
  .map((tag) => (_jsx(Chip, { label: tag, style: {
107
120
  margin: "0.25rem",
108
121
  backgroundColor: `var(${tagsToColors.get(tag) || TAG_COLORS[0]})`,
109
- } }, tag))), placement: "right", arrow: true, children: _jsx(BookmarkIcon, { sx: { fontSize: "0.75rem", color: "var(--bs-accent1-medium)" } }) })) : null] }), isChild && isTemporaryNetwork && (_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: "0.25rem", marginLeft: "auto" }, children: [networkDefinition && (_jsx(Tooltip, { title: isExpired ? "Expired" : "Download network definition", children: _jsx("span", { children: _jsx(IconButton, { onClick: (e) => {
122
+ } }, tag))), placement: "right", arrow: true, children: _jsx(BookmarkIcon, { sx: { fontSize: "0.75rem", color: "var(--bs-accent1-medium)" } }) })) : null] }), isChild && isTemporaryNetwork && (_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: "0.25rem", marginLeft: "auto" }, children: [networkHocon && (_jsx(Tooltip, { title: isExpired ? "Expired" : DOWNLOAD_NETWORK_DEFINITION_LABEL, children: _jsx("span", { children: _jsx(ActionIconButton, { onClick: (e) => {
123
+ // The button is disabled while expired, so no need to guard here.
110
124
  e.stopPropagation();
111
- // Strip the reservation UUID before building the filename so
112
- // exported files carry a clean name (toSafeFilename would
113
- // otherwise flatten the UUID's hyphens to underscores).
114
- const cleanName = removeTrailingUuid(labelString);
115
- const fileName = `${toSafeFilename(cleanName)}.json`;
116
- downloadFile(JSON.stringify(networkDefinition, null, 2), fileName, "application/json");
117
- }, disabled: isExpired, "aria-label": "Download network definition", size: "small", sx: {
118
- padding: 0,
119
- color: "var(--bs-secondary)",
120
- "&:hover": { color: "var(--bs-secondary-dark)" },
121
- "&.Mui-disabled": {
122
- color: "var(--bs-secondary)",
123
- opacity: 0.3,
124
- },
125
- }, children: _jsx(DownloadIcon, { sx: { fontSize: "0.75rem" } }) }) }) })), _jsx(Tooltip, { title: "Edit this network", children: _jsx("span", { children: _jsx(IconButton, { onClick: (e) => {
125
+ downloadFile(networkHocon, item.downloadFileName, "text/plain");
126
+ }, disabled: isExpired, "aria-label": DOWNLOAD_NETWORK_DEFINITION_LABEL, size: "small", children: _jsx(DownloadIcon, { sx: { fontSize: "0.75rem" } }) }) }) })), _jsx(Tooltip, { title: EDIT_NETWORK_LABEL, children: _jsx("span", { children: _jsx(ActionIconButton, { onClick: (e) => {
126
127
  e.stopPropagation();
127
128
  onEditNetwork?.(itemId);
128
- }, disabled: isExpired, size: "small", sx: {
129
- padding: 0,
130
- color: "var(--bs-secondary)",
131
- "&:hover": { color: "var(--bs-secondary-dark)" },
132
- "&.Mui-disabled": {
133
- color: "var(--bs-secondary)",
134
- opacity: 0.3,
135
- },
136
- }, children: _jsx(Edit, { sx: { fontSize: "0.75rem" } }) }) }) }), _jsx(Tooltip, { title: "Delete network", children: _jsx(Delete, { onClick: (e) => {
129
+ }, disabled: isExpired, "aria-label": EDIT_NETWORK_LABEL, size: "small", children: _jsx(Edit, { sx: { fontSize: "0.75rem" } }) }) }) }), _jsx(Tooltip, { title: "Delete network", children: _jsx(Delete, { onClick: (e) => {
137
130
  e.stopPropagation();
138
131
  onDeleteNetwork?.(itemId, isExpired);
139
132
  }, sx: {
140
133
  color: "var(--bs-secondary)",
141
134
  "&:hover": { color: (theme) => theme.palette.warning.main },
142
135
  "&.Mui-disabled": {
143
- color: "var(--bs-secondary)",
144
136
  opacity: 0.3,
145
137
  },
146
138
  cursor: "pointer",
@@ -6,14 +6,12 @@ export declare const SPARKLE_HIGHLIGHT_CLASS = "sparkle-highlight";
6
6
  export interface SidebarProps {
7
7
  readonly id: string;
8
8
  readonly isAwaitingLlm: boolean;
9
- readonly isImporting: boolean;
10
9
  readonly networkIconSuggestions?: NetworkIconSuggestions;
11
10
  readonly networks: readonly AgentInfo[];
12
11
  readonly neuroSanServerURL: string;
13
12
  readonly newlyAddedTemporaryNetworks?: Set<string>;
14
13
  readonly onDeleteNetwork?: (network: string, isExpired: boolean) => void;
15
14
  readonly onEditNetwork?: (network: string) => void;
16
- readonly onImportClick?: () => void;
17
15
  readonly setSelectedNetwork: (network: string) => void;
18
16
  readonly temporaryNetworks?: readonly TemporaryNetwork[];
19
17
  }
@@ -15,7 +15,6 @@ See the License for the specific language governing permissions and
15
15
  limitations under the License.
16
16
  */
17
17
  import AddBoxRounded from "@mui/icons-material/AddBoxRounded";
18
- import FileUploadOutlinedIcon from "@mui/icons-material/FileUploadOutlined";
19
18
  import Box from "@mui/material/Box";
20
19
  import Button from "@mui/material/Button";
21
20
  import { keyframes, styled } from "@mui/material/styles";
@@ -116,7 +115,7 @@ const ServerStatusTooltip = styled(({ className, ...props }) => (_jsx(Tooltip, {
116
115
  },
117
116
  });
118
117
  //#endregion: Types
119
- export const Sidebar = ({ id, isAwaitingLlm, isImporting, networkIconSuggestions, networks, neuroSanServerURL, newlyAddedTemporaryNetworks, onDeleteNetwork, onEditNetwork, onImportClick, setSelectedNetwork, temporaryNetworks = EMPTY_ARRAY, }) => {
118
+ export const Sidebar = ({ id, isAwaitingLlm, networkIconSuggestions, networks, neuroSanServerURL, newlyAddedTemporaryNetworks, onDeleteNetwork, onEditNetwork, setSelectedNetwork, temporaryNetworks = EMPTY_ARRAY, }) => {
120
119
  const [expandedItems, setExpandedItems] = useState([]);
121
120
  const [selectedItem, setSelectedItem] = useState(null);
122
121
  // Display option for agent/network names
@@ -215,10 +214,12 @@ export const Sidebar = ({ id, isAwaitingLlm, isImporting, networkIconSuggestions
215
214
  "aria-label": "Neuro-san server status",
216
215
  },
217
216
  }, title: _jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 0.5 }, children: [_jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "Status:" }), " ", mapStatus(neuroSanServerStatus?.statusText)] }), _jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "URL:" }), " ", neuroSanServerURL || "unknown"] }), _jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "Service:" }), " ", neuroSanServerStatus?.healthCheckResponse?.service || "unknown"] }), _jsx(Typography, { sx: { fontWeight: "bold" }, variant: "body2", children: "Versions:" }), _jsxs(Box, { sx: { ml: 1 }, children: [_jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "Neuro SAN:" }), " ", neuroSanServerStatus?.healthCheckResponse?.versions?.["neuro-san"] ?? "unknown"] }), _jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "Neuro SAN Studio:" }), " ", neuroSanServerStatus?.healthCheckResponse?.versions?.["neuro-san-studio"] ?? "unknown"] })] }), !neuroSanServerStatus?.success && neuroSanServerStatus?.httpStatus && (_jsx(_Fragment, { children: _jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "HTTP status:" }), " ", `${neuroSanServerStatus.httpStatus} (${httpStatus[neuroSanServerStatus.httpStatus] ?? "Unknown status"})`] }) }))] }), placement: "right", children: _jsx("span", { children: _jsx(StatusLight, { id: `${id}-agent-network-status-light`, statusValue: toStatusColor() }) }) }));
218
- const getAddNetworkButton = () => (_jsxs(Box, { sx: { display: "flex" }, children: [_jsx(Button, { "aria-label": "Add New Network", disabled: isAwaitingLlm, id: "add-network-btn", onClick: () => {
219
- setSelectedItem(AGENT_NETWORK_DESIGNER_ID);
220
- setSelectedNetwork(AGENT_NETWORK_DESIGNER_ID);
221
- }, sx: { display: "inline-block", minWidth: "40px" }, children: _jsx(Tooltip, { title: "Create your own agent network", placement: "top", children: _jsx(AddBoxRounded, { id: "add-network-icon", sx: { color: "var(--bs-secondary)" } }) }) }), _jsx(Button, { "aria-label": "Import Network Definition", disabled: isAwaitingLlm || isImporting, id: "import-network-btn", onClick: onImportClick, sx: { display: "inline-block", minWidth: "40px" }, children: _jsx(Tooltip, { title: "Import a network definition file", placement: "top", children: _jsx(FileUploadOutlinedIcon, { id: "import-network-icon", sx: { color: isAwaitingLlm || isImporting ? "rgba(0, 0, 0, 0.12)" : "var(--bs-secondary)" } }) }) })] }));
217
+ const getAddNetworkButton = () => (_jsx(Box, { sx: { display: "flex" }, children: _jsx(Button, { "aria-label": "Add New Network", disabled: isAwaitingLlm, id: "add-network-btn", onClick: () => {
218
+ setSelectedItem(AGENT_NETWORK_DESIGNER_ID);
219
+ setSelectedNetwork(AGENT_NETWORK_DESIGNER_ID);
220
+ }, sx: { display: "inline-block", minWidth: "40px" }, children: _jsx(Tooltip, { title: "Create your own agent network", placement: "top", children: _jsx(AddBoxRounded, { id: "add-network-icon", sx: (theme) => ({
221
+ color: isAwaitingLlm ? theme.palette.action.disabled : "var(--bs-secondary)",
222
+ }) }) }) }) }));
222
223
  const getSidebarHeading = () => (_jsxs(SidebarHeading, { id: `${id}-heading`, children: [_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: (theme) => theme.spacing(1.5) }, children: [getStatusLight(), "Agent Networks"] }), getAddNetworkButton()] }));
223
224
  const getSidebarNetworksTree = () => (_jsx(RichTreeView, { disableSelection: isAwaitingLlm, expandedItems: expandedItems, items: treeViewItems, multiSelect: false, onExpandedItemsChange: (_event, itemIds) => setExpandedItems(itemIds), onSelectedItemsChange: handleSelectedItemsChange, selectedItems: selectedItem, slotProps: {
224
225
  item: { onDeleteNetwork, onEditNetwork: handleEditNetworkWithSelect },
@@ -1,7 +1,6 @@
1
1
  import { TreeViewDefaultItemModelProperties } from "@mui/x-tree-view/models";
2
2
  import { AgentInfo } from "../../../generated/neuro-san/NeuroSanClient.js";
3
3
  import { TemporaryNetwork } from "../../../state/TemporaryNetworks.js";
4
- import { AgentNetworkDefinitionEntry } from "../const.js";
5
4
  /**
6
5
  * Represents either a category (folder) or a network (leaf) in the tree view.
7
6
  * Note that we omit the `children` property from the parent as we want to use read-only semantics
@@ -9,11 +8,12 @@ import { AgentNetworkDefinitionEntry } from "../const.js";
9
8
  export interface AgentNetworkTreeItemModel extends Omit<TreeViewDefaultItemModelProperties, "children"> {
10
9
  readonly children?: readonly AgentNetworkTreeItemModel[];
11
10
  readonly displayName: string;
11
+ readonly downloadFileName?: string;
12
12
  readonly iconSuggestion?: string;
13
13
  readonly isNetwork: boolean;
14
14
  readonly tags?: readonly string[];
15
15
  readonly temporaryNetworkExpirationTime?: Date;
16
- readonly temporaryNetworkDefinition?: readonly AgentNetworkDefinitionEntry[];
16
+ readonly temporaryNetworkHocon?: string | null;
17
17
  }
18
18
  /**
19
19
  * Recursively searches for a tree item with the specified ID within the given list of tree items.
@@ -1,4 +1,5 @@
1
- import { cleanUpAgentName, removeTrailingUuid } from "../../AgentChat/Common/Utils.js";
1
+ import { cleanUpAgentName, removeTrailingUuid, toDisplayName } from "../../../utils/AgentName.js";
2
+ import { toSafeFilename } from "../../../utils/File.js";
2
3
  //#endregion Types and Interfaces
3
4
  /**
4
5
  * Recursively searches for a tree item with the specified ID within the given list of tree items.
@@ -20,14 +21,6 @@ export const findTreeItemById = (items, itemId) => {
20
21
  }
21
22
  return undefined;
22
23
  };
23
- /**
24
- * Converts a raw agent name into a display name for the tree view, respecting the user's preference for native
25
- * vs cleaned names.
26
- * @param itemName - The raw agent name from the API
27
- * @param useNativeNames - Whether to use native names or cleaned-up names for display
28
- * @returns The display name to show in the tree view
29
- */
30
- const toDisplayName = (itemName, useNativeNames) => useNativeNames ? itemName : cleanUpAgentName(removeTrailingUuid(itemName));
31
24
  /**
32
25
  * Computes the display name for a network (leaf) node.
33
26
  * @param label - The label to use for the tree item (usually derived from the agent name)
@@ -45,6 +38,14 @@ const toLeafDisplayName = (label, useNativeNames, displayNameOverride) => {
45
38
  const cleanedName = cleanUpAgentName(displayNameOverride ?? removeTrailingUuid(label));
46
39
  return cleanedName;
47
40
  };
41
+ /**
42
+ * Computes the filename used when exporting a network's HOCON to disk. The trailing reservation
43
+ * UUID is stripped first so the export carries a clean name (toSafeFilename would otherwise flatten
44
+ * the UUID's hyphens to underscores).
45
+ * @param label - The label to use for the tree item (usually derived from the agent name)
46
+ * @returns The sanitized `.hocon` filename for the exported network HOCON
47
+ */
48
+ const toDownloadFileName = (label) => `${toSafeFilename(removeTrailingUuid(label))}.hocon`;
48
49
  /**
49
50
  * Converts an AgentInfo object into a tree item model representing a network (leaf node).
50
51
  * @param network - The AgentInfo object containing details about the network
@@ -57,11 +58,12 @@ const toNetworkLeaf = (network, label, useNativeNames, metadata = {}) => ({
57
58
  id: network.agent_name,
58
59
  label,
59
60
  displayName: toLeafDisplayName(label, useNativeNames, metadata.displayNameOverride),
61
+ downloadFileName: metadata.temporaryNetworkHocon ? toDownloadFileName(label) : undefined,
60
62
  iconSuggestion: metadata.iconSuggestion,
61
63
  isNetwork: true,
62
64
  tags: network.tags,
63
65
  temporaryNetworkExpirationTime: metadata.temporaryNetworkExpirationTime,
64
- temporaryNetworkDefinition: metadata.temporaryNetworkDefinition,
66
+ temporaryNetworkHocon: metadata.temporaryNetworkHocon,
65
67
  });
66
68
  /**
67
69
  * Recursively sort tree nodes and their children by display name
@@ -174,9 +176,7 @@ export const buildTreeViewItems = (useNativeNames, regularNetworks = [], tempora
174
176
  tree = withNetworkAdded(tree, temporaryNetwork.agentInfo, useNativeNames, {
175
177
  iconSuggestion: "HourglassTop",
176
178
  temporaryNetworkExpirationTime: new Date(temporaryNetwork.reservation.expiration_time_in_seconds * 1000),
177
- // The structured definition is carried through and serialized at download time (it's the same shape
178
- // the import modal reads back in).
179
- temporaryNetworkDefinition: temporaryNetwork.agentNetworkDefinition,
179
+ temporaryNetworkHocon: temporaryNetwork.networkHocon,
180
180
  displayNameOverride: temporaryNetwork.agentNetworkName,
181
181
  });
182
182
  }
@@ -65,22 +65,8 @@ export declare const mergeNetworks: (target: TemporaryNetwork[], incoming: Tempo
65
65
  /** Logs and notifies about a save error. Suppresses AbortError (user-canceled). */
66
66
  export declare const notifySaveError: (agentName: string, e: unknown) => void;
67
67
  /**
68
- * Streams a network definition through the network designer, collecting the
69
- * reservations returned across every chunk. Returns the accumulated networks
70
- * (empty if the designer returned no reservation).
68
+ * Streams a network definition through the network designer to obtain its reservation.
69
+ * Returns the single updated network as a one-element list (empty if the designer returned
70
+ * no reservation).
71
71
  */
72
72
  export declare const streamNetworkDesignerUpsert: (neuroSanURL: string, signal: AbortSignal, frontman: string, networkDef: AgentNetworkDefinitionEntry[], agentNetworkName: string | undefined, username: string) => Promise<TemporaryNetwork[]>;
73
- /** Reasons an import can fail before its networks reach the store. */
74
- export type ImportFailureReason = "invalid-definition" | "no-reservation";
75
- export type ImportNetworkResult = {
76
- networks: TemporaryNetwork[];
77
- } | {
78
- failure: ImportFailureReason;
79
- };
80
- export declare const IMPORT_FAILURE_DETAIL: Record<ImportFailureReason, string>;
81
- /**
82
- * Converts imported JSON into a network definition and streams it through the
83
- * network designer to obtain reservations. Returns the resulting networks, or a
84
- * failure reason if the content is not a valid definition or no reservation is returned.
85
- */
86
- export declare const importNetworkFromJson: (content: string, agentNetworkName: string, neuroSanURL: string, username: string) => Promise<ImportNetworkResult>;
@@ -1,10 +1,11 @@
1
- import { getFrontman } from "./AgentFlow/GraphStructure.js";
1
+ import { keyBy, mergeWith } from "lodash-es";
2
2
  import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_HOCON, AGENT_RESERVATIONS_KEY, DisplayAs, TEMPORARY_NETWORK_FOLDER, } from "./const.js";
3
- import { jsonToNetworkDefinition } from "./Sidebar/ImportNetworkModal.js";
4
3
  import { sendNetworkDesignerRequest } from "../../controller/agent/Agent.js";
5
4
  import { ChatMessageType } from "../../generated/neuro-san/NeuroSanClient.js";
6
- import { chatMessageFromChunk, removeTrailingUuid } from "../AgentChat/Common/Utils.js";
5
+ import { removeTrailingUuid } from "../../utils/AgentName.js";
6
+ import { chatMessageFromChunk } from "../AgentChat/Common/Utils.js";
7
7
  import { NotificationType, sendNotification } from "../Common/notification.js";
8
+ //#endregion: Types
8
9
  /**
9
10
  * Extracts agent reservations from a chat message, if they exist.
10
11
  * @param message The chat message to extract reservations from. We expect reservations to be present in messages of
@@ -90,22 +91,17 @@ export const isTemporaryNetwork = (agentName, networks) => agentName !== null &&
90
91
  * Merges incoming networks into target, keeping the entry with the highest expiration time.
91
92
  * Returns a new array; does not mutate either argument.
92
93
  */
93
- export const mergeNetworks = (target, incoming) => incoming.reduce((result, n) => {
94
- const existingIdx = result.findIndex((e) => e.agentNetworkName === n.agentNetworkName);
95
- // No existing entry with this name - append.
96
- if (existingIdx < 0)
97
- return [...result, n];
98
- // Existing entry found - keep whichever reservation expires later.
99
- if (n.reservation.expiration_time_in_seconds > result[existingIdx].reservation.expiration_time_in_seconds) {
100
- return result.map((e, i) => (i === existingIdx ? n : e));
101
- }
102
- return result;
103
- }, [...target]);
94
+ export const mergeNetworks = (target, incoming) => Object.values(mergeWith(keyBy(target, "agentNetworkName"), keyBy(incoming, "agentNetworkName"),
95
+ // Both collections share a name: keep whichever reservation expires later, existing wins ties.
96
+ (existing, candidate) => existing &&
97
+ existing.reservation.expiration_time_in_seconds >= candidate.reservation.expiration_time_in_seconds
98
+ ? existing
99
+ : candidate));
104
100
  /**
105
101
  * Extracts TemporaryNetworks from a single streamed chunk, merging into `accumulated`.
106
102
  * Returns `accumulated` unchanged if the chunk yields no reservations or on parse error.
107
103
  */
108
- const collectNetworksFromChunk = (chunk, updated, accumulated) => {
104
+ const collectNetworkFromChunk = (chunk, updated, accumulated) => {
109
105
  try {
110
106
  const chatMessage = chatMessageFromChunk(chunk);
111
107
  if (!chatMessage)
@@ -132,35 +128,16 @@ export const notifySaveError = (agentName, e) => {
132
128
  sendNotification(NotificationType.error, `Failed to update network "${agentName}".`, detail);
133
129
  };
134
130
  /**
135
- * Streams a network definition through the network designer, collecting the
136
- * reservations returned across every chunk. Returns the accumulated networks
137
- * (empty if the designer returned no reservation).
131
+ * Streams a network definition through the network designer to obtain its reservation.
132
+ * Returns the single updated network as a one-element list (empty if the designer returned
133
+ * no reservation).
138
134
  */
139
135
  export const streamNetworkDesignerUpsert = async (neuroSanURL, signal, frontman, networkDef, agentNetworkName, username) => {
140
136
  let newNetworks = [];
141
137
  await sendNetworkDesignerRequest(neuroSanURL, signal, frontman, networkDef, agentNetworkName, username, (chunk) => {
142
- newNetworks = collectNetworksFromChunk(chunk, networkDef, newNetworks);
138
+ // There is only one network. The designer echoes it across streamed chunks. Merge by name so the
139
+ // repeated echoes collapse into that single entry rather than appending duplicates.
140
+ newNetworks = collectNetworkFromChunk(chunk, networkDef, newNetworks);
143
141
  });
144
142
  return newNetworks;
145
143
  };
146
- export const IMPORT_FAILURE_DETAIL = {
147
- "invalid-definition": "The file does not contain a valid network definition.",
148
- "no-reservation": "The network designer did not return a reservation. Please try again.",
149
- };
150
- /**
151
- * Converts imported JSON into a network definition and streams it through the
152
- * network designer to obtain reservations. Returns the resulting networks, or a
153
- * failure reason if the content is not a valid definition or no reservation is returned.
154
- */
155
- export const importNetworkFromJson = async (content, agentNetworkName, neuroSanURL, username) => {
156
- const networkDef = jsonToNetworkDefinition(JSON.parse(content));
157
- if (networkDef.length === 0) {
158
- return { failure: "invalid-definition" };
159
- }
160
- const frontman = getFrontman(networkDef)?.origin ?? networkDef[0]?.origin ?? "agent";
161
- const networks = await streamNetworkDesignerUpsert(neuroSanURL, new AbortController().signal, frontman, networkDef, agentNetworkName, username);
162
- if (networks.length === 0) {
163
- return { failure: "no-reservation" };
164
- }
165
- return { networks };
166
- };
@@ -1,3 +1,4 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
1
2
  /**
2
3
  * This file defines the steps for the main tour of the Multi-Agent Accelerator application.
3
4
  *
@@ -18,8 +19,7 @@ export const MAIN_TOUR_STEPS = [
18
19
  placement: "bottom",
19
20
  },
20
21
  {
21
- content: "Agent Network Designer: Click here to create and edit your own network, with the help of a " +
22
- "powerful AI assistant.",
22
+ content: (_jsxs(_Fragment, { children: ["Agent Network Designer: Click here to create and edit your own network, with the help of a powerful AI assistant.", _jsx("br", {}), _jsx("br", {}), "Networks you create can be downloaded from the sidebar as a HOCON file and run in neuro-san or neuro-san-studio. See the User Guide (under Help) for more information."] })),
23
23
  target: () => document.querySelector("#add-network-icon"),
24
24
  placement: "bottom",
25
25
  },
@@ -1,16 +1,10 @@
1
1
  import { ConnectivityInfo } from "../../generated/neuro-san/NeuroSanClient.js";
2
- export declare const DEFAULT_FRONTMAN_X_POS = 150;
3
- export declare const DEFAULT_FRONTMAN_Y_POS = 450;
4
- export declare const BASE_RADIUS = 100;
5
- export declare const LEVEL_SPACING = 150;
6
- export declare const TEMPORARY_NETWORK_FOLDER = "temporary";
7
- export declare const AGENT_RESERVATIONS_KEY = "agent_reservations";
8
- export declare const AGENT_NETWORK_HOCON = "agent_network_hocon_text";
9
- export declare const AGENT_PROGRESS_CONNECTIVITY_KEY = "connectivity_info";
10
- export declare const AGENT_NETWORK_DESIGNER_ID = "agent_network_designer";
11
2
  export declare const AGENT_NETWORK_DEFINITION_KEY = "agent_network_definition";
3
+ export declare const AGENT_NETWORK_DESIGNER_ID = "agent_network_designer";
4
+ export declare const AGENT_NETWORK_HOCON = "agent_network_hocon_text";
12
5
  export declare const AGENT_NETWORK_NAME_KEY = "agent_network_name";
13
- export declare const TRIGGER_APP_TOUR_EVENT_NAME = "trigger-app-tour";
6
+ export declare const AGENT_PROGRESS_CONNECTIVITY_KEY = "connectivity_info";
7
+ export declare const AGENT_RESERVATIONS_KEY = "agent_reservations";
14
8
  /**
15
9
  * A single agent entry within an agent network definition, as received in sly_data from the backend.
16
10
  * Extends ConnectivityInfo with editable instructions and description fields for the Agent Network Designer.
@@ -19,6 +13,9 @@ export type AgentNetworkDefinitionEntry = ConnectivityInfo & {
19
13
  readonly instructions?: string;
20
14
  readonly description?: string;
21
15
  };
16
+ export declare const BASE_RADIUS = 100;
17
+ export declare const DEFAULT_FRONTMAN_X_POS = 150;
18
+ export declare const DEFAULT_FRONTMAN_Y_POS = 450;
22
19
  /** Possible values for the `display_as` field in ConnectivityInfo / AgentNetworkDefinitionEntry. */
23
20
  export declare enum DisplayAs {
24
21
  LLM_AGENT = "llm_agent",
@@ -26,6 +23,9 @@ export declare enum DisplayAs {
26
23
  LANGCHAIN_TOOL = "langchain_tool",
27
24
  EXTERNAL_AGENT = "external_agent"
28
25
  }
26
+ export declare const EXPIRED_NETWORKS_CHECK_INTERVAL_MS: number;
29
27
  export declare const GRACE_PERIOD_MS: number;
28
+ export declare const LEVEL_SPACING = 150;
30
29
  export declare const SHOW_TOUR_DELAY_MS = 5000;
31
- export declare const EXPIRED_NETWORKS_CHECK_INTERVAL_MS: number;
30
+ export declare const TEMPORARY_NETWORK_FOLDER = "temporary";
31
+ export declare const TRIGGER_APP_TOUR_EVENT_NAME = "trigger-app-tour";
@@ -13,30 +13,22 @@ 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
- export const DEFAULT_FRONTMAN_X_POS = 150;
17
- export const DEFAULT_FRONTMAN_Y_POS = 450;
18
- // Minimum distance from center
19
- export const BASE_RADIUS = 100;
20
- // Distance between depth levels
21
- export const LEVEL_SPACING = 150;
22
- // Temporary folder name for networks created from agent reservations. These networks are not "in a folder" when
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
- // temporary.
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";
30
- // The key in the message structure where connectivity info is stored for agent progress messages
31
- export const AGENT_PROGRESS_CONNECTIVITY_KEY = "connectivity_info";
32
- // Agent name for the special "Agent Network Designer" network
33
- export const AGENT_NETWORK_DESIGNER_ID = "agent_network_designer";
34
16
  // The key in sly_data where the agent network definition is stored
35
17
  export const AGENT_NETWORK_DEFINITION_KEY = "agent_network_definition";
18
+ // Agent name for the special "Agent Network Designer" network
19
+ export const AGENT_NETWORK_DESIGNER_ID = "agent_network_designer";
20
+ // We expect the agent network HOCON to be stored in sly_data under this key, if it is provided by the backend
21
+ export const AGENT_NETWORK_HOCON = "agent_network_hocon_text";
36
22
  // The key in sly_data where the agent network name is stored
37
23
  export const AGENT_NETWORK_NAME_KEY = "agent_network_name";
38
- // The event name that needs to be fired for the app tour to start
39
- export const TRIGGER_APP_TOUR_EVENT_NAME = "trigger-app-tour";
24
+ // The key in the message structure where connectivity info is stored for agent progress messages
25
+ export const AGENT_PROGRESS_CONNECTIVITY_KEY = "connectivity_info";
26
+ // We expect the agent reservations to be stored in sly_data under this key
27
+ export const AGENT_RESERVATIONS_KEY = "agent_reservations";
28
+ // Minimum distance from center
29
+ export const BASE_RADIUS = 100;
30
+ export const DEFAULT_FRONTMAN_X_POS = 150;
31
+ export const DEFAULT_FRONTMAN_Y_POS = 450;
40
32
  /** Possible values for the `display_as` field in ConnectivityInfo / AgentNetworkDefinitionEntry. */
41
33
  export var DisplayAs;
42
34
  (function (DisplayAs) {
@@ -45,9 +37,17 @@ export var DisplayAs;
45
37
  DisplayAs["LANGCHAIN_TOOL"] = "langchain_tool";
46
38
  DisplayAs["EXTERNAL_AGENT"] = "external_agent";
47
39
  })(DisplayAs || (DisplayAs = {}));
40
+ // Check for expired networks every this many milliseconds
41
+ export const EXPIRED_NETWORKS_CHECK_INTERVAL_MS = 10 * 1000;
48
42
  // Display expired temporary networks for this amount of time after they expire so users can see what happened
49
43
  export const GRACE_PERIOD_MS = 5 * 60 * 1000; // 5 minutes
44
+ // Distance between depth levels
45
+ export const LEVEL_SPACING = 150;
50
46
  // We show the tour modal after this amount of time so as not to "pounce" on the user when they first open the app
51
47
  export const SHOW_TOUR_DELAY_MS = 5000;
52
- // Check for expired networks every this many milliseconds
53
- export const EXPIRED_NETWORKS_CHECK_INTERVAL_MS = 10 * 1000;
48
+ // Temporary folder name for networks created from agent reservations. These networks are not "in a folder" when
49
+ // they come from the backend, but we need to put them somewhere in the UI, and this makes it clear that they're
50
+ // temporary.
51
+ export const TEMPORARY_NETWORK_FOLDER = "temporary";
52
+ // The event name that needs to be fired for the app tour to start
53
+ export const TRIGGER_APP_TOUR_EVENT_NAME = "trigger-app-tour";
@@ -13,6 +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 { keyBy } from "lodash-es";
16
17
  import { create } from "zustand";
17
18
  import { persist } from "zustand/middleware";
18
19
  /**
@@ -22,29 +23,19 @@ export const useTempNetworksStore = create()(persist((set) => ({
22
23
  tempNetworks: [],
23
24
  setTempNetworks: (tempNetworks) => set({ tempNetworks }),
24
25
  upsertTempNetworks: (incomingNetworks) => {
25
- set((state) => {
26
- const updated = [...state.tempNetworks];
27
- for (const incomingNetwork of incomingNetworks) {
28
- const existingIndex = updated.findIndex((network) => network.agentNetworkName === incomingNetwork.agentNetworkName);
29
- if (existingIndex >= 0) {
30
- updated[existingIndex] = incomingNetwork; // replace the existing entry in-place
31
- }
32
- else {
33
- updated.push(incomingNetwork); // no existing entry — add as new
34
- }
35
- }
36
- return { tempNetworks: updated };
37
- });
26
+ set((state) => ({
27
+ // Key both sides by name and let incoming win; existing entries keep their position,
28
+ // new ones are appended.
29
+ tempNetworks: Object.values({
30
+ ...keyBy(state.tempNetworks, "agentNetworkName"),
31
+ ...keyBy(incomingNetworks, "agentNetworkName"),
32
+ }),
33
+ }));
38
34
  return incomingNetworks;
39
35
  },
40
- updateTempNetworkDefinition: (networkName, agentNetworkDefinition) => set((state) => {
41
- const updated = [...state.tempNetworks];
42
- const existingIndex = updated.findIndex((network) => network.agentInfo.agent_name === networkName);
43
- if (existingIndex >= 0) {
44
- updated[existingIndex] = { ...updated[existingIndex], agentNetworkDefinition };
45
- }
46
- return { tempNetworks: updated };
47
- }),
36
+ updateTempNetworkDefinition: (networkName, agentNetworkDefinition) => set(({ tempNetworks }) => ({
37
+ tempNetworks: tempNetworks.map((network) => network.agentInfo.agent_name === networkName ? { ...network, agentNetworkDefinition } : network),
38
+ })),
48
39
  }), {
49
40
  name: "temp-networks",
50
41
  }));