@cognizant-ai-lab/ui-common 1.5.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/dist/Theme/Theme.js +3 -3
  2. package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +11 -1
  3. package/dist/components/AgentChat/ChatCommon/ChatCommon.js +284 -285
  4. package/dist/components/AgentChat/ChatCommon/ChatHistory.d.ts +1 -7
  5. package/dist/components/AgentChat/ChatCommon/ChatHistory.js +33 -22
  6. package/dist/components/AgentChat/ChatCommon/ControlButtons.js +2 -2
  7. package/dist/components/AgentChat/ChatCommon/Conversation.d.ts +13 -0
  8. package/dist/components/AgentChat/ChatCommon/Conversation.js +80 -0
  9. package/dist/components/AgentChat/ChatCommon/ConversationTurn.d.ts +23 -0
  10. package/dist/components/AgentChat/ChatCommon/ConversationTurn.js +11 -0
  11. package/dist/components/AgentChat/ChatCommon/FormattedMarkdown.js +5 -3
  12. package/dist/components/AgentChat/ChatCommon/SampleQueries.d.ts +3 -0
  13. package/dist/components/AgentChat/ChatCommon/SampleQueries.js +6 -3
  14. package/dist/components/AgentChat/ChatCommon/Thinking.d.ts +12 -0
  15. package/dist/components/AgentChat/ChatCommon/Thinking.js +51 -0
  16. package/dist/components/AgentChat/Common/LlmChatButton.d.ts +2 -2
  17. package/dist/components/AgentChat/Common/Types.d.ts +6 -5
  18. package/dist/components/AgentChat/Common/Types.js +5 -0
  19. package/dist/components/AgentChat/Common/Utils.d.ts +1 -1
  20. package/dist/components/AgentChat/Common/Utils.js +14 -9
  21. package/dist/components/Common/AccordionLite.d.ts +14 -0
  22. package/dist/components/Common/AccordionLite.js +25 -0
  23. package/dist/components/Common/ConfirmationModal.d.ts +1 -0
  24. package/dist/components/Common/ConfirmationModal.js +1 -1
  25. package/dist/components/Common/CustomerLogo.js +1 -1
  26. package/dist/components/Common/MUIAlert.d.ts +1 -0
  27. package/dist/components/Common/MUIAlert.js +3 -4
  28. package/dist/components/Common/Navbar.d.ts +2 -1
  29. package/dist/components/Common/Navbar.js +8 -4
  30. package/dist/components/Common/notification.d.ts +1 -1
  31. package/dist/components/Common/notification.js +17 -12
  32. package/dist/components/MultiAgentAccelerator/AgentFlow.d.ts +8 -0
  33. package/dist/components/MultiAgentAccelerator/AgentFlow.js +282 -82
  34. package/dist/components/MultiAgentAccelerator/AgentNode.d.ts +3 -1
  35. package/dist/components/MultiAgentAccelerator/AgentNode.js +64 -28
  36. package/dist/components/MultiAgentAccelerator/AgentNodePopup.d.ts +1 -4
  37. package/dist/components/MultiAgentAccelerator/AgentNodePopup.js +4 -5
  38. package/dist/components/MultiAgentAccelerator/GraphLayouts.js +19 -9
  39. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +2 -2
  40. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +268 -60
  41. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +1 -0
  42. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +28 -12
  43. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +1 -0
  44. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +21 -5
  45. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +4 -3
  46. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +8 -2
  47. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +19 -2
  48. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +40 -5
  49. package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.js +27 -14
  50. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.d.ts +7 -0
  51. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +88 -0
  52. package/dist/components/MultiAgentAccelerator/const.d.ts +7 -10
  53. package/dist/components/MultiAgentAccelerator/const.js +9 -10
  54. package/dist/const.d.ts +5 -1
  55. package/dist/const.js +5 -2
  56. package/dist/controller/agent/Agent.d.ts +10 -0
  57. package/dist/controller/agent/Agent.js +17 -1
  58. package/dist/controller/llm/LlmChat.js +2 -2
  59. package/dist/index.d.ts +0 -1
  60. package/dist/index.js +0 -1
  61. package/dist/state/TemporaryNetworks.d.ts +5 -15
  62. package/dist/state/TemporaryNetworks.js +15 -34
  63. package/dist/state/Tour.d.ts +29 -0
  64. package/dist/state/Tour.js +22 -0
  65. package/dist/state/UserInfo.d.ts +2 -1
  66. package/dist/tsconfig.build.tsbuildinfo +1 -1
  67. package/dist/utils/Authentication.js +12 -3
  68. package/dist/utils/File.d.ts +7 -0
  69. package/dist/utils/File.js +14 -3
  70. package/dist/utils/text.js +2 -2
  71. package/dist/utils/title.js +1 -1
  72. package/dist/utils/zIndexLayers.js +3 -0
  73. package/package.json +15 -11
  74. package/dist/components/AgentChat/ChatCommon/AgentConnectivity.d.ts +0 -14
  75. package/dist/components/AgentChat/ChatCommon/AgentConnectivity.js +0 -23
  76. package/dist/components/AgentChat/ChatCommon/Greetings.d.ts +0 -1
  77. package/dist/components/AgentChat/ChatCommon/Greetings.js +0 -38
  78. package/dist/components/AgentChat/ChatCommon/UserQueryDisplay.d.ts +0 -7
  79. package/dist/components/AgentChat/ChatCommon/UserQueryDisplay.js +0 -32
  80. package/dist/components/Common/LlmChatOptionsButton.d.ts +0 -6
  81. package/dist/components/Common/LlmChatOptionsButton.js +0 -31
@@ -15,7 +15,7 @@ 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 CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
18
+ import CheckCircleOutlinedIcon from "@mui/icons-material/CheckCircleOutlined";
19
19
  import ClearIcon from "@mui/icons-material/Clear";
20
20
  import HighlightOff from "@mui/icons-material/HighlightOff";
21
21
  import SettingsIcon from "@mui/icons-material/Settings";
@@ -121,7 +121,7 @@ var CONNECTION_STATUS;
121
121
  })(CONNECTION_STATUS || (CONNECTION_STATUS = {}));
122
122
  // #endregion: Types
123
123
  const EMPTY_ARRAY = [];
124
- export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaitingLlm, networkIconSuggestions, networks, newlyAddedTemporaryNetworks, onDeleteNetwork, setSelectedNetwork, temporaryNetworks = EMPTY_ARRAY, }) => {
124
+ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaitingLlm, networkIconSuggestions, networks, newlyAddedTemporaryNetworks, onDeleteNetwork, onEditNetwork, setSelectedNetwork, temporaryNetworks = EMPTY_ARRAY, }) => {
125
125
  // Get default URL from the environment store.
126
126
  const { backendNeuroSanApiUrl } = useEnvironmentStore();
127
127
  const [urlInput, setUrlInput] = useState(customURLLocalStorage || backendNeuroSanApiUrl);
@@ -134,6 +134,7 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
134
134
  const [settingsAnchorEl, setSettingsAnchorEl] = useState(null);
135
135
  const settingsPopoverOpen = Boolean(settingsAnchorEl);
136
136
  const [expandedItems, setExpandedItems] = useState([]);
137
+ const [selectedItem, setSelectedItem] = useState(null);
137
138
  // Theming/Dark mode
138
139
  const darkMode = useTheme().palette.mode === "dark";
139
140
  const handleSettingsClick = (event) => {
@@ -202,6 +203,15 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
202
203
  };
203
204
  void fetchVersion();
204
205
  }, [customURLLocalStorage, backendNeuroSanApiUrl]);
206
+ // When the edit pencil is clicked, select the network first (if not already selected) so the network loads
207
+ // before entering edit mode — the user shouldn't have to click the network and then click the pencil separately.
208
+ const handleEditNetworkWithSelect = (network) => {
209
+ if (selectedItem !== network) {
210
+ setSelectedItem(network);
211
+ setSelectedNetwork(network);
212
+ }
213
+ onEditNetwork?.(network);
214
+ };
205
215
  const { treeViewItems, nodeIndex } = buildTreeViewItems(networks, temporaryNetworks);
206
216
  const temporaryNetworkExpirationTimes = temporaryNetworks.reduce((acc, tempNetwork) => {
207
217
  acc[tempNetwork.agentInfo.agent_name] = new Date(tempNetwork.reservation.expiration_time_in_seconds * 1000);
@@ -211,7 +221,6 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
211
221
  acc[tempNetwork.agentInfo.agent_name] = tempNetwork.networkHocon;
212
222
  return acc;
213
223
  }, {});
214
- const [selectedItem, setSelectedItem] = useState(null);
215
224
  const handleSelectedItemsChange = (_event, itemId) => {
216
225
  if (!itemId) {
217
226
  return;
@@ -272,6 +281,7 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
272
281
  networkIconSuggestions,
273
282
  nodeIndex,
274
283
  onDeleteNetwork,
284
+ onEditNetwork: handleEditNetworkWithSelect,
275
285
  temporaryNetworkExpirationTimes,
276
286
  temporaryNetworkHoconStrings,
277
287
  },
@@ -295,8 +305,14 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
295
305
  marginLeft: "0.35rem",
296
306
  marginTop: "2px",
297
307
  color: darkMode ? "var(--bs-dark-mode-link)" : undefined,
298
- }, variant: "text", children: "Default" }), (connectionStatusSuccess || connectionStatusError) && (_jsxs(Box, { id: "connection-status-box", display: "flex", alignItems: "center", sx: {
308
+ }, variant: "text", children: "Default" }), (connectionStatusSuccess || connectionStatusError) && (_jsxs(Box, { id: "connection-status-box", sx: {
309
+ display: "flex",
310
+ alignItems: "center",
299
311
  marginLeft: "0.25rem",
300
312
  marginBottom: "0.5rem",
301
- }, children: [connectionStatusSuccess && (_jsxs(_Fragment, { children: [_jsx(CheckCircleOutlineIcon, { id: "connection-status-success-icon", sx: { color: "var(--bs-green)", fontSize: "1.2rem", marginRight: "0.25rem" } }), _jsx(Typography, { id: "connection-status-success-msg", variant: "body2", color: "var(--bs-green)", children: "Connected successfully" })] })), connectionStatusError && (_jsxs(_Fragment, { children: [_jsx(HighlightOff, { id: "connection-status-error-icon", sx: { color: "var(--bs-red)", fontSize: "1.2rem", marginRight: "0.25rem" } }), _jsx(Typography, { id: "connection-status-failed-msg", variant: "body2", color: "var(--bs-red)", children: `Connection failed: "${testConnectionResult?.status || "unknown error"}"` })] }))] }))] })] }));
313
+ }, children: [connectionStatusSuccess && (_jsxs(_Fragment, { children: [_jsx(CheckCircleOutlinedIcon, { id: "connection-status-success-icon", sx: { color: "var(--bs-green)", fontSize: "1.2rem", marginRight: "0.25rem" } }), _jsx(Typography, { id: "connection-status-success-msg", variant: "body2", sx: {
314
+ color: "var(--bs-green)",
315
+ }, children: "Connected successfully" })] })), connectionStatusError && (_jsxs(_Fragment, { children: [_jsx(HighlightOff, { id: "connection-status-error-icon", sx: { color: "var(--bs-red)", fontSize: "1.2rem", marginRight: "0.25rem" } }), _jsx(Typography, { id: "connection-status-failed-msg", variant: "body2", sx: {
316
+ color: "var(--bs-red)",
317
+ }, children: `Connection failed: "${testConnectionResult?.status || "unknown error"}"` })] }))] }))] })] }));
302
318
  };
@@ -1,4 +1,4 @@
1
- import { TreeViewBaseItem } from "@mui/x-tree-view/models";
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
4
  export type NodeIndex = Map<string, {
@@ -11,9 +11,10 @@ export type NodeIndex = Map<string, {
11
11
  * The tree structure is used by the RichTreeView component to display the networks
12
12
  * @param networks - Array of networks from the Neuro-san /list API
13
13
  * @param temporaryNetworks - Array of temporary networks (e.g. ones recently created by the user)
14
- * @returns Array of TreeViewBaseItem objects representing the tree structure and an index for rapid access
14
+ * @returns Array of {@linkcode TreeViewDefaultItemModelProperties} objects representing the tree structure and an
15
+ * index for rapid access
15
16
  */
16
17
  export declare const buildTreeViewItems: (networks: readonly AgentInfo[], temporaryNetworks: readonly TemporaryNetwork[]) => {
17
- treeViewItems: TreeViewBaseItem[];
18
+ treeViewItems: TreeViewDefaultItemModelProperties[];
18
19
  nodeIndex: NodeIndex;
19
20
  };
@@ -2,6 +2,7 @@ import { cleanUpAgentName, removeTrailingUuid } from "../../AgentChat/Common/Uti
2
2
  /**
3
3
  * Iteratively sort all children of tree nodes using a queue-based approach
4
4
  * @param nodes - Array of tree nodes to sort
5
+ * @param nodeIndex - Index mapping node IDs to their AgentInfo and display names, used for sorting by display name
5
6
  */
6
7
  const sortTreeNodes = (nodes, nodeIndex) => {
7
8
  // Sort the top level nodes first. We sort by displayName because that's what the user sees
@@ -85,7 +86,8 @@ const addNetworkToTree = (network, result, uncategorized, map, nodeIndex, displa
85
86
  * The tree structure is used by the RichTreeView component to display the networks
86
87
  * @param networks - Array of networks from the Neuro-san /list API
87
88
  * @param temporaryNetworks - Array of temporary networks (e.g. ones recently created by the user)
88
- * @returns Array of TreeViewBaseItem objects representing the tree structure and an index for rapid access
89
+ * @returns Array of {@linkcode TreeViewDefaultItemModelProperties} objects representing the tree structure and an
90
+ * index for rapid access
89
91
  */
90
92
  export const buildTreeViewItems = (networks, temporaryNetworks) => {
91
93
  // Map to keep track of created nodes in a tree structure
@@ -95,7 +97,11 @@ export const buildTreeViewItems = (networks, temporaryNetworks) => {
95
97
  // Resulting tree view items, ready for consumption by RichTreeView
96
98
  const treeViewItems = [];
97
99
  // Special parent node for networks that aren't in any folder
98
- const uncategorized = { id: "uncategorized", label: "Uncategorized", children: [] };
100
+ const uncategorized = {
101
+ id: "uncategorized",
102
+ label: "Uncategorized",
103
+ children: [],
104
+ };
99
105
  const displayNameCounts = new Map();
100
106
  // Build a tree structure from the flat list of networks.
101
107
  // The networks come in as a series of "paths" like "industry/retail/macys" and we need to build a tree
@@ -40,8 +40,25 @@ export declare const extractNetworkHocon: (message: ChatMessage) => string | nul
40
40
  * @param networkHocon Optional network HOCON string associated with the reservations.
41
41
  * @param agentNetworkDefinition Optional agent network definition entries.
42
42
  * @param agentNetworkName Optional backend canonical network name used to match / deduplicate networks.
43
- * When omitted, the name is derived from the reservation_id via {@link extractNetworkNameFromReservationId}.
44
43
  * @returns List of TemporaryNetwork objects ready for the store.
45
44
  */
46
45
  export declare const convertReservationsToNetworks: (agentReservations: AgentReservation[], networkHocon: string | null, agentNetworkDefinition?: AgentNetworkDefinitionEntry[], agentNetworkName?: string) => TemporaryNetwork[];
47
- export { extractNetworkNameFromReservationId } from "../../state/TemporaryNetworks.js";
46
+ export declare const isEditableAgent: (displayAs: string | undefined) => boolean;
47
+ /**
48
+ * Extracts TemporaryNetwork objects from a chat message by reading reservations, HOCON,
49
+ * agent network definition, and agent network name from the message / sly_data.
50
+ * @param message The chat message to extract from.
51
+ * @param agentNetworkDefinitionOverride Optional definition override (e.g. a locally-edited version) that
52
+ * takes precedence over whatever the backend echoes in sly_data.
53
+ * @returns A (possibly empty) array of TemporaryNetwork objects ready for the store.
54
+ */
55
+ export declare const extractTemporaryNetworksFromMessage: (message: ChatMessage, agentNetworkDefinitionOverride?: AgentNetworkDefinitionEntry[]) => TemporaryNetwork[];
56
+ /**
57
+ * Returns true when agentName refers to a known temporary network.
58
+ */
59
+ export declare const isTemporaryNetwork: (agentName: string | null, networks: TemporaryNetwork[]) => boolean;
60
+ /**
61
+ * Merges incoming networks into target, keeping the entry with the highest expiration time.
62
+ * Returns a new array; does not mutate either argument.
63
+ */
64
+ export declare const mergeNetworks: (target: TemporaryNetwork[], incoming: TemporaryNetwork[]) => TemporaryNetwork[];
@@ -1,6 +1,6 @@
1
- import { AGENT_NETWORK_HOCON, AGENT_RESERVATIONS_KEY, TEMPORARY_NETWORK_FOLDER, } from "./const.js";
1
+ import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_HOCON, AGENT_RESERVATIONS_KEY, DisplayAs, TEMPORARY_NETWORK_FOLDER, } from "./const.js";
2
2
  import { ChatMessageType } from "../../generated/neuro-san/NeuroSanClient.js";
3
- import { extractNetworkNameFromReservationId } from "../../state/TemporaryNetworks.js";
3
+ import { removeTrailingUuid } from "../AgentChat/Common/Utils.js";
4
4
  /**
5
5
  * Extracts agent reservations from a chat message, if they exist.
6
6
  * @param message The chat message to extract reservations from. We expect reservations to be present in messages of
@@ -41,7 +41,6 @@ export const extractNetworkHocon = (message) => {
41
41
  * @param networkHocon Optional network HOCON string associated with the reservations.
42
42
  * @param agentNetworkDefinition Optional agent network definition entries.
43
43
  * @param agentNetworkName Optional backend canonical network name used to match / deduplicate networks.
44
- * When omitted, the name is derived from the reservation_id via {@link extractNetworkNameFromReservationId}.
45
44
  * @returns List of TemporaryNetwork objects ready for the store.
46
45
  */
47
46
  export const convertReservationsToNetworks = (agentReservations, networkHocon, agentNetworkDefinition, agentNetworkName) => {
@@ -52,9 +51,45 @@ export const convertReservationsToNetworks = (agentReservations, networkHocon, a
52
51
  },
53
52
  // Use the explicit name when provided; fall back to extracting it from the reservation_id so that
54
53
  // networks are always deduplicated by name even when the backend omits AGENT_NETWORK_NAME_KEY.
55
- agentNetworkName: agentNetworkName ?? extractNetworkNameFromReservationId(reservation.reservation_id),
54
+ agentNetworkName: agentNetworkName ?? removeTrailingUuid(reservation.reservation_id),
56
55
  networkHocon,
57
56
  agentNetworkDefinition,
58
57
  }));
59
58
  };
60
- export { extractNetworkNameFromReservationId } from "../../state/TemporaryNetworks.js";
59
+ export const isEditableAgent = (displayAs) => displayAs === DisplayAs.LLM_AGENT;
60
+ /**
61
+ * Extracts TemporaryNetwork objects from a chat message by reading reservations, HOCON,
62
+ * agent network definition, and agent network name from the message / sly_data.
63
+ * @param message The chat message to extract from.
64
+ * @param agentNetworkDefinitionOverride Optional definition override (e.g. a locally-edited version) that
65
+ * takes precedence over whatever the backend echoes in sly_data.
66
+ * @returns A (possibly empty) array of TemporaryNetwork objects ready for the store.
67
+ */
68
+ export const extractTemporaryNetworksFromMessage = (message, agentNetworkDefinitionOverride) => {
69
+ const reservations = extractReservations(message);
70
+ if (reservations.length === 0)
71
+ return [];
72
+ const networkHocon = extractNetworkHocon(message);
73
+ const agentNetworkDefinition = agentNetworkDefinitionOverride ??
74
+ message.sly_data?.[AGENT_NETWORK_DEFINITION_KEY];
75
+ return convertReservationsToNetworks(reservations, networkHocon, agentNetworkDefinition);
76
+ };
77
+ /**
78
+ * Returns true when agentName refers to a known temporary network.
79
+ */
80
+ export const isTemporaryNetwork = (agentName, networks) => agentName !== null && networks.some((n) => n.agentInfo.agent_name === agentName);
81
+ /**
82
+ * Merges incoming networks into target, keeping the entry with the highest expiration time.
83
+ * Returns a new array; does not mutate either argument.
84
+ */
85
+ export const mergeNetworks = (target, incoming) => incoming.reduce((result, n) => {
86
+ const existingIdx = result.findIndex((e) => e.agentNetworkName === n.agentNetworkName);
87
+ // No existing entry with this name - append.
88
+ if (existingIdx < 0)
89
+ return [...result, n];
90
+ // Existing entry found - keep whichever reservation expires later.
91
+ if (n.reservation.expiration_time_in_seconds > result[existingIdx].reservation.expiration_time_in_seconds) {
92
+ return result.map((e, i) => (i === existingIdx ? n : e));
93
+ }
94
+ return result;
95
+ }, [...target]);
@@ -81,6 +81,13 @@ const TruncatedText = styled("div")(({ isHovered, isTruncated }) => ({
81
81
  }));
82
82
  // #endregion: Styled Components
83
83
  export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true, isStreaming = false, onBubbleHoverChange, }) => {
84
+ /**
85
+ * A pure, managed timestamp that can be safely referenced during renders.
86
+ * Instead of calling the impure Date.now() directly in the render phase (which violates React's purity rules
87
+ * and triggers linting warnings), the component captures timestamps at controlled moments and stores them
88
+ * in state.
89
+ */
90
+ const [time, setTime] = useState(() => Date.now());
84
91
  // hoveredBubbleId: id of currently hovered bubble (or null)
85
92
  const [hoveredBubbleId, setHoveredBubbleId] = useState(null);
86
93
  // truncatedBubbles: set of edge ids whose text overflows the collapsed box
@@ -101,6 +108,13 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
101
108
  // rAF ref for scheduling post-paint updates
102
109
  const rafRef = useRef(null);
103
110
  const mountedRef = useRef(true);
111
+ // Effect to update `time` every second to trigger re-renders for animation timing and line updates.
112
+ useEffect(() => {
113
+ const interval = setInterval(() => {
114
+ setTime(Date.now());
115
+ }, 250);
116
+ return () => clearInterval(interval);
117
+ }, []);
104
118
  // Filter edges with meaningful text (memoized to prevent infinite re-renders)
105
119
  const thoughtBubbleEdges = useMemo(() => edges.filter((e) => {
106
120
  const text = e.data?.text;
@@ -120,13 +134,12 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
120
134
  // Find new bubbles that should appear
121
135
  const newBubbles = thoughtBubbleEdges.filter((e) => !previousBubbleIds.has(e.id));
122
136
  // Find bubbles that should disappear
123
- const removingBubbles = Array.from(previousBubbleIds).filter((id) => !currentEdgeIds.has(id));
137
+ const removingBubbles = [...previousBubbleIds].filter((id) => !currentEdgeIds.has(id));
124
138
  const newState = new Map(prev);
125
139
  // Add new bubbles in entering state. Record when they entered so we can delay showing
126
140
  // connecting lines until the bubble's entrance animation delay.
127
- const now = Date.now();
128
141
  newBubbles.forEach((edge) => {
129
- newState.set(edge.id, { isVisible: true, isExiting: false, enteredAt: now });
142
+ newState.set(edge.id, { isVisible: true, isExiting: false, enteredAt: time });
130
143
  });
131
144
  // Mark removing bubbles as exiting
132
145
  removingBubbles.forEach((id) => {
@@ -152,7 +165,7 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
152
165
  });
153
166
  return newState;
154
167
  });
155
- }, [thoughtBubbleEdges]);
168
+ }, [thoughtBubbleEdges, time]);
156
169
  // Cleanup timeouts on unmount
157
170
  useEffect(() => {
158
171
  const timeouts = animationTimeouts.current;
@@ -178,9 +191,9 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
178
191
  for (const node of nodes) {
179
192
  const getConversations = node.data?.["getConversations"];
180
193
  if (typeof getConversations === "function") {
181
- const convs = getConversations();
182
- if (Array.isArray(convs)) {
183
- const hasSelf = convs.some((conv) => conv?.agents?.has?.(node.id));
194
+ const conversations = getConversations();
195
+ if (Array.isArray(conversations)) {
196
+ const hasSelf = conversations.some((conversation) => conversation?.agents?.has?.(node.id));
184
197
  if (hasSelf) {
185
198
  set.add(node.id);
186
199
  }
@@ -297,13 +310,13 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
297
310
  }
298
311
  return results;
299
312
  }, [activeAgentIds]);
300
- // Get all bubbles to render (including exiting ones)
301
- const allBubbleIds = Array.from(bubbleStates.keys());
302
313
  // Memoize the resolved edges so updateAllLines (and effects depending on it)
303
314
  // doesn't get recreated on every render unnecessarily.
304
- const renderableBubbles = useMemo(() => allBubbleIds
315
+ const renderableBubbles = useMemo(() =>
316
+ // Get all bubbles to render (including exiting ones)
317
+ [...bubbleStates.keys()]
305
318
  .map((id) => sortedEdges.find((e) => e.id === id) ?? edges.find((e) => e.id === id))
306
- .filter((edge) => edge !== undefined), [allBubbleIds, sortedEdges, edges]);
319
+ .filter((edge) => edge !== undefined), [bubbleStates, sortedEdges, edges]);
307
320
  // Update all SVG lines imperatively after paint. This reads DOM (getBoundingClientRect)
308
321
  // and writes attributes on existing <line> elements stored in `lineRefs`.
309
322
  const updateAllLines = useCallback(() => {
@@ -313,7 +326,7 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
313
326
  if (!bubbleState.isVisible || bubbleState.isExiting)
314
327
  return;
315
328
  // Respect the entrance animation delay gating (lines appear only after bubble start)
316
- const elapsed = Date.now() - (bubbleState?.enteredAt ?? 0);
329
+ const elapsed = time - (bubbleState?.enteredAt ?? 0);
317
330
  const animationDelay = index * LAYOUT_BUBBLES_ANIMATION_DELAY_MS;
318
331
  if (elapsed < animationDelay)
319
332
  return;
@@ -338,7 +351,7 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
338
351
  // Do not throw to avoid breaking the app
339
352
  console.error("ThoughtBubbleOverlay: updateAllLines error", err);
340
353
  }
341
- }, [renderableBubbles, bubbleStates, calculateLineCoordinates]);
354
+ }, [bubbleStates, calculateLineCoordinates, renderableBubbles, time]);
342
355
  // Schedule post-paint updates with rAF and ResizeObserver. Also, optionally run a
343
356
  // continuous loop while `isStreaming` is true to keep lines in sync during streaming.
344
357
  useEffect(() => {
@@ -416,7 +429,7 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
416
429
  if (!bubbleState.isVisible)
417
430
  return null;
418
431
  // Only render lines after the bubble's entrance animation delay has elapsed.
419
- const elapsed = Date.now() - (bubbleState.enteredAt || 0);
432
+ const elapsed = time - (bubbleState.enteredAt || 0);
420
433
  const shouldShowLines = elapsed >= animationDelay;
421
434
  if (!shouldShowLines)
422
435
  return null;
@@ -0,0 +1,7 @@
1
+ import { Step } from "react-joyride";
2
+ /**
3
+ * This file defines the steps for the main tour of the Multi-Agent Accelerator application.
4
+ *
5
+ * @see https://react-joyride.com/docs/step
6
+ */
7
+ export declare const MAIN_TOUR_STEPS: Step[];
@@ -0,0 +1,88 @@
1
+ /**
2
+ * This file defines the steps for the main tour of the Multi-Agent Accelerator application.
3
+ *
4
+ * @see https://react-joyride.com/docs/step
5
+ */
6
+ export const MAIN_TOUR_STEPS = [
7
+ {
8
+ // First step doesn't refer to any particular UI element, just a welcome message
9
+ content: "Welcome to Cognizant AI Lab Multi-Agent Accelerator! This tour will give you a quick overview of " +
10
+ "the application.",
11
+ target: () => document.querySelector("#multi-agent-accelerator-grid"),
12
+ placement: "center",
13
+ },
14
+ {
15
+ content: "This is the list of agent networks available on the server. Select a network to see its agents and " +
16
+ "start interacting with it!",
17
+ target: () => document.querySelector("#multi-agent-accelerator-sidebar-heading"),
18
+ placement: "bottom",
19
+ },
20
+ {
21
+ content: "Agent Network Designer: Click here to create and edit your own network, with the help of a " +
22
+ "powerful AI assistant.",
23
+ target: () => document.querySelector("#add-network-icon"),
24
+ placement: "bottom",
25
+ },
26
+ {
27
+ content: "Click this icon to connect to any Neuro SAN backend service. Mouse over it to see the current " +
28
+ "server you are connected to including the version.",
29
+ target: () => document.querySelector("#agent-network-settings-icon"),
30
+ placement: "bottom",
31
+ },
32
+ {
33
+ content: "These are the agents within the current network.",
34
+ target: () => document.querySelector("#multi-agent-accelerator-grid-agent-flow"),
35
+ placement: "auto",
36
+ },
37
+ {
38
+ content: "These buttons allow you to adjust the look of the agent flow to your liking. " +
39
+ "Mouse over them to see what they do!",
40
+ // get by aria-label "Control Panel"
41
+ target: () => document.querySelector('div[aria-label="Control Panel"]'),
42
+ },
43
+ {
44
+ content: "This is the legend for the agent flow. You can select either 'depth' or 'heatmap' view " +
45
+ "to see the corresponding information about the agents in the flow.",
46
+ target: () => document.querySelector("#multi-agent-accelerator-agent-flow-legend"),
47
+ },
48
+ {
49
+ content: "This is the chat window where you can interact with the currently selected network.",
50
+ target: () => document.querySelector("#llm-chat-agent-network-ui"),
51
+ placement: "auto",
52
+ },
53
+ {
54
+ content: "These are the sample queries for your currently selected network. Click one to send it to the agents!",
55
+ target: () => document.querySelector("#sample-queries-box"),
56
+ placement: "top",
57
+ },
58
+ {
59
+ content: "Try typing your query here and sending it to the agents by hitting <Enter>!",
60
+ target: () => document.querySelector("#user-input-div"),
61
+ placement: "top",
62
+ },
63
+ {
64
+ content: "These buttons allow you to control what is shown in the Chat window. " +
65
+ "Try mousing over them to see what they do!",
66
+ target: () => document.querySelector("#show-thinking-button"),
67
+ placement: "bottom",
68
+ },
69
+ {
70
+ content: "Access Settings from this icon. Here you can change the look and feel of the application, including " +
71
+ "auto branding for a particular customer!",
72
+ target: () => document.querySelector("#settings-icon"),
73
+ placement: "top",
74
+ },
75
+ {
76
+ content: "Select the items in this menu to learn more about Neuro-san, our open-source framework for building " +
77
+ "and running multi-agent systems, including access our GitHub repository with code samples " +
78
+ "and documentation.",
79
+ target: () => document.querySelector("#explore-dropdown"),
80
+ placement: "top",
81
+ },
82
+ {
83
+ content: "If you want to take the tour again, or if you have any questions or need help, click here to access " +
84
+ "the help information or to contact the Cognizant AI Labs team. We'd love to hear from you!",
85
+ target: () => document.querySelector("#help-dropdown"),
86
+ placement: "top",
87
+ },
88
+ ];
@@ -10,6 +10,7 @@ export declare const AGENT_PROGRESS_CONNECTIVITY_KEY = "connectivity_info";
10
10
  export declare const AGENT_NETWORK_DESIGNER_ID = "agent_network_designer";
11
11
  export declare const AGENT_NETWORK_DEFINITION_KEY = "agent_network_definition";
12
12
  export declare const AGENT_NETWORK_NAME_KEY = "agent_network_name";
13
+ export declare const TRIGGER_APP_TOUR_EVENT_NAME = "trigger-app-tour";
13
14
  /**
14
15
  * A single agent entry within an agent network definition, as received in sly_data from the backend.
15
16
  * Extends ConnectivityInfo with editable instructions and description fields for the Agent Network Designer.
@@ -19,13 +20,9 @@ export type AgentNetworkDefinitionEntry = ConnectivityInfo & {
19
20
  readonly description?: string;
20
21
  };
21
22
  /** Possible values for the `display_as` field in ConnectivityInfo / AgentNetworkDefinitionEntry. */
22
- export declare const DISPLAY_AS_LLM_AGENT = "llm_agent";
23
- export declare const DISPLAY_AS_CODED_TOOL = "coded_tool";
24
- export declare const DISPLAY_AS_LANGCHAIN_TOOL = "langchain_tool";
25
- export declare const DISPLAY_AS_EXTERNAL_AGENT = "external_agent";
26
- /**
27
- * Returns true when an agent node supports the edit popup (instructions + description).
28
- * Only `llm_agent` nodes (and the Frontman, whose `display_as` is undefined) are editable.
29
- * `coded_tool`, `langchain_tool`, `external_agent`, and any other types are read-only.
30
- */
31
- export declare const isEditableAgent: (displayAs: string | undefined) => boolean;
23
+ export declare enum DisplayAs {
24
+ LLM_AGENT = "llm_agent",
25
+ CODED_TOOL = "coded_tool",
26
+ LANGCHAIN_TOOL = "langchain_tool",
27
+ EXTERNAL_AGENT = "external_agent"
28
+ }
@@ -35,14 +35,13 @@ export const AGENT_NETWORK_DESIGNER_ID = "agent_network_designer";
35
35
  export const AGENT_NETWORK_DEFINITION_KEY = "agent_network_definition";
36
36
  // The key in sly_data where the agent network name is stored
37
37
  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";
38
40
  /** Possible values for the `display_as` field in ConnectivityInfo / AgentNetworkDefinitionEntry. */
39
- export const DISPLAY_AS_LLM_AGENT = "llm_agent";
40
- export const DISPLAY_AS_CODED_TOOL = "coded_tool";
41
- export const DISPLAY_AS_LANGCHAIN_TOOL = "langchain_tool";
42
- export const DISPLAY_AS_EXTERNAL_AGENT = "external_agent";
43
- /**
44
- * Returns true when an agent node supports the edit popup (instructions + description).
45
- * Only `llm_agent` nodes (and the Frontman, whose `display_as` is undefined) are editable.
46
- * `coded_tool`, `langchain_tool`, `external_agent`, and any other types are read-only.
47
- */
48
- export const isEditableAgent = (displayAs) => displayAs === DISPLAY_AS_LLM_AGENT;
41
+ export var DisplayAs;
42
+ (function (DisplayAs) {
43
+ DisplayAs["LLM_AGENT"] = "llm_agent";
44
+ DisplayAs["CODED_TOOL"] = "coded_tool";
45
+ DisplayAs["LANGCHAIN_TOOL"] = "langchain_tool";
46
+ DisplayAs["EXTERNAL_AGENT"] = "external_agent";
47
+ })(DisplayAs || (DisplayAs = {}));
package/dist/const.d.ts CHANGED
@@ -1,9 +1,13 @@
1
1
  export declare const LOGO: string;
2
2
  export declare const NEURO_SAN_UI_VERSION: string;
3
3
  export declare const getContactUsConfirmationText: (email: string) => string;
4
+ /**
5
+ * The default username to use when the user is not authenticated or does not have a name available, or when
6
+ * auentication is disabled
7
+ */
8
+ export declare const DEFAULT_USERNAME = "Guest";
4
9
  /**
5
10
  * The default user image to use when the user does not have a profile picture.
6
11
  */
7
12
  export declare const DEFAULT_USER_IMAGE = "https://www.gravatar.com/avatar/?d=mp";
8
13
  export declare const authenticationEnabled: () => boolean;
9
- export declare const DEFAULT_NEURO_SAN_SERVER_URL = "https://neuro-san-dev.decisionai.ml";
package/dist/const.js CHANGED
@@ -20,10 +20,13 @@ export const getContactUsConfirmationText = (email) => "Would you like to send t
20
20
  "You will need to have an email client installed on your device in order " +
21
21
  "to continue. If you don't have an email client, you can still contact us at " +
22
22
  `${email} using a web based email client.`;
23
+ /**
24
+ * The default username to use when the user is not authenticated or does not have a name available, or when
25
+ * auentication is disabled
26
+ */
27
+ export const DEFAULT_USERNAME = "Guest";
23
28
  /**
24
29
  * The default user image to use when the user does not have a profile picture.
25
30
  */
26
31
  export const DEFAULT_USER_IMAGE = "https://www.gravatar.com/avatar/?d=mp";
27
32
  export const authenticationEnabled = () => process.env["NEXT_PUBLIC_ENABLE_AUTHENTICATION"] !== "false";
28
- // Default "dev URL" for NeuroSan server, to allow for "zero config" execution.
29
- export const DEFAULT_NEURO_SAN_SERVER_URL = "https://neuro-san-dev.decisionai.ml";
@@ -1,3 +1,7 @@
1
+ /**
2
+ * Controller module for interacting with the Agent LLM API.
3
+ */
4
+ import { AgentNetworkDefinitionEntry } from "../../components/MultiAgentAccelerator/const.js";
1
5
  import { AgentInfo, ChatContext, ChatResponse, ConnectivityResponse, FunctionResponse } from "../../generated/neuro-san/NeuroSanClient.js";
2
6
  import { StreamingUnit } from "../llm/LlmChat.js";
3
7
  import { AgentIconSuggestions } from "../Types/AgentIconSuggestions.js";
@@ -76,3 +80,9 @@ export declare const getConnectivity: (url: string, network: string, userId: str
76
80
  * @throws Various exceptions if anything goes wrong such as network issues or invalid agent type.
77
81
  */
78
82
  export declare const getAgentFunction: (url: string, agent: string, userId: string) => Promise<FunctionResponse>;
83
+ /**
84
+ * Streams the Agent Network Designer endpoint with an updated agent definition.
85
+ * Calls `onChunk` for each line received; callers are responsible for parsing
86
+ * reservations / networks out of each chunk.
87
+ */
88
+ export declare const sendNetworkDesignerUpdate: (url: string, signal: AbortSignal, agentName: string, updated: AgentNetworkDefinitionEntry[], agentNetworkName: string | undefined, currentUser: string, onChunk: (chunk: string) => void) => Promise<void>;
@@ -16,7 +16,7 @@ limitations under the License.
16
16
  /**
17
17
  * Controller module for interacting with the Agent LLM API.
18
18
  */
19
- import { TEMPORARY_NETWORK_FOLDER } from "../../components/MultiAgentAccelerator/const.js";
19
+ import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_NAME_KEY, TEMPORARY_NETWORK_FOLDER, } from "../../components/MultiAgentAccelerator/const.js";
20
20
  import { ApiPaths,
21
21
  // eslint-disable-next-line camelcase
22
22
  ChatFilterChat_filter_type, ChatMessageType, } from "../../generated/neuro-san/NeuroSanClient.js";
@@ -213,3 +213,19 @@ export const getAgentFunction = async (url, agent, userId) => {
213
213
  }
214
214
  return response.json();
215
215
  };
216
+ /**
217
+ * Streams the Agent Network Designer endpoint with an updated agent definition.
218
+ * Calls `onChunk` for each line received; callers are responsible for parsing
219
+ * reservations / networks out of each chunk.
220
+ */
221
+ export const sendNetworkDesignerUpdate = async (url, signal, agentName, updated, agentNetworkName, currentUser, onChunk) => {
222
+ await sendChatQuery(url, signal,
223
+ // Shouldn't have to pass a user message, but API behaves different without it
224
+ `Update instructions for agent "${agentName}"`, AGENT_NETWORK_DESIGNER_ID, onChunk, null, {
225
+ [AGENT_NETWORK_DEFINITION_KEY]: updated,
226
+ // Use the backend's canonical name, not the local UUID-based key.
227
+ ...(agentNetworkName ? { [AGENT_NETWORK_NAME_KEY]: agentNetworkName } : {}),
228
+ // skip_designer prevents the backend from using a reasoning model for edits
229
+ skip_designer: true,
230
+ }, currentUser, StreamingUnit.Line);
231
+ };
@@ -56,9 +56,9 @@ const handleStreamingCallback = async (res, callback, streamingUnit) => {
56
56
  let newlineIndex;
57
57
  while ((newlineIndex = buffer.indexOf("\n")) !== -1) {
58
58
  // Extract the complete line (without the newline)
59
- const line = buffer.substring(0, newlineIndex).trim();
59
+ const line = buffer.slice(0, Math.max(0, newlineIndex)).trim();
60
60
  // Keep the rest for next iteration
61
- buffer = buffer.substring(newlineIndex + 1);
61
+ buffer = buffer.slice(Math.max(0, newlineIndex + 1));
62
62
  // Skip empty lines
63
63
  if (line.length > 0) {
64
64
  // Send the current line
package/dist/index.d.ts CHANGED
@@ -13,7 +13,6 @@ export * from "./components/Authentication/Auth.js";
13
13
  export * from "./components/Common/Breadcrumbs.js";
14
14
  export * from "./components/Common/ConfirmationModal.js";
15
15
  export * from "./components/Common/Footer.js";
16
- export * from "./components/Common/LlmChatOptionsButton.js";
17
16
  export * from "./components/Common/LoadingSpinner.js";
18
17
  export * from "./components/Common/MUIAccordion.js";
19
18
  export * from "./components/Common/MUIAlert.js";
package/dist/index.js CHANGED
@@ -28,7 +28,6 @@ export * from "./components/Authentication/Auth.js";
28
28
  export * from "./components/Common/Breadcrumbs.js";
29
29
  export * from "./components/Common/ConfirmationModal.js";
30
30
  export * from "./components/Common/Footer.js";
31
- export * from "./components/Common/LlmChatOptionsButton.js";
32
31
  export * from "./components/Common/LoadingSpinner.js";
33
32
  export * from "./components/Common/MUIAccordion.js";
34
33
  export * from "./components/Common/MUIAlert.js";
@@ -9,7 +9,7 @@ export type TemporaryNetwork = {
9
9
  readonly reservation: AgentReservation;
10
10
  readonly agentInfo: AgentInfo;
11
11
  /** The agent_network_name as sent by the backend — used when bouncing sly_data back to the backend. */
12
- readonly agentNetworkName?: string;
12
+ readonly agentNetworkName: string;
13
13
  readonly networkHocon?: string | null;
14
14
  readonly agentNetworkDefinition?: AgentNetworkDefinitionEntry[];
15
15
  };
@@ -21,24 +21,14 @@ interface TempNetworksStore {
21
21
  readonly setTempNetworks: (tempNetworks: TemporaryNetwork[]) => void;
22
22
  /**
23
23
  * Upsert new networks into the store. Networks are matched by the UUID-stripped portion of
24
- * their `reservation_id` (e.g. `"travel_agency_ops"` from `"travel_agency_ops-{uuid}"`), falling
25
- * back to `agentNetworkName` when the reservation_id has no UUID suffix. If an incoming network
24
+ * their `reservation_id` (e.g. `"travel_agency_ops"` from `"travel_agency_ops-{uuid}"`), or, just
25
+ * `agentNetworkName` which is the same as having the UUID-stripped. If an incoming network
26
26
  * matches an existing one, the existing entry is replaced. Returns the final list of upserted
27
27
  * networks (those that were added or replaced).
28
28
  */
29
- readonly upsertTempNetworks: (newNetworks: TemporaryNetwork[]) => TemporaryNetwork[];
30
- readonly updateTempNetworkDefinition: (networkName: string, definition: AgentNetworkDefinitionEntry[]) => void;
29
+ readonly upsertTempNetworks: (incomingNetworks: TemporaryNetwork[]) => TemporaryNetwork[];
30
+ readonly updateTempNetworkDefinition: (networkName: string, agentNetworkDefinition: AgentNetworkDefinitionEntry[]) => void;
31
31
  }
32
- /**
33
- * Derives the canonical network name from a reservation ID.
34
- *
35
- * The backend encodes the network name as a prefix in the reservation ID, followed by a UUID suffix:
36
- * `{network_name}-{uuid}`, e.g. `travel_agency_ops-7876642e-fe75-4d44-a61e-300688a1a6c5`.
37
- *
38
- * Stripping the UUID suffix gives the stable name that can be used for deduplication across reservations.
39
- * Returns `undefined` when the reservation ID doesn't match the expected format.
40
- */
41
- export declare const extractNetworkNameFromReservationId: (reservationId: string) => string | undefined;
42
32
  /**
43
33
  * The hook that lets apps use the store.
44
34
  */