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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/dist/Theme/Palettes.d.ts +0 -9
  2. package/dist/Theme/Palettes.js +0 -18
  3. package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +11 -7
  4. package/dist/components/AgentChat/ChatCommon/ChatCommon.js +119 -90
  5. package/dist/components/AgentChat/ChatCommon/Const.d.ts +1 -0
  6. package/dist/components/AgentChat/ChatCommon/Const.js +2 -0
  7. package/dist/components/AgentChat/ChatCommon/ControlButtons.d.ts +1 -1
  8. package/dist/components/AgentChat/ChatCommon/ControlButtons.js +7 -4
  9. package/dist/components/AgentChat/ChatCommon/FormattedMarkdown.js +3 -0
  10. package/dist/components/AgentChat/ChatCommon/SendButton.js +7 -6
  11. package/dist/components/AgentChat/ChatCommon/SyntaxHighlighterThemes.js +1 -1
  12. package/dist/components/AgentChat/ChatCommon/Thinking.d.ts +1 -0
  13. package/dist/components/AgentChat/ChatCommon/Thinking.js +10 -7
  14. package/dist/components/AgentChat/Common/LlmChatButton.d.ts +0 -2
  15. package/dist/components/AgentChat/Common/LlmChatButton.js +6 -9
  16. package/dist/components/AgentChat/VoiceChat/MicrophoneButton.js +12 -10
  17. package/dist/components/ChatBot/ChatBot.d.ts +0 -4
  18. package/dist/components/ChatBot/ChatBot.js +2 -2
  19. package/dist/components/Common/Navbar.js +24 -22
  20. package/dist/components/Common/StatusLight.d.ts +9 -0
  21. package/dist/components/Common/StatusLight.js +21 -0
  22. package/dist/components/ErrorPage/ErrorBoundary.js +1 -1
  23. package/dist/components/ErrorPage/ErrorPage.d.ts +3 -3
  24. package/dist/components/ErrorPage/ErrorPage.js +4 -3
  25. package/dist/components/Logo/Common.d.ts +4 -0
  26. package/dist/components/Logo/Common.js +8 -0
  27. package/dist/components/Logo/CustomerLogo.d.ts +23 -0
  28. package/dist/components/Logo/CustomerLogo.js +41 -0
  29. package/dist/components/MultiAgentAccelerator/AgentConversations.js +0 -1
  30. package/dist/components/MultiAgentAccelerator/AgentFlow.js +85 -53
  31. package/dist/components/MultiAgentAccelerator/AgentNode.js +23 -27
  32. package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +17 -10
  33. package/dist/components/MultiAgentAccelerator/GraphLayouts.js +4 -6
  34. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +3 -9
  35. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +102 -47
  36. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +0 -5
  37. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +30 -25
  38. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +3 -4
  39. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +84 -168
  40. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +30 -13
  41. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +157 -100
  42. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +10 -8
  43. package/dist/components/MultiAgentAccelerator/const.d.ts +2 -0
  44. package/dist/components/MultiAgentAccelerator/const.js +4 -0
  45. package/dist/components/Settings/ApiKeyInput.d.ts +16 -0
  46. package/dist/components/Settings/ApiKeyInput.js +69 -0
  47. package/dist/components/Settings/InfoTip.d.ts +8 -0
  48. package/dist/components/Settings/InfoTip.js +14 -0
  49. package/dist/components/Settings/SettingsDialog.js +379 -178
  50. package/dist/components/Settings/SettingsRow.d.ts +18 -0
  51. package/dist/components/Settings/SettingsRow.js +35 -0
  52. package/dist/controller/agent/Agent.d.ts +3 -20
  53. package/dist/controller/agent/Agent.js +9 -43
  54. package/dist/controller/agent/IconSuggestions.d.ts +35 -0
  55. package/dist/controller/agent/IconSuggestions.js +85 -0
  56. package/dist/controller/llm/Providers.d.ts +2 -0
  57. package/dist/controller/llm/Providers.js +41 -0
  58. package/dist/index.d.ts +0 -2
  59. package/dist/index.js +0 -2
  60. package/dist/state/IconSuggestions.d.ts +27 -0
  61. package/dist/state/IconSuggestions.js +50 -0
  62. package/dist/state/Settings.d.ts +19 -3
  63. package/dist/state/Settings.js +24 -1
  64. package/dist/tsconfig.build.tsbuildinfo +1 -1
  65. package/dist/utils/title.js +1 -1
  66. package/package.json +5 -1
  67. package/dist/components/Common/CustomerLogo.d.ts +0 -17
  68. package/dist/components/Common/CustomerLogo.js +0 -49
  69. package/dist/components/Common/MUIAccordion.d.ts +0 -17
  70. package/dist/components/Common/MUIAccordion.js +0 -66
  71. package/dist/utils/useLocalStorage.d.ts +0 -1
  72. package/dist/utils/useLocalStorage.js +0 -55
@@ -19,6 +19,7 @@ import Box from "@mui/material/Box";
19
19
  import Grid from "@mui/material/Grid";
20
20
  import Slide from "@mui/material/Slide";
21
21
  import { useTheme } from "@mui/material/styles";
22
+ import Tooltip from "@mui/material/Tooltip";
22
23
  import Typography from "@mui/material/Typography";
23
24
  import { ReactFlowProvider } from "@xyflow/react";
24
25
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
@@ -27,18 +28,18 @@ import { extractConversations } from "./AgentConversations.js";
27
28
  import { getUpdatedAgentCounts } from "./AgentCounts.js";
28
29
  import { AgentFlow } from "./AgentFlow.js";
29
30
  import { extractAgentNetworkDesignerProgress } from "./AgentNetworkDesigner.js";
30
- import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_HOCON, AGENT_NETWORK_NAME_KEY, TRIGGER_APP_TOUR_EVENT_NAME, } from "./const.js";
31
+ import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_HOCON, AGENT_NETWORK_NAME_KEY, GRACE_PERIOD_MS, SHOW_TOUR_DELAY_MS, TRIGGER_APP_TOUR_EVENT_NAME, } from "./const.js";
31
32
  import { Sidebar } from "./Sidebar/Sidebar.js";
32
33
  import { extractTemporaryNetworksFromMessage, isTemporaryNetwork, mergeNetworks } from "./TemporaryNetworks.js";
33
- import { getAgentFunction, getAgentIconSuggestions, getAgentNetworks, getConnectivity, getNetworkIconSuggestions, sendNetworkDesignerUpdate, } from "../../controller/agent/Agent.js";
34
+ import { getAgentFunction, getAgentNetworks, getConnectivity, sendNetworkDesignerUpdate, } from "../../controller/agent/Agent.js";
35
+ import { getAgentIconSuggestions, getNetworkIconSuggestions } from "../../controller/agent/IconSuggestions.js";
34
36
  import { useAgentChatHistoryStore } from "../../state/ChatHistory.js";
35
37
  import { useSettingsStore } from "../../state/Settings.js";
36
38
  import { useTempNetworksStore } from "../../state/TemporaryNetworks.js";
37
39
  import { TourPromptState, useTourStore } from "../../state/Tour.js";
38
- import { useLocalStorage } from "../../utils/useLocalStorage.js";
39
40
  import { getZIndex } from "../../utils/zIndexLayers.js";
40
41
  import { ChatCommon } from "../AgentChat/ChatCommon/ChatCommon.js";
41
- import { SmallLlmChatButton } from "../AgentChat/Common/LlmChatButton.js";
42
+ import { LlmChatButton } from "../AgentChat/Common/LlmChatButton.js";
42
43
  import { isLegacyAgentType } from "../AgentChat/Common/Types.js";
43
44
  import { chatMessageFromChunk, cleanUpAgentName, removeTrailingUuid } from "../AgentChat/Common/Utils.js";
44
45
  import { ConfirmationModal, StyledButton } from "../Common/ConfirmationModal.js";
@@ -47,14 +48,10 @@ import { MAIN_TOUR_STEPS } from "./Tour/MainTourSteps.js";
47
48
  import { MUIDialog } from "../Common/MUIDialog.js";
48
49
  // Check for expired networks every this many milliseconds
49
50
  const EXPIRED_NETWORKS_CHECK_INTERVAL_MS = 10 * 1000;
50
- // Display expired temporary networks for this amount of time after they expire so users can see what happened
51
- export const GRACE_PERIOD_MS = 5 * 60 * 1000; // 5 minutes
52
51
  // Animation time for the left and right panels to slide in or out when launching the animation
53
52
  const GROW_ANIMATION_TIME_MS = 800;
54
53
  // Optimization to avoid creating a new empty map on every render
55
54
  const EMPTY_THOUGHT_BUBBLE_EDGES = new Map();
56
- // We show the tour modal after this amount of time so as not to "pounce" on the user when they first open the app
57
- export const SHOW_TOUR_DELAY_MS = 5000;
58
55
  // #region: Agent-save helpers
59
56
  /**
60
57
  * Extracts TemporaryNetworks from a single streamed chunk, merging into `accumulated`.
@@ -76,7 +73,7 @@ const collectNetworksFromChunk = (chunk, updated, accumulated) => {
76
73
  return accumulated;
77
74
  }
78
75
  };
79
- /** Logs and notifies about a save error. Suppresses AbortError (user-cancelled). */
76
+ /** Logs and notifies about a save error. Suppresses AbortError (user-canceled). */
80
77
  const notifySaveError = (agentName, e) => {
81
78
  if (e instanceof DOMException && e.name === "AbortError")
82
79
  return;
@@ -90,13 +87,15 @@ const notifySaveError = (agentName, e) => {
90
87
  /**
91
88
  * Main Multi-Agent Accelerator component that contains the sidebar, agent flow, and chat components.
92
89
  * @param backendNeuroSanApiUrl Initial URL of the backend Neuro-San API. User can change this in the UI.
93
- * @param darkMode Whether dark mode is enabled.
94
- * @param userInfo Information about the current user, including userName and userImage.
90
+ * @param username Identifier to use for interactions with the backend (for personalization and tracking).
95
91
  */
96
- export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
92
+ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
97
93
  // MUI theme
98
94
  const theme = useTheme();
99
95
  const enableZenMode = useSettingsStore((state) => state.settings.behavior.enableZenMode);
96
+ const apiKeys = useSettingsStore((state) => state.settings.apiKeys);
97
+ // Display option for agent/network names
98
+ const useNativeNames = useSettingsStore((state) => state.settings.appearance.useNativeNames);
100
99
  // Stores whether are currently awaiting LLM response (for knowing when to show spinners)
101
100
  const [isAwaitingLlm, setIsAwaitingLlm] = useState(false);
102
101
  const [isEditingNetwork, setIsEditingNetwork] = useState(false);
@@ -117,13 +116,12 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
117
116
  const [agentIconSuggestions, setAgentIconSuggestions] = useState(null);
118
117
  const [selectedNetwork, setSelectedNetwork] = useState(null);
119
118
  const [networkDescription, setNetworkDescription] = useState("");
120
- const networkDisplayName = useMemo(() => cleanUpAgentName(removeTrailingUuid(selectedNetwork)), [selectedNetwork]);
121
- const [customURLLocalStorage, setCustomURLLocalStorage] = useLocalStorage("customAgentNetworkURL", null);
122
- // An extra set of quotes is making it in the string in local storage.
123
- const [neuroSanURL, setNeuroSanURL] = useState(customURLLocalStorage?.replaceAll('"', "") || backendNeuroSanApiUrl);
119
+ const networkDisplayName = useMemo(() => (useNativeNames ? selectedNetwork : cleanUpAgentName(removeTrailingUuid(selectedNetwork))), [selectedNetwork, useNativeNames]);
120
+ const [providerKeysRequired, setProviderKeysRequired] = useState(new Set());
121
+ const neuroSanURL = useSettingsStore((state) => state.settings.externalServices.neuroSanUrl) ?? defaultNeuroSanUrl;
124
122
  // Tracks how many times each agent has been involved in the conversation
125
123
  const [agentCounts, setAgentCounts] = useState(new Map());
126
- //common function to change the selected network and reset related state
124
+ // Common function to change the selected network and reset related state
127
125
  const changeSelectedNetwork = useCallback((next) => {
128
126
  setSelectedNetwork(next);
129
127
  setAgentCounts(new Map());
@@ -136,10 +134,6 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
136
134
  const [confirmationModalOpen, setConfirmationModalOpen] = useState(false);
137
135
  const [tourModalOpen, setTourModalOpen] = useState(false);
138
136
  const [haveShownTourModal, setHaveShownTourModal] = useState(false);
139
- const customURLCallback = useCallback((url) => {
140
- setNeuroSanURL(url || backendNeuroSanApiUrl);
141
- setCustomURLLocalStorage(url === "" ? null : url);
142
- }, [backendNeuroSanApiUrl, setCustomURLLocalStorage]);
143
137
  // Memoized key for agent names to trigger icon suggestion updates when the set of agents changes, not just
144
138
  // when sorting/other operations on the agents list
145
139
  const agentNamesKey = useMemo(() => agentsInNetwork
@@ -203,23 +197,62 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
203
197
  const designerTempNetwork = designerNetworkName
204
198
  ? temporaryNetworks.find((n) => n.agentNetworkName === designerNetworkName)
205
199
  : undefined;
206
- const extraSlyData = currentTempNetwork
200
+ /**
201
+ * Builds the API keys object to be sent as extraSlyData with each request, based on the LLM providers required
202
+ * by the currently selected network. Only includes keys for providers that are required.
203
+ */
204
+ const getApiKeys = () => {
205
+ const llmConfig = {};
206
+ if (providerKeysRequired.has("OpenAI")) {
207
+ llmConfig["openai_api_key"] = apiKeys["OpenAI"];
208
+ }
209
+ if (providerKeysRequired.has("Anthropic")) {
210
+ llmConfig["anthropic_api_key"] = apiKeys["Anthropic"];
211
+ }
212
+ return { llm_config: llmConfig };
213
+ };
214
+ /**
215
+ * Builds the extraSlyData object to be sent with each request, including information for Agent Network Designer
216
+ * and (if required) API keys for LLM providers.
217
+ */
218
+ const buildExtraSlyData = () => {
219
+ if (currentTempNetwork) {
220
+ const result = {
221
+ [AGENT_NETWORK_DEFINITION_KEY]: currentTempNetwork.agentNetworkDefinition,
222
+ };
223
+ // Use agentNetworkName, not reservation_id
224
+ if (currentTempNetwork.agentNetworkName) {
225
+ result[AGENT_NETWORK_NAME_KEY] = currentTempNetwork.agentNetworkName;
226
+ }
227
+ if (currentTempNetwork.networkHocon) {
228
+ result[AGENT_NETWORK_HOCON] = currentTempNetwork.networkHocon;
229
+ }
230
+ return result;
231
+ }
232
+ if (designerTempNetwork) {
233
+ return {
234
+ [AGENT_NETWORK_DEFINITION_KEY]: designerTempNetwork.agentNetworkDefinition,
235
+ };
236
+ }
237
+ return undefined;
238
+ };
239
+ // Whether any API keys are required
240
+ const anyApiKeysRequired = providerKeysRequired.size > 0;
241
+ // Build base extraSlyData
242
+ const baseExtraSlyData = buildExtraSlyData();
243
+ // Add API keys to extraSlyData if needed, merging with baseExtraSlyData if it exists
244
+ const extraSlyData = anyApiKeysRequired || baseExtraSlyData
207
245
  ? {
208
- [AGENT_NETWORK_DEFINITION_KEY]: currentTempNetwork.agentNetworkDefinition,
209
- // Use the agentNetworkName, not reservation_id
210
- ...(currentTempNetwork.agentNetworkName
211
- ? { [AGENT_NETWORK_NAME_KEY]: currentTempNetwork.agentNetworkName }
212
- : {}),
213
- ...(currentTempNetwork.networkHocon ? { [AGENT_NETWORK_HOCON]: currentTempNetwork.networkHocon } : {}),
246
+ ...baseExtraSlyData,
247
+ ...(anyApiKeysRequired ? getApiKeys() : {}),
214
248
  }
215
- : designerTempNetwork
216
- ? { [AGENT_NETWORK_DEFINITION_KEY]: designerTempNetwork.agentNetworkDefinition }
217
- : undefined;
249
+ : undefined;
218
250
  // Handle external stop button click - stops streaming and exits zen mode
219
251
  const handleExternalStop = useCallback(() => {
220
252
  chatRef.current?.handleStop();
221
253
  resetState();
222
254
  }, [resetState]);
255
+ const isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
223
256
  useEffect(() => {
224
257
  ;
225
258
  (async () => {
@@ -240,11 +273,26 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
240
273
  const fetchAgentDetails = async () => {
241
274
  // It is a Neuro-san agent, so get the function and connectivity info
242
275
  try {
243
- const agentFunction = await getAgentFunction(neuroSanURL, selectedNetwork, userInfo.userName);
276
+ const agentFunction = await getAgentFunction(neuroSanURL, selectedNetwork, username);
244
277
  setNetworkDescription(agentFunction?.function?.description || "");
278
+ const schema = agentFunction?.function?.sly_data_schema;
279
+ const schemaProperties = isRecord(schema) ? schema["properties"] : undefined;
280
+ const llmConfig = isRecord(schemaProperties) ? schemaProperties["llm_config"] : undefined;
281
+ const llmConfigRequired = isRecord(llmConfig) ? llmConfig["required"] : undefined;
282
+ setProviderKeysRequired(new Set((Array.isArray(llmConfigRequired) ? llmConfigRequired : []).flatMap((key) => {
283
+ if (key === "openai_api_key") {
284
+ return ["OpenAI"];
285
+ }
286
+ if (key === "anthropic_api_key") {
287
+ return ["Anthropic"];
288
+ }
289
+ console.warn(`Unknown API key requirement "${key}" for network ${selectedNetwork}. Skipping.`);
290
+ // Will get dropped by flatMap
291
+ return [];
292
+ })));
245
293
  }
246
- catch {
247
- // Ignore. May be a legacy agent without a functional description in Neuro-san.
294
+ catch (e) {
295
+ console.warn(`Unable to get agent details for network ${selectedNetwork}:`, e);
248
296
  }
249
297
  };
250
298
  // Clear out existing
@@ -252,7 +300,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
252
300
  if (selectedNetwork && !isLegacyAgentType(selectedNetwork)) {
253
301
  void fetchAgentDetails();
254
302
  }
255
- }, [neuroSanURL, selectedNetwork, userInfo.userName]);
303
+ }, [neuroSanURL, selectedNetwork, username]);
256
304
  useEffect(() => {
257
305
  ;
258
306
  (async () => {
@@ -273,7 +321,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
273
321
  (async () => {
274
322
  if (selectedNetwork) {
275
323
  try {
276
- const connectivity = await getConnectivity(neuroSanURL, selectedNetwork, userInfo.userName);
324
+ const connectivity = await getConnectivity(neuroSanURL, selectedNetwork, username);
277
325
  const agentsInNetworkSorted = [...connectivity.connectivity_info].sort((a, b) => a?.origin.localeCompare(b?.origin));
278
326
  setAgentsInNetwork(agentsInNetworkSorted);
279
327
  const sampleQueriesTmp = connectivity?.metadata?.["sample_queries"];
@@ -296,7 +344,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
296
344
  setAgentsInNetwork([]);
297
345
  }
298
346
  })();
299
- }, [networkDisplayName, neuroSanURL, selectedNetwork, userInfo.userName]);
347
+ }, [networkDisplayName, neuroSanURL, selectedNetwork, username]);
300
348
  useEffect(() => {
301
349
  ;
302
350
  (async () => {
@@ -419,7 +467,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
419
467
  const onSaveAgent = useCallback(async (agentName, updated, agentNetworkName, signal) => {
420
468
  try {
421
469
  let newNetworks = [];
422
- await sendNetworkDesignerUpdate(neuroSanURL, signal, agentName, updated, agentNetworkName, userInfo.userName, (chunk) => {
470
+ await sendNetworkDesignerUpdate(neuroSanURL, signal, agentName, updated, agentNetworkName, username, (chunk) => {
423
471
  newNetworks = collectNetworksFromChunk(chunk, updated, newNetworks);
424
472
  });
425
473
  if (newNetworks.length === 0) {
@@ -443,7 +491,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
443
491
  catch (e) {
444
492
  notifySaveError(agentName, e);
445
493
  }
446
- }, [neuroSanURL, userInfo.userName, selectedNetwork]);
494
+ }, [neuroSanURL, username, selectedNetwork]);
447
495
  const onStreamingStarted = useCallback(() => {
448
496
  // Reset agent counts
449
497
  setAgentCounts(new Map());
@@ -506,12 +554,15 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
506
554
  setTourRequested(false);
507
555
  }
508
556
  }, [tourRequested, selectedNetwork, agentsInNetwork, networks, controls, setTourStatus]);
557
+ const getMissingApiKeys = () => {
558
+ return providerKeysRequired.size > 0 ? [...providerKeysRequired].filter((provider) => !apiKeys?.[provider]) : [];
559
+ };
509
560
  const getLeftPanel = () => {
510
561
  return (_jsx(Slide, { id: "multi-agent-accelerator-grid-sidebar-slide", in: !enableZenMode || !isAwaitingLlm, direction: "right", timeout: GROW_ANIMATION_TIME_MS, onExited: () => {
511
562
  setIsStreaming(true);
512
563
  }, children: _jsx(Grid, { id: "multi-agent-accelerator-grid-sidebar", size: enableZenMode && isStreaming ? 0 : 3.25, sx: {
513
564
  height: "100%",
514
- }, children: _jsx(Sidebar, { customURLLocalStorage: customURLLocalStorage, customURLCallback: customURLCallback, id: "multi-agent-accelerator-sidebar", isAwaitingLlm: isAwaitingLlm, networks: networks, networkIconSuggestions: networkIconSuggestions, newlyAddedTemporaryNetworks: newlyAddedTemporaryNetworks, onEditNetwork: handleEditNetwork, onDeleteNetwork: handleDeleteNetwork, setSelectedNetwork: (newNetwork) => changeSelectedNetwork(newNetwork), temporaryNetworks: temporaryNetworks }) }) }));
565
+ }, children: _jsx(Sidebar, { id: "multi-agent-accelerator-sidebar", isAwaitingLlm: isAwaitingLlm, networkIconSuggestions: networkIconSuggestions, networks: networks, neuroSanServerURL: neuroSanURL, newlyAddedTemporaryNetworks: newlyAddedTemporaryNetworks, onDeleteNetwork: handleDeleteNetwork, onEditNetwork: handleEditNetwork, setSelectedNetwork: (newNetwork) => changeSelectedNetwork(newNetwork), temporaryNetworks: temporaryNetworks }) }) }));
515
566
  };
516
567
  const getCenterPanel = () => {
517
568
  return (_jsx(Grid, { id: "multi-agent-accelerator-grid-agent-flow", size: enableZenMode && isStreaming ? 18 : 8.25, sx: {
@@ -524,26 +575,30 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
524
575
  height: "100%",
525
576
  maxWidth: 1000,
526
577
  margin: "0 auto",
527
- }, children: _jsx(AgentFlow, { agentCounts: agentCounts, agentsInNetwork: agentsInNetwork, agentIconSuggestions: agentIconSuggestions, id: "multi-agent-accelerator-agent-flow", currentConversations: currentConversations, currentUser: userInfo.userName, isAwaitingLlm: isAwaitingLlm, isEditMode: isEditingNetwork, isStreaming: isStreaming, isSelectedNetworkTemporary: isSelectedNetworkTemporary, networkDisplayName: selectedNetwork || undefined, networkId: isSelectedNetworkTemporary ? selectedNetwork : undefined, neuroSanURL: neuroSanURL, onEnterEditMode: () => setIsEditingNetwork(true), onExitEditMode: () => setIsEditingNetwork(false), onNetworkReplaced: (_old, newId) => changeSelectedNetwork(newId), onSaveAgent: onSaveAgent, thoughtBubbleEdges: thoughtBubbleEdges, setThoughtBubbleEdges: setThoughtBubbleEdges }, "multi-agent-accelerator-agent-flow") }) }) }));
578
+ }, children: _jsx(AgentFlow, { agentCounts: agentCounts, agentsInNetwork: agentsInNetwork, agentIconSuggestions: agentIconSuggestions, id: "multi-agent-accelerator-agent-flow", currentConversations: currentConversations, currentUser: username, isAwaitingLlm: isAwaitingLlm, isEditMode: isEditingNetwork, isStreaming: isStreaming, isSelectedNetworkTemporary: isSelectedNetworkTemporary, networkDisplayName: networkDisplayName || "", networkId: isSelectedNetworkTemporary ? selectedNetwork : "", neuroSanURL: neuroSanURL, onEnterEditMode: () => setIsEditingNetwork(true), onExitEditMode: () => setIsEditingNetwork(false), onNetworkReplaced: (_old, newId) => changeSelectedNetwork(newId), onSaveAgent: onSaveAgent, thoughtBubbleEdges: thoughtBubbleEdges, setThoughtBubbleEdges: setThoughtBubbleEdges }, "multi-agent-accelerator-agent-flow") }) }) }));
528
579
  };
529
580
  const getRightPanel = () => {
530
581
  return (_jsx(Slide, { id: "multi-agent-accelerator-grid-agent-chat-common-slide", in: !enableZenMode || !isAwaitingLlm, direction: "left", timeout: GROW_ANIMATION_TIME_MS, onExited: () => {
531
582
  setIsStreaming(true);
532
583
  }, children: _jsx(Grid, { id: "multi-agent-accelerator-grid-agent-chat-common", size: enableZenMode && isStreaming ? 0 : 6.5, sx: {
533
584
  height: "100%",
534
- }, children: _jsx(ChatCommon, { customAgentGreetings: {
535
- [AGENT_NETWORK_DESIGNER_ID]: "Let's build a network together!",
536
- }, agentPlaceholders: {
585
+ }, children: _jsx(ChatCommon, { agentPlaceholders: {
537
586
  [AGENT_NETWORK_DESIGNER_ID]: "Describe in plain language the network you would like to build.",
538
- }, currentUser: userInfo.userName, extraSlyData: extraSlyData, id: "agent-network-ui", isAwaitingLlm: isAwaitingLlm, networkDescription: networkDescription, neuroSanURL: neuroSanURL, onChunkReceived: onChunkReceived, onStreamingComplete: onStreamingComplete, onStreamingStarted: onStreamingStarted, ref: chatRef, sampleQueries: sampleQueries, setIsAwaitingLlm: setIsAwaitingLlm, targetAgent: selectedNetwork, userImage: userInfo.userImage }, selectedNetwork ?? "no-network") }) }));
587
+ }, currentUser: username, customAgentGreetings: {
588
+ [AGENT_NETWORK_DESIGNER_ID]: "Let's build a network together!",
589
+ }, extraSlyData: extraSlyData, id: "agent-network-ui", isAwaitingLlm: isAwaitingLlm, missingApiKeys: getMissingApiKeys(), networkDescription: networkDescription, neuroSanURL: neuroSanURL, onChunkReceived: onChunkReceived, onStreamingComplete: onStreamingComplete, onStreamingStarted: onStreamingStarted, ref: chatRef, sampleQueries: sampleQueries, setIsAwaitingLlm: setIsAwaitingLlm, selectedNetwork: selectedNetwork, setSelectedNetwork: changeSelectedNetwork }, selectedNetwork ?? "no-network") }) }));
539
590
  };
540
591
  const getStopButton = () => {
541
592
  return (_jsx(_Fragment, { children: isAwaitingLlm && enableZenMode && (_jsx(Box, { id: "stop-button-container", sx: {
542
593
  position: "absolute",
543
594
  bottom: "1rem",
544
595
  right: "1rem",
545
- zIndex: 10,
546
- }, children: _jsx(SmallLlmChatButton, { "aria-label": "Stop", disabled: !isAwaitingLlm, id: "stop-output-button", onClick: handleExternalStop, posBottom: 8, posRight: 23, children: _jsx(StopCircle, { fontSize: "small", id: "stop-button-icon", sx: { color: "var(--bs-white)" } }) }) })) }));
596
+ zIndex: getZIndex(2, theme),
597
+ }, children: _jsx(Tooltip, { title: "Stop response (Esc)", children: _jsx(LlmChatButton, { "aria-label": "Stop", id: "stop-output-button", onClick: handleExternalStop, sx: {
598
+ "&:hover": {
599
+ backgroundColor: "error.main",
600
+ },
601
+ }, children: _jsx(StopCircle, { fontSize: "large", id: "stop-button-icon" }) }) }) })) }));
547
602
  };
548
603
  const getDeleteNetworkConfirmationModal = () => confirmationModalOpen ? (_jsx(ConfirmationModal, { id: "delete-network-confirmation-modal", content: `The network "${cleanUpAgentName(removeTrailingUuid(networkToBeDeleted))}" will be deleted. ` +
549
604
  "This action cannot be undone. Are you sure you want to proceed?", handleCancel: () => {
@@ -1,13 +1,8 @@
1
1
  import { TreeItemProps } from "@mui/x-tree-view/TreeItem";
2
2
  import { FC } from "react";
3
- import { NodeIndex } from "./TreeBuilder.js";
4
3
  export interface AgentNetworkNodeProps extends TreeItemProps {
5
- readonly nodeIndex: NodeIndex;
6
4
  readonly onDeleteNetwork?: (network: string, isExpired: boolean) => void;
7
5
  readonly onEditNetwork?: (network: string) => void;
8
- readonly networkIconSuggestions: Record<string, string>;
9
- readonly temporaryNetworkExpirationTimes?: Record<string, Date>;
10
- readonly temporaryNetworkHoconStrings?: Record<string, string | null>;
11
6
  }
12
7
  /**
13
8
  * Custom Tree Item for MUI RichTreeView to display agent networks with tags
@@ -9,14 +9,13 @@ 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 { useTheme } from "@mui/material/styles";
13
12
  import Tooltip from "@mui/material/Tooltip";
13
+ import { useTreeItemModel } from "@mui/x-tree-view/hooks";
14
14
  import { TreeItemContent, TreeItemGroupTransition, TreeItemLabel, TreeItemRoot, } from "@mui/x-tree-view/TreeItem";
15
15
  import { TreeItemProvider } from "@mui/x-tree-view/TreeItemProvider";
16
16
  import { useTreeItem } from "@mui/x-tree-view/useTreeItem";
17
17
  import { useRef } from "react";
18
18
  import { downloadFile, toSafeFilename } from "../../../utils/File.js";
19
- import { cleanUpAgentName } from "../../AgentChat/Common/Utils.js";
20
19
  // Palette of colors we can use for tags
21
20
  const TAG_COLORS = [
22
21
  "--bs-accent2-light",
@@ -44,21 +43,15 @@ const isTemporaryNetworkExpired = (expirationDate) => {
44
43
  * @param props - see AgentNetworkNode interface
45
44
  * @returns JSX.Element containing the custom tree item
46
45
  */
47
- export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networkIconSuggestions, nodeIndex, onDeleteNetwork, onEditNetwork, temporaryNetworkExpirationTimes, temporaryNetworkHoconStrings, }) => {
48
- const theme = useTheme();
46
+ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, onDeleteNetwork, onEditNetwork, }) => {
47
+ const item = useTreeItemModel(itemId);
49
48
  // We know all labels are strings because we set them that way in the tree view items
50
49
  const labelString = label;
51
- const displayLabel = nodeIndex.get(itemId)?.displayName || cleanUpAgentName(labelString);
52
50
  const { getContextProviderProps, getRootProps, getContentProps, getLabelProps, getGroupTransitionProps } = useTreeItem({ itemId, children, label, disabled });
53
51
  const rootRef = useRef(null);
54
52
  const isParent = Array.isArray(children) && children.length > 0;
55
53
  const isChild = !isParent;
56
- const agentNode = nodeIndex?.get(itemId)?.agentInfo;
57
- // Only child items (the actual networks, not the containing folders) have tags. Retrieve tags from the
58
- // networkFolders data structure passed in as a prop. This could in theory be a custom property for the
59
- // RichTreeView item, but that isn't well-supported at this time.
60
- // Discussion: https://stackoverflow.com/questions/69481071/material-ui-how-to-pass-custom-props-to-a-custom-treeitem
61
- const tags = isChild ? agentNode?.tags || [] : [];
54
+ const tags = item.tags ?? [];
62
55
  // Assign colors to tags as needed and store in tagsToColors map
63
56
  for (const tag of tags) {
64
57
  if (!tagsToColors.has(tag)) {
@@ -67,18 +60,33 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networ
67
60
  }
68
61
  }
69
62
  // Determine if expired (temporary networks only)
70
- const expirationTime = temporaryNetworkExpirationTimes?.[itemId];
63
+ const expirationTime = item?.temporaryNetworkExpirationTime;
71
64
  const isTemporaryNetwork = Boolean(expirationTime);
72
65
  const isExpired = isChild && isTemporaryNetwork && isTemporaryNetworkExpired(expirationTime);
73
- const networkHocon = isTemporaryNetwork ? temporaryNetworkHoconStrings?.[itemId] : null;
74
- const iconNameSuggestion = isTemporaryNetwork ? "HourglassTop" : isChild ? networkIconSuggestions?.[itemId] : null;
75
- let muiIconElement = null;
76
- if (iconNameSuggestion && MuiIcons[iconNameSuggestion]) {
77
- const IconComponent = MuiIcons[iconNameSuggestion];
78
- muiIconElement = _jsx(IconComponent, { sx: { fontSize: "1rem" } });
66
+ const networkHocon = item?.temporaryNetworkHocon ?? null;
67
+ const iconNameSuggestion = item.iconSuggestion;
68
+ let muiIconElement;
69
+ // If the item is a child (i.e., a network), we want to render an icon next to it.
70
+ if (isChild) {
71
+ if (iconNameSuggestion) {
72
+ if (MuiIcons[iconNameSuggestion]) {
73
+ // If the icon name suggestion is valid, use it to render the icon
74
+ const IconComponent = MuiIcons[iconNameSuggestion];
75
+ muiIconElement = _jsx(IconComponent, { sx: { fontSize: "1rem" } });
76
+ }
77
+ else {
78
+ // If the icon name suggestion is not valid, use a default icon
79
+ muiIconElement = _jsx(MuiIcons.Hub, { sx: { fontSize: "1rem" } });
80
+ }
81
+ }
82
+ else {
83
+ // If no icon name suggestion is provided, use a default icon
84
+ muiIconElement = _jsx(MuiIcons.Hub, { sx: { fontSize: "1rem" } });
85
+ }
79
86
  }
80
- else if (iconNameSuggestion) {
81
- console.warn(`Icon "${iconNameSuggestion}" not found in MUI icons library.`);
87
+ else {
88
+ // Use folder icon for parent items (i.e., folders)
89
+ muiIconElement = _jsx(MuiIcons.Folder, { sx: { fontSize: "1rem" } });
82
90
  }
83
91
  return (_jsx(TreeItemProvider, { ...getContextProviderProps(), children: _jsxs(TreeItemRoot, { ...getRootProps(), ref: rootRef, "data-itemid": itemId, children: [_jsx(TreeItemContent, { ...getContentProps(), sx: {
84
92
  cursor: isExpired ? "not-allowed" : "pointer",
@@ -92,16 +100,13 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networ
92
100
  "&:hover": {
93
101
  textDecoration: "underline",
94
102
  },
95
- }, children: displayLabel })] }) }), isChild && tags?.length > 0 ? (_jsx(Tooltip, { title: [...tags]
103
+ }, children: item.displayName })] }) }), isChild && tags.length > 0 ? (_jsx(Tooltip, { title: [...tags]
96
104
  .sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()))
97
105
  .map((tag) => (_jsx(Chip, { label: tag, style: {
98
106
  margin: "0.25rem",
99
107
  backgroundColor: `var(${tagsToColors.get(tag) || TAG_COLORS[0]})`,
100
108
  } }, 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", children: _jsx("span", { children: _jsx(IconButton, { onClick: (e) => {
101
109
  e.stopPropagation();
102
- if (isExpired) {
103
- return;
104
- }
105
110
  const fileName = `${toSafeFilename(labelString)}.hocon`;
106
111
  downloadFile(networkHocon, fileName);
107
112
  }, disabled: isExpired, "aria-label": "Download network definition", size: "small", sx: {
@@ -128,7 +133,7 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networ
128
133
  onDeleteNetwork?.(itemId, isExpired);
129
134
  }, sx: {
130
135
  color: "var(--bs-secondary)",
131
- "&:hover": { color: theme.palette.warning.main },
136
+ "&:hover": { color: (theme) => theme.palette.warning.main },
132
137
  "&.Mui-disabled": {
133
138
  color: "var(--bs-secondary)",
134
139
  opacity: 0.3,
@@ -4,16 +4,15 @@ import { AgentInfo } from "../../../generated/neuro-san/NeuroSanClient.js";
4
4
  import { TemporaryNetwork } from "../../../state/TemporaryNetworks.js";
5
5
  export declare const SPARKLE_HIGHLIGHT_CLASS = "sparkle-highlight";
6
6
  export interface SidebarProps {
7
- readonly customURLCallback: (url: string) => void;
8
- readonly customURLLocalStorage?: string;
9
7
  readonly id: string;
10
8
  readonly isAwaitingLlm: boolean;
11
9
  readonly networkIconSuggestions?: NetworkIconSuggestions;
12
10
  readonly networks: readonly AgentInfo[];
13
- readonly onEditNetwork?: (network: string) => void;
11
+ readonly neuroSanServerURL: string;
12
+ readonly newlyAddedTemporaryNetworks?: Set<string>;
14
13
  readonly onDeleteNetwork?: (network: string, isExpired: boolean) => void;
14
+ readonly onEditNetwork?: (network: string) => void;
15
15
  readonly setSelectedNetwork: (network: string) => void;
16
16
  readonly temporaryNetworks?: readonly TemporaryNetwork[];
17
- readonly newlyAddedTemporaryNetworks?: Set<string>;
18
17
  }
19
18
  export declare const Sidebar: FC<SidebarProps>;