@cognizant-ai-lab/ui-common 1.8.0 → 1.9.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 (56) hide show
  1. package/README.md +3 -3
  2. package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +9 -3
  3. package/dist/components/AgentChat/ChatCommon/ChatCommon.js +89 -23
  4. package/dist/components/AgentChat/ChatCommon/ChatHistory.d.ts +7 -0
  5. package/dist/components/AgentChat/ChatCommon/ChatHistory.js +1 -1
  6. package/dist/components/AgentChat/ChatCommon/ControlButtons.d.ts +4 -2
  7. package/dist/components/AgentChat/ChatCommon/ControlButtons.js +8 -2
  8. package/dist/components/AgentChat/ChatCommon/ConversationTurn.d.ts +5 -5
  9. package/dist/components/AgentChat/ChatCommon/ConversationTurn.js +1 -0
  10. package/dist/components/AgentChat/Common/Utils.d.ts +4 -0
  11. package/dist/components/AgentChat/Common/Utils.js +5 -1
  12. package/dist/components/Common/Breadcrumbs.js +1 -1
  13. package/dist/components/Common/notification.d.ts +4 -4
  14. package/dist/components/MultiAgentAccelerator/{AgentFlow.d.ts → AgentFlow/AgentFlow.d.ts} +15 -5
  15. package/dist/components/MultiAgentAccelerator/{AgentFlow.js → AgentFlow/AgentFlow.js} +44 -35
  16. package/dist/components/MultiAgentAccelerator/{AgentNode.d.ts → AgentFlow/AgentNode.d.ts} +1 -1
  17. package/dist/components/MultiAgentAccelerator/{AgentNode.js → AgentFlow/AgentNode.js} +4 -4
  18. package/dist/components/MultiAgentAccelerator/{GraphLayouts.d.ts → AgentFlow/GraphLayouts.d.ts} +4 -4
  19. package/dist/components/MultiAgentAccelerator/{GraphLayouts.js → AgentFlow/GraphLayouts.js} +6 -35
  20. package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.d.ts +21 -0
  21. package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.js +27 -0
  22. package/dist/components/MultiAgentAccelerator/AgentFlow/PlasmaEdge.d.ts +7 -0
  23. package/dist/components/MultiAgentAccelerator/{PlasmaEdge.js → AgentFlow/PlasmaEdge.js} +13 -8
  24. package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +116 -78
  25. package/dist/components/MultiAgentAccelerator/Schema/SlyData.d.ts +17 -0
  26. package/dist/components/MultiAgentAccelerator/Schema/SlyData.js +23 -0
  27. package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +9 -4
  28. package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.d.ts +67 -0
  29. package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.js +585 -0
  30. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +2 -0
  31. package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +19 -26
  32. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +2 -1
  33. package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +24 -4
  34. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +22 -0
  35. package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +74 -3
  36. package/dist/components/MultiAgentAccelerator/{ThoughtBubbleEdge.d.ts → ThoughtBubbles/ThoughtBubbleEdge.d.ts} +1 -1
  37. package/dist/components/MultiAgentAccelerator/{ThoughtBubbleOverlay.js → ThoughtBubbles/ThoughtBubbleOverlay.js} +1 -1
  38. package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +5 -0
  39. package/dist/components/MultiAgentAccelerator/const.d.ts +1 -0
  40. package/dist/components/MultiAgentAccelerator/const.js +2 -0
  41. package/dist/components/Settings/SettingsDialog.js +26 -10
  42. package/dist/controller/agent/Agent.d.ts +14 -6
  43. package/dist/controller/agent/Agent.js +18 -14
  44. package/dist/index.d.ts +1 -1
  45. package/dist/index.js +1 -1
  46. package/dist/state/Settings.d.ts +81 -4
  47. package/dist/state/Settings.js +87 -7
  48. package/dist/tsconfig.build.tsbuildinfo +1 -1
  49. package/dist/utils/BrowserNavigation.js +2 -0
  50. package/dist/utils/File.d.ts +4 -1
  51. package/dist/utils/File.js +4 -9
  52. package/dist/utils/text.js +3 -7
  53. package/package.json +9 -10
  54. package/dist/components/MultiAgentAccelerator/PlasmaEdge.d.ts +0 -3
  55. /package/dist/components/MultiAgentAccelerator/{ThoughtBubbleEdge.js → ThoughtBubbles/ThoughtBubbleEdge.js} +0 -0
  56. /package/dist/components/MultiAgentAccelerator/{ThoughtBubbleOverlay.d.ts → ThoughtBubbles/ThoughtBubbleOverlay.d.ts} +0 -0
package/README.md CHANGED
@@ -72,7 +72,7 @@ via the chat interface, and visualize the agent network flow.
72
72
  Among the technologies and libraries used in this package are:
73
73
 
74
74
  - ESLint and Prettier (for code quality)
75
- - Jest and React Testing Library (for testing)
75
+ - Vitest and React Testing Library (for testing)
76
76
  - MUI
77
77
  - next-auth (for authentication)
78
78
  - React
@@ -160,9 +160,9 @@ async function checkServer() {
160
160
  const result = await testConnection("https://api.example.com")
161
161
 
162
162
  if (result.success) {
163
- console.log(`Connected! Version: ${result.version}`)
163
+ console.log(`Connected!`)
164
164
  } else {
165
- console.error(`Connection failed: ${result.status}`)
165
+ console.error(`Connection failed: ${result.statusText}`)
166
166
  }
167
167
  }
168
168
  ```
@@ -1,5 +1,5 @@
1
1
  import { Dispatch, Ref, SetStateAction } from "react";
2
- import { LLMProvider } from "../../../state/Settings.js";
2
+ import { MessageRole } from "./ConversationTurn.js";
3
3
  import { CombinedAgentType } from "../Common/Types.js";
4
4
  export interface ChatCommonProps {
5
5
  /**
@@ -96,14 +96,20 @@ export interface ChatCommonProps {
96
96
  */
97
97
  readonly sampleQueries?: string[];
98
98
  /**
99
- * Array of LLM providers for which API keys are required but missing. Only applies to BYOK networks.
99
+ * If true, indicates that the user is missing API keys for one or more LLM providers.
100
100
  */
101
- readonly missingApiKeys?: LLMProvider[];
101
+ readonly hasMissingApiKeys?: boolean;
102
102
  }
103
103
  export type ChatCommonHandle = {
104
104
  handleStop: () => void;
105
105
  handleClearChat: () => void;
106
106
  };
107
+ /**
108
+ * Helper function to convert a message role to a label for exporting chat history.
109
+ * @param role The message role to convert
110
+ * @returns The label for the message role
111
+ */
112
+ export declare const roleToExportLabel: (role: MessageRole) => string;
107
113
  /**
108
114
  * Common chat component for agent chat. This component is used by all agent chat components to provide a consistent
109
115
  * experience for users when chatting with agents. It handles user input as well as displaying and nicely formatting
@@ -37,7 +37,7 @@ import Typography from "@mui/material/Typography";
37
37
  import { isEmpty } from "lodash-es";
38
38
  import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, } from "react";
39
39
  import { v4 as uuid } from "uuid";
40
- import { ChatHistory } from "./ChatHistory.js";
40
+ import { ChatHistory, toTurns } from "./ChatHistory.js";
41
41
  import { MAX_TURNS } from "./Const.js";
42
42
  import { ControlButtons } from "./ControlButtons.js";
43
43
  import { Conversation } from "./Conversation.js";
@@ -50,8 +50,10 @@ import { sendLlmRequest, StreamingUnit } from "../../../controller/llm/LlmChat.j
50
50
  import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
51
51
  import { useAgentChatHistoryStore } from "../../../state/ChatHistory.js";
52
52
  import { useSettingsStore } from "../../../state/Settings.js";
53
+ import { downloadFile, toSafeFilename } from "../../../utils/File.js";
53
54
  import { hasOnlyWhitespace } from "../../../utils/text.js";
54
55
  import { getZIndex } from "../../../utils/zIndexLayers.js";
56
+ import { ConfirmationModal } from "../../Common/ConfirmationModal.js";
55
57
  import { AGENT_NETWORK_DESIGNER_ID } from "../../MultiAgentAccelerator/const.js";
56
58
  import { givesFinalAnswer, isLegacyAgentType } from "../Common/Types.js";
57
59
  import { chatMessageFromChunk, checkError, cleanUpAgentName, removeTrailingUuid } from "../Common/Utils.js";
@@ -70,14 +72,29 @@ const MAX_AGENT_RETRIES = 3;
70
72
  * @returns The final answer from the agent, if it exists or undefined if it doesn't
71
73
  */
72
74
  const extractFinalAnswer = (response) => /Final Answer: (?<finalAnswerText>.*)/su.exec(response)?.groups?.["finalAnswerText"];
75
+ // List of message roles that are exported when saving chat history.
76
+ const EXPORTED_MESSAGE_TYPES = [MessageRole.User, MessageRole.FinalAnswer, MessageRole.Warning, MessageRole.Error];
77
+ const EXPORT_ROLE_LABELS = {
78
+ [MessageRole.User]: "User",
79
+ [MessageRole.Agent]: "Assistant",
80
+ [MessageRole.FinalAnswer]: "Assistant",
81
+ [MessageRole.Warning]: "Warning",
82
+ [MessageRole.Error]: "Error",
83
+ };
73
84
  //#endregion
85
+ /**
86
+ * Helper function to convert a message role to a label for exporting chat history.
87
+ * @param role The message role to convert
88
+ * @returns The label for the message role
89
+ */
90
+ export const roleToExportLabel = (role) => EXPORT_ROLE_LABELS[role];
74
91
  /**
75
92
  * Common chat component for agent chat. This component is used by all agent chat components to provide a consistent
76
93
  * experience for users when chatting with agents. It handles user input as well as displaying and nicely formatting
77
94
  * agent responses. Customization for inputs and outputs is provided via event handlers-like props.
78
95
  */
79
96
  export const ChatCommon = ({ ref, ...props }) => {
80
- const { agentPlaceholders = EMPTY, backgroundColor, currentUser, customAgentGreetings = EMPTY, extraParams, extraSlyData, id, isAwaitingLlm, legacyAgentEndpoint, missingApiKeys = [], networkDescription, neuroSanURL, onChunkReceived, onClose, onSend, onStreamingComplete, onStreamingStarted, sampleQueries, selectedNetwork, setIsAwaitingLlm, setPreviousResponse, setSelectedNetwork, title, } = props;
97
+ const { agentPlaceholders = EMPTY, backgroundColor, currentUser, customAgentGreetings = EMPTY, extraParams, extraSlyData, id, isAwaitingLlm, legacyAgentEndpoint, hasMissingApiKeys = false, networkDescription, neuroSanURL, onChunkReceived, onClose, onSend, onStreamingComplete, onStreamingStarted, sampleQueries, selectedNetwork, setIsAwaitingLlm, setPreviousResponse, setSelectedNetwork, title, } = props;
81
98
  // MUI theme
82
99
  const theme = useTheme();
83
100
  // User LLM chat input
@@ -106,6 +123,10 @@ export const ChatCommon = ({ ref, ...props }) => {
106
123
  // (see store implementation for details)
107
124
  const storedChatHistory = useAgentChatHistoryStore((state) => selectedNetwork ? state?.history?.[selectedNetwork] : undefined);
108
125
  const agentChatHistory = useMemo(() => storedChatHistory ?? { chatHistory: [], chatContext: null, slyData: {} }, [storedChatHistory]);
126
+ // For saving chat history. Specifically, exclude "Agent" messages as they are verbose and not useful in a saved
127
+ // export.
128
+ const exportableTurns = turns.filter((turn) => EXPORTED_MESSAGE_TYPES.includes(turn.role));
129
+ const enableSaveChatButton = turns.length > 0 || agentChatHistory?.chatHistory?.length > 0;
109
130
  // Display option for agent/network names
110
131
  const useNativeNames = useSettingsStore((state) => state.settings.appearance.useNativeNames);
111
132
  // Access store for context items
@@ -475,6 +496,44 @@ export const ChatCommon = ({ ref, ...props }) => {
475
496
  resetState();
476
497
  }
477
498
  }, [addTurn, resetState]);
499
+ const formatTurns = (items) => items.map((turn) => `${roleToExportLabel(turn.role)}: ${turn.text}`).join("\n\n");
500
+ /**
501
+ * Save the current chat history and session to a file
502
+ */
503
+ const handleSaveChat = () => {
504
+ const chatHistory = agentChatHistory?.chatHistory;
505
+ const chatHistoryTurns = toTurns(chatHistory);
506
+ // Get the current date and time in a human-readable format, including the local time zone
507
+ const exportedAt = new Date();
508
+ const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
509
+ const exportedAtText = exportedAt.toLocaleString(undefined, {
510
+ year: "numeric",
511
+ month: "short",
512
+ day: "numeric",
513
+ hour: "numeric",
514
+ minute: "2-digit",
515
+ second: "2-digit",
516
+ timeZoneName: "short",
517
+ });
518
+ const allHistoryText = [
519
+ "Chat export",
520
+ `Network: ${selectedNetwork}`,
521
+ `Exported: ${exportedAtText} (${timeZone})`,
522
+ "",
523
+ "====================",
524
+ "Previous history",
525
+ "====================",
526
+ formatTurns(chatHistoryTurns) || "No persisted chat history.",
527
+ "",
528
+ "====================",
529
+ "Current session",
530
+ "====================",
531
+ formatTurns(exportableTurns) || "No current session turns.",
532
+ ].join("\n");
533
+ // Download the file
534
+ const filename = `${toSafeFilename(`${selectedNetwork}-history`)}.txt`;
535
+ downloadFile(allHistoryText, filename, "text/plain");
536
+ };
478
537
  // Regex to check if user has typed anything besides whitespace
479
538
  const userInputEmpty = !chatInput || chatInput.length === 0 || hasOnlyWhitespace(chatInput);
480
539
  // Enable Send button when there is user input and not awaiting a response
@@ -483,7 +542,8 @@ export const ChatCommon = ({ ref, ...props }) => {
483
542
  const shouldEnableRegenerateButton = previousUserQuery && !isAwaitingLlm;
484
543
  // Enable Clear Chat button if not awaiting response and there is chat output to clear
485
544
  const enableClearChatButton = !isAwaitingLlm && (turns.length > 0 || agentChatHistory?.chatHistory?.length > 0);
486
- const getPlaceholder = () => selectedNetwork ? agentPlaceholders[selectedNetwork] || `Chat with ${networkDisplayName}` : null;
545
+ const [clearChatConfirmationDialogOpen, setClearChatConfirmationDialogOpen] = useState(false);
546
+ const getPlaceholder = () => agentPlaceholders[selectedNetwork] || `Chat with ${networkDisplayName}`;
487
547
  const handleClearChat = useCallback(() => {
488
548
  setTurns([]);
489
549
  resetHistory(selectedNetwork);
@@ -563,14 +623,14 @@ export const ChatCommon = ({ ref, ...props }) => {
563
623
  right: theme.spacing(3),
564
624
  position: "absolute",
565
625
  zIndex: getZIndex(2, theme),
566
- }, children: _jsx(ControlButtons, { enableClearChatButton: enableClearChatButton, handleClearChat: handleClearChat, handleSend: handleSend, handleStop: handleStop, isAwaitingLlm: isAwaitingLlm, previousUserQuery: previousUserQuery, shouldEnableRegenerateButton: shouldEnableRegenerateButton }) }));
626
+ }, children: _jsx(ControlButtons, { enableClearChatButton: enableClearChatButton, enableSaveChatButton: enableSaveChatButton, handleClearChat: () => setClearChatConfirmationDialogOpen(true), handleSave: handleSaveChat, handleSend: handleSend, handleStop: handleStop, isAwaitingLlm: isAwaitingLlm, previousUserQuery: previousUserQuery, shouldEnableRegenerateButton: shouldEnableRegenerateButton }) }));
567
627
  const getWorkingSpinner = () => (_jsx(_Fragment, { children: isAwaitingLlm && (_jsxs(Box, { id: "awaitingOutputContainer", sx: { display: "flex", alignItems: "center", fontSize: "smaller" }, children: [_jsx("span", { id: "working-span", style: { marginRight: "1rem" }, children: "Working..." }), _jsx(CircularProgress, { id: "awaitingOutputSpinner", sx: {
568
628
  color: "var(--bs-primary)",
569
629
  }, size: "1rem" })] })) }));
570
630
  const getThinking = () => (_jsx(_Fragment, { children: !isAwaitingLlm && turns.length > 0 && (
571
631
  // Only show thinking once streaming is complete
572
632
  _jsx(Thinking, { id: id, turns: turns, useNativeNames: useNativeNames })) }));
573
- const getResponseBox = () => (_jsx(Box, { id: "llm-response-div", sx: {
633
+ const getResponseBox = () => (_jsxs(Box, { id: "llm-response-div", sx: {
574
634
  ...divStyle,
575
635
  border: "var(--bs-border-width) var(--bs-border-style)",
576
636
  borderRadius: "var(--bs-border-radius)",
@@ -579,18 +639,27 @@ export const ChatCommon = ({ ref, ...props }) => {
579
639
  marginLeft: "10px",
580
640
  position: "relative",
581
641
  overflowY: "auto",
582
- }, children: _jsxs(Box, { id: "llm-responses", ref: chatOutputRef, sx: {
583
- backgroundColor,
584
- borderRadius: "0.5rem",
585
- fontSize: "16px",
586
- overflowY: "auto",
587
- paddingBottom: "60px",
588
- paddingLeft: "15px",
589
- paddingRight: "15px",
590
- paddingTop: "7.5px",
591
- scrollbarGutter: "stable",
592
- width: "100%",
593
- }, tabIndex: -1, children: [getOptionsMenu(), getOptionsMenuButton(), getChatHistory(), getNetworkIntro(), getAgentGreeting(), _jsx(SampleQueries, { disabled: isAwaitingLlm, handleSend: handleSend, sampleQueries: sampleQueries }), _jsx(Conversation, { id: id, includeAgentMessages: !givesFinalAnswer(selectedNetwork), shouldWrapOutput: shouldWrapOutput, turns: turns }), getThinking(), getWorkingSpinner(), getControlButtons()] }) }));
642
+ }, children: [clearChatConfirmationDialogOpen ? (_jsx(ConfirmationModal, { id: `${id}-clear-chat-confirmation-modal`, content: "This will clear the current chat session and any persisted chat history for this " +
643
+ "network. This operation cannot be undone. Are you sure you wish to continue?", handleCancel: () => {
644
+ setClearChatConfirmationDialogOpen(false);
645
+ }, handleOk: () => {
646
+ setClearChatConfirmationDialogOpen(false);
647
+ setTurns([]);
648
+ resetHistory(selectedNetwork);
649
+ setPreviousUserQuery("");
650
+ currentResponse.current = "";
651
+ }, okBtnLabel: "Yes, clear chat", title: `Clear all chat including history for ${networkDisplayName}?` })) : null, _jsxs(Box, { id: "llm-responses", ref: chatOutputRef, sx: {
652
+ backgroundColor,
653
+ borderRadius: "0.5rem",
654
+ fontSize: "16px",
655
+ overflowY: "auto",
656
+ paddingBottom: "60px",
657
+ paddingLeft: "15px",
658
+ paddingRight: "15px",
659
+ paddingTop: "7.5px",
660
+ scrollbarGutter: "stable",
661
+ width: "100%",
662
+ }, tabIndex: -1, children: [getOptionsMenu(), getOptionsMenuButton(), getChatHistory(), getNetworkIntro(), getAgentGreeting(), _jsx(SampleQueries, { disabled: isAwaitingLlm, handleSend: handleSend, sampleQueries: sampleQueries }), _jsx(Conversation, { id: id, includeAgentMessages: !givesFinalAnswer(selectedNetwork), shouldWrapOutput: shouldWrapOutput, turns: turns }), getThinking(), getWorkingSpinner(), getControlButtons()] })] }));
594
663
  const getUserInputBox = () => (_jsxs(Box, { id: "user-input-div", sx: {
595
664
  ...divStyle,
596
665
  alignItems: "center",
@@ -642,15 +711,12 @@ export const ChatCommon = ({ ref, ...props }) => {
642
711
  color: "var(--bs-secondary)",
643
712
  fontSize: "1rem",
644
713
  } }) }), "to design your own network!"] }));
645
- const allApiKeysPresent = missingApiKeys?.length === 0;
646
- const getMissingApiKeysOverlayBody = () => (_jsx(Typography, { component: "span", children: `API key(s) required for: ${missingApiKeys.join(", ")}. Please add the required key(s) in
647
- "Settings" to use this Network.` }));
648
714
  return (_jsx(Box, { id: `llm-chat-${id}`, sx: {
649
715
  height: "100%",
650
716
  position: "relative",
651
717
  }, children: selectedNetwork
652
- ? allApiKeysPresent
653
- ? getChatBox()
654
- : getErrorOverlay(getMissingApiKeysOverlayBody())
718
+ ? hasMissingApiKeys
719
+ ? getErrorOverlay("")
720
+ : getChatBox() // Parent will display error for this case
655
721
  : getErrorOverlay(getSelectNetworkOverlayBody()) }));
656
722
  };
@@ -1,9 +1,16 @@
1
1
  import { BaseMessage } from "@langchain/core/messages";
2
2
  import { FC } from "react";
3
+ import { ConversationTurn } from "./ConversationTurn.js";
3
4
  interface ChatHistoryProps {
4
5
  readonly messages: BaseMessage[];
5
6
  readonly id: string;
6
7
  }
8
+ /**
9
+ * Helper function to convert from BaseMessage format used in persisted chat history to ConversationTurn format used for
10
+ * rendering the conversation.
11
+ * @param chatHistory
12
+ */
13
+ export declare const toTurns: (chatHistory: BaseMessage[]) => ConversationTurn[];
7
14
  /**
8
15
  * Component for displaying chat history from previous interactions with the agent.
9
16
  */
@@ -10,7 +10,7 @@ import { AccordionLite } from "../../Common/AccordionLite.js";
10
10
  * rendering the conversation.
11
11
  * @param chatHistory
12
12
  */
13
- const toTurns = (chatHistory) => chatHistory
13
+ export const toTurns = (chatHistory) => chatHistory
14
14
  .filter((message) => message.type === "human" || message.type === "ai")
15
15
  .map((message) => {
16
16
  const role = message.type === "human" ? MessageRole.User : MessageRole.Agent;
@@ -1,10 +1,12 @@
1
1
  import { FC } from "react";
2
2
  interface ControlButtonsProps {
3
- handleClearChat: () => void;
4
3
  enableClearChatButton: boolean;
5
- isAwaitingLlm: boolean;
4
+ enableSaveChatButton: boolean;
5
+ handleClearChat: () => void;
6
+ handleSave: () => void;
6
7
  handleSend: (query: string) => void;
7
8
  handleStop: () => void;
9
+ isAwaitingLlm: boolean;
8
10
  previousUserQuery: string;
9
11
  shouldEnableRegenerateButton: boolean;
10
12
  }
@@ -16,14 +16,20 @@ limitations under the License.
16
16
  */
17
17
  import DeleteOutlined from "@mui/icons-material/DeleteOutlined";
18
18
  import Loop from "@mui/icons-material/Loop";
19
+ import SaveIcon from "@mui/icons-material/Save";
19
20
  import StopCircle from "@mui/icons-material/StopCircle";
20
21
  import Box from "@mui/material/Box";
22
+ import Tooltip from "@mui/material/Tooltip";
21
23
  import { SmallLlmChatButton } from "../Common/LlmChatButton.js";
22
24
  //#endregion: Types
23
25
  /**
24
26
  * Generate the Control Buttons for a chat window (resend, clear chat, stop)
25
27
  * @returns A fragment containing the Control Buttons.
26
28
  */
27
- export const ControlButtons = ({ handleClearChat, enableClearChatButton, isAwaitingLlm, handleSend, handleStop, previousUserQuery, shouldEnableRegenerateButton, }) => (_jsx(_Fragment, { children: isAwaitingLlm ? (
29
+ export const ControlButtons = ({ enableClearChatButton, enableSaveChatButton, handleClearChat, handleSave, handleSend, handleStop, isAwaitingLlm, previousUserQuery, shouldEnableRegenerateButton, }) => (_jsx(_Fragment, { children: isAwaitingLlm ? (
28
30
  // Stop Button
29
- _jsx(SmallLlmChatButton, { "aria-label": "Stop", disabled: !isAwaitingLlm, id: "stop-output-button", onClick: () => handleStop(), children: _jsx(StopCircle, { fontSize: "small", id: "stop-button-icon" }) })) : (_jsxs(Box, { sx: { display: "flex", gap: 1 }, children: [_jsx(SmallLlmChatButton, { "aria-label": "Clear Chat", disabled: !enableClearChatButton, id: "clear-chat-button", onClick: handleClearChat, children: _jsx(DeleteOutlined, { fontSize: "small", id: "stop-button-icon" }) }), _jsx(SmallLlmChatButton, { "aria-label": "Regenerate", disabled: !shouldEnableRegenerateButton, id: "regenerate-output-button", onClick: () => handleSend(previousUserQuery), children: _jsx(Loop, { fontSize: "small", id: "generate-icon" }) })] })) }));
31
+ _jsx(Tooltip, { title: "Terminate request", children: _jsx(SmallLlmChatButton, { "aria-label": "Stop", disabled: !isAwaitingLlm, id: "stop-output-button", onClick: () => handleStop(), children: _jsx(StopCircle, { fontSize: "small", id: "stop-button-icon" }) }) })) : (_jsxs(Box, { sx: { display: "flex", gap: 1 }, children: [_jsx(Tooltip, { title: "Save Chat", children: _jsx("span", { children: _jsx(SmallLlmChatButton, { "aria-label": "Save Chat", disabled: !enableSaveChatButton, id: "save-chat-button", onClick: handleSave, children: _jsx(SaveIcon, { fontSize: "small", id: "save-button-icon" }) }) }) }), _jsx(Tooltip, { title: "Clear Chat", children: _jsx("span", { children: _jsx(SmallLlmChatButton, { "aria-label": "Clear Chat", disabled: !enableClearChatButton, id: "clear-chat-button", onClick: handleClearChat, children: _jsx(DeleteOutlined, { fontSize: "small", id: "stop-button-icon", sx: {
32
+ "&:hover": {
33
+ color: "error.main",
34
+ },
35
+ } }) }) }) }), _jsx(Tooltip, { title: "Resend last query", children: _jsx("span", { children: _jsx(SmallLlmChatButton, { "aria-label": "Regenerate", disabled: !shouldEnableRegenerateButton, id: "regenerate-output-button", onClick: () => handleSend(previousUserQuery), children: _jsx(Loop, { fontSize: "small", id: "generate-icon" }) }) }) })] })) }));
@@ -3,11 +3,11 @@ import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js"
3
3
  * The various messages roles in a conversation turn.
4
4
  */
5
5
  export declare enum MessageRole {
6
- "Agent" = "Agent",
7
- "Error" = "Error",
8
- "FinalAnswer" = "FinalAnswer",
9
- "User" = "User",
10
- "Warning" = "Warning"
6
+ Agent = "Agent",
7
+ Error = "Error",
8
+ FinalAnswer = "FinalAnswer",
9
+ User = "User",
10
+ Warning = "Warning"
11
11
  }
12
12
  /**
13
13
  * Represents a single turn in a conversation, which may include a message from the user or agent, or an error
@@ -4,6 +4,7 @@
4
4
  export var MessageRole;
5
5
  (function (MessageRole) {
6
6
  MessageRole["Agent"] = "Agent";
7
+ // eslint-disable-next-line no-shadow
7
8
  MessageRole["Error"] = "Error";
8
9
  MessageRole["FinalAnswer"] = "FinalAnswer";
9
10
  MessageRole["User"] = "User";
@@ -3,6 +3,10 @@ export declare const KNOWN_MESSAGE_TYPES: ChatMessageType[];
3
3
  export declare const KNOWN_MESSAGE_TYPES_FOR_PLASMA: ChatMessageType[];
4
4
  export declare const chatMessageFromChunk: (chunk: string) => ChatMessage | null;
5
5
  export declare const checkError: (chatMessageJson: Record<string, unknown>) => string | null;
6
+ /**
7
+ * Strip a trailing canonical (hyphen-delimited) UUID appended to an agent name or
8
+ * reservation_id, e.g. `copy_cat-hello_world-14ecb260-4389-44f3-afad-ea315dfa1966`.
9
+ */
6
10
  export declare const removeTrailingUuid: (agentName: string) => string;
7
11
  /**
8
12
  * Convert FOO_BAR to more human "Foo Bar".
@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
14
14
  limitations under the License.
15
15
  */
16
16
  import { isEmpty } from "lodash-es";
17
- import startCase from "lodash-es/startCase.js";
17
+ import startCase from "lodash-es/startCase";
18
18
  import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
19
19
  // We ignore any messages that are not of these types
20
20
  export const KNOWN_MESSAGE_TYPES = [
@@ -60,6 +60,10 @@ export const checkError = (chatMessageJson) => {
60
60
  const { error, traceback, tool } = chatMessageJson;
61
61
  return `Error occurred. Error: "${error}", traceback: "${traceback}", tool: "${tool}"`;
62
62
  };
63
+ /**
64
+ * Strip a trailing canonical (hyphen-delimited) UUID appended to an agent name or
65
+ * reservation_id, e.g. `copy_cat-hello_world-14ecb260-4389-44f3-afad-ea315dfa1966`.
66
+ */
63
67
  export const removeTrailingUuid = (agentName) => {
64
68
  return agentName?.replace(/-[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/u, "");
65
69
  };
@@ -18,7 +18,7 @@ import ChevronRightIcon from "@mui/icons-material/ChevronRight";
18
18
  import Breadcrumbs from "@mui/material/Breadcrumbs";
19
19
  import Grid from "@mui/material/Grid";
20
20
  import Typography from "@mui/material/Typography";
21
- import startCase from "lodash-es/startCase.js";
21
+ import startCase from "lodash-es/startCase";
22
22
  export const NeuroAIBreadcrumbs = ({ pathname }) => {
23
23
  const urlPaths = pathname?.split("/").filter((path) => path !== "");
24
24
  const pageName = startCase(urlPaths?.at(-1));
@@ -1,10 +1,10 @@
1
1
  import { SnackbarOrigin } from "notistack";
2
2
  import { JSX as ReactJSX } from "react";
3
3
  export declare enum NotificationType {
4
- "success" = 0,
5
- "info" = 1,
6
- "warning" = 2,
7
- "error" = 3
4
+ success = 0,
5
+ info = 1,
6
+ warning = 2,
7
+ error = 3
8
8
  }
9
9
  export declare const closeNotification: () => void;
10
10
  /**
@@ -1,9 +1,11 @@
1
+ import { NodeChange, Node as RFNode } from "@xyflow/react";
1
2
  import { Dispatch, FC, SetStateAction } from "react";
2
- import { AgentConversation } from "./AgentConversations.js";
3
- import { AgentNetworkDefinitionEntry } from "./const.js";
4
- import { ThoughtBubbleEdgeShape } from "./ThoughtBubbleEdge.js";
5
- import { AgentIconSuggestions } from "../../controller/Types/AgentIconSuggestions.js";
6
- import { ConnectivityInfo } from "../../generated/neuro-san/NeuroSanClient.js";
3
+ import { AgentConversation } from "../AgentConversations.js";
4
+ import { AgentNodeProps } from "./AgentNode.js";
5
+ import { AgentNetworkDefinitionEntry } from "../const.js";
6
+ import { AgentIconSuggestions } from "../../../controller/Types/AgentIconSuggestions.js";
7
+ import { ConnectivityInfo } from "../../../generated/neuro-san/NeuroSanClient.js";
8
+ import { ThoughtBubbleEdgeShape } from "../ThoughtBubbles/ThoughtBubbleEdge.js";
7
9
  export interface AgentFlowProps {
8
10
  readonly agentCounts?: Map<string, number>;
9
11
  readonly agentIconSuggestions?: AgentIconSuggestions;
@@ -40,4 +42,12 @@ export interface AgentFlowProps {
40
42
  }>>>;
41
43
  }
42
44
  export declare const DOCK_BANNER_AUTO_DISMISS_MS = 5000;
45
+ /**
46
+ * Filters node events based on the current mode. Don't allow any topological-modifying events (adding, deleting nodes)
47
+ * and in Agent Network Designer mode, don't allow dragging nodes (position changes) either.
48
+ * @param change The node change event to filter.
49
+ * @param isAgentNetworkDesignerMode Whether the flow is in Agent Network Designer mode (read-only preview).
50
+ * @return True if the event should be allowed, false if it should be filtered out.
51
+ */
52
+ export declare const filterNodeEvents: (change: NodeChange<RFNode<AgentNodeProps>>, isAgentNetworkDesignerMode: boolean) => boolean;
43
53
  export declare const AgentFlow: FC<AgentFlowProps>;
@@ -35,32 +35,32 @@ import Typography from "@mui/material/Typography";
35
35
  import { applyNodeChanges, Background, ConnectionMode, ControlButton, Controls, ReactFlow, useReactFlow, useStore, } from "@xyflow/react";
36
36
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
37
37
  import { AgentNode, NODE_HEIGHT, NODE_WIDTH } from "./AgentNode.js";
38
- import { AgentNodePopup } from "./AgentNodePopup.js";
39
- import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_NAME_KEY, BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING, } from "./const.js";
38
+ import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_NAME_KEY, BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING, } from "../const.js";
40
39
  import { addThoughtBubbleEdge, layoutLinear, layoutRadial } from "./GraphLayouts.js";
41
40
  import { PlasmaEdge } from "./PlasmaEdge.js";
42
- import { convertReservationsToNetworks, extractNetworkHocon, extractReservations, isEditableAgent, mergeNetworks, } from "./TemporaryNetworks.js";
43
- import { ThoughtBubbleEdge } from "./ThoughtBubbleEdge.js";
44
- import { ThoughtBubbleOverlay } from "./ThoughtBubbleOverlay.js";
45
- import { sendChatQuery } from "../../controller/agent/Agent.js";
46
- import { StreamingUnit } from "../../controller/llm/LlmChat.js";
47
- import { useAgentChatHistoryStore } from "../../state/ChatHistory.js";
48
- import { usePalette, useSettingsStore } from "../../state/Settings.js";
49
- import { useTempNetworksStore } from "../../state/TemporaryNetworks.js";
50
- import { getZIndex } from "../../utils/zIndexLayers.js";
51
- import { chatMessageFromChunk } from "../AgentChat/Common/Utils.js";
52
- import { MUIAlert } from "../Common/MUIAlert.js";
53
- import { NotificationType, sendNotification } from "../Common/notification.js";
54
- // #endregion: Types
55
- // #region: Constants
41
+ import { sendChatQuery } from "../../../controller/agent/Agent.js";
42
+ import { StreamingUnit } from "../../../controller/llm/LlmChat.js";
43
+ import { useAgentChatHistoryStore } from "../../../state/ChatHistory.js";
44
+ import { usePalette, useSettingsStore } from "../../../state/Settings.js";
45
+ import { useTempNetworksStore } from "../../../state/TemporaryNetworks.js";
46
+ import { getZIndex } from "../../../utils/zIndexLayers.js";
47
+ import { chatMessageFromChunk } from "../../AgentChat/Common/Utils.js";
48
+ import { MUIAlert } from "../../Common/MUIAlert.js";
49
+ import { NotificationType, sendNotification } from "../../Common/notification.js";
50
+ import { AgentNodePopup } from "../AgentNodePopup.js";
51
+ import { convertReservationsToNetworks, extractNetworkHocon, extractReservations, isEditableAgent, mergeNetworks, } from "../TemporaryNetworks.js";
52
+ import { ThoughtBubbleEdge } from "../ThoughtBubbles/ThoughtBubbleEdge.js";
53
+ import { ThoughtBubbleOverlay } from "../ThoughtBubbles/ThoughtBubbleOverlay.js";
54
+ //#endregion: Types
55
+ //#region: Constants
56
56
  // Timeout for thought bubbles is set to 10 seconds
57
57
  const THOUGHT_BUBBLE_TIMEOUT_MS = 10_000;
58
58
  // How long the dock's status banner stays visible before auto-dismissing. Error banners persist until dismissed.
59
59
  // Exported for tests.
60
60
  export const DOCK_BANNER_AUTO_DISMISS_MS = 5_000;
61
- // #endregion: Constants
62
61
  const DOCK_PROMPT_PLACEHOLDER = "Describe a change to the network";
63
- // #region: Helpers
62
+ //#endregion: Constants
63
+ //#region: Helpers
64
64
  /**
65
65
  * Streams the Agent Network Designer endpoint with a natural-language prompt and the current
66
66
  * network definition, collecting any returned reservations.
@@ -86,7 +86,22 @@ const streamNetworkDesignerPrompt = async (neuroSanURL, signal, userPrompt, curr
86
86
  }, currentUser, StreamingUnit.Line);
87
87
  return newNetworks;
88
88
  };
89
- // #endregion: Helpers
89
+ /**
90
+ * Filters node events based on the current mode. Don't allow any topological-modifying events (adding, deleting nodes)
91
+ * and in Agent Network Designer mode, don't allow dragging nodes (position changes) either.
92
+ * @param change The node change event to filter.
93
+ * @param isAgentNetworkDesignerMode Whether the flow is in Agent Network Designer mode (read-only preview).
94
+ * @return True if the event should be allowed, false if it should be filtered out.
95
+ */
96
+ export const filterNodeEvents = (change, isAgentNetworkDesignerMode) => {
97
+ // Only allow nodes to be dragged, no topological edits to the graph (read-only)
98
+ if (["remove", "add", "replace"].includes(change.type))
99
+ return false;
100
+ // Disallow dragging nodes in AND mode since it's supposed to be a read-only preview but
101
+ // pass along all other event types as
102
+ return !(change.type === "position" && isAgentNetworkDesignerMode);
103
+ };
104
+ //#endregion: Helpers
90
105
  export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork, currentConversations, currentUser, id, isAgentNetworkDesignerMode, isAwaitingLlm, isEditMode, isStreaming, isSelectedNetworkTemporary: isTemporaryNetwork, networkDisplayName, networkId, neuroSanURL, onEnterEditMode, onNetworkReplaced, onExitEditMode, onSaveAgent, thoughtBubbleEdges, setThoughtBubbleEdges, }) => {
91
106
  const theme = useTheme();
92
107
  const { fitView } = useReactFlow();
@@ -466,10 +481,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
466
481
  }, 50);
467
482
  }, [agentsInNetwork, fitView, layout]);
468
483
  const onNodesChange = useCallback((changes) => {
469
- setNodes((currentNodes) => applyNodeChanges(
470
- // For now, we only allow dragging, no updates. In agent network designer mode, it doesn't make
471
- // sense to allow position changes since the user isn't actually manipulating a real network
472
- changes.filter((c) => c.type === "position" && !isAgentNetworkDesignerMode), currentNodes));
484
+ setNodes((currentNodes) => applyNodeChanges(changes.filter((change) => filterNodeEvents(change, isAgentNetworkDesignerMode)), currentNodes));
473
485
  }, [isAgentNetworkDesignerMode]);
474
486
  const transform = useStore((state) => state.transform);
475
487
  // Why not just a "const"? See: https://reactflow.dev/learn/customization/custom-nodes
@@ -529,7 +541,6 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
529
541
  }, size: "small", sx: {
530
542
  marginLeft: theme.spacing(2),
531
543
  "& .MuiToggleButton-root": {
532
- backgroundColor: theme.palette.background.paper,
533
544
  borderColor: theme.palette.divider,
534
545
  color: theme.palette.text.primary,
535
546
  minHeight: 22,
@@ -554,7 +565,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
554
565
  height: "1rem",
555
566
  }, children: "Heatmap" })] })] }));
556
567
  };
557
- // Get the background color for the control buttons based on the layout and dark mode setting
568
+ // Get the background color for the control buttons; differs based on whether the button is active or not
558
569
  const getControlButtonBackgroundColor = (isActive) => {
559
570
  return isActive ? theme.palette.action.selected : undefined;
560
571
  };
@@ -593,12 +604,10 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
593
604
  }, children: [_jsx(Tooltip, { title: networkDisplayName, placement: "top", children: _jsx(Box, { sx: { pointerEvents: "auto" }, children: _jsx(Typography, { id: `${id}-network-title`, variant: "subtitle1", sx: {
594
605
  backdropFilter: "blur(6px)",
595
606
  backgroundColor: titleBackgroundColor,
596
- border: `1px solid ${alpha(theme.palette.divider, 0.6)}`,
607
+ border: `1px solid ${alpha(theme.palette.divider, 0.75)}`,
597
608
  borderRadius: 2,
598
- boxShadow: theme.palette.mode === "dark"
599
- ? `0 6px 20px ${alpha(theme.palette.common.black, 0.35)}`
600
- : `0 6px 16px ${alpha(theme.palette.common.black, 0.12)}`,
601
- color: theme.palette.getContrastText(alpha(titleBackgroundColor, 0.65)),
609
+ boxShadow: theme.shadows[6],
610
+ color: theme.palette.text.primary,
602
611
  fontWeight: 600,
603
612
  letterSpacing: "0.01em",
604
613
  lineHeight: 1.35,
@@ -623,18 +632,18 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
623
632
  "& .react-flow__node": {
624
633
  border: "1px solid divider",
625
634
  },
626
- "& .react-flow__panel": {
635
+ "& .react-flow__panel, & .react-flow__controls-button": {
627
636
  backgroundColor: theme.palette.background.paper,
628
- border: "1px solid divider",
629
637
  color: theme.palette.text.primary,
630
638
  },
639
+ "& .react-flow__panel": {
640
+ border: "1px solid divider",
641
+ },
631
642
  "& .react-flow__controls-button": {
632
- backgroundColor: theme.palette.background.paper,
633
643
  borderBottom: "1px solid divider",
634
- color: theme.palette.text.primary,
635
644
  fill: theme.palette.text.primary,
636
645
  },
637
- }, children: [_jsxs(Box, { id: `${id}-react-flow-wrapper`, sx: { position: "relative", flex: 1, minHeight: 0 }, children: [networkDisplayName ? _jsx(Box, { sx: { marginBottom: "1rem" }, children: getTitle() }) : null, _jsx(ReactFlow, { id: `${id}-react-flow`, nodes: nodes, edges: edges, onNodesChange: onNodesChange, onNodeClick: handleNodeClick, fitView: true, nodeTypes: nodeTypes, edgeTypes: edgeTypes, connectionMode: ConnectionMode.Loose, children: !isAwaitingLlm && (_jsxs(_Fragment, { children: [agentsInNetwork?.length && !isAgentNetworkDesignerMode && !isEditMode ? getLegend() : null, _jsx(Background, { id: `${id}-background` }), !isAgentNetworkDesignerMode && !isEditMode && getControls(), shouldShowRadialGuides ? getRadialGuides() : null] })) }), _jsx(ThoughtBubbleOverlay, { nodes: nodes, edges: thoughtBubbleEdgesForOverlay, showThoughtBubbles: showThoughtBubbles, isStreaming: isStreaming, onBubbleHoverChange: handleBubbleHoverChange })] }), isEditMode && isTemporaryNetwork && !isAwaitingLlm && (_jsxs(Box, { sx: {
646
+ }, children: [_jsxs(Box, { id: `${id}-react-flow-wrapper`, sx: { position: "relative", flex: 1, minHeight: 0 }, children: [networkDisplayName ? _jsx(Box, { sx: { marginBottom: "1rem" }, children: getTitle() }) : null, _jsx(ReactFlow, { connectionMode: ConnectionMode.Loose, edgeTypes: edgeTypes, edges: edges, fitView: true, id: `${id}-react-flow`, nodeTypes: nodeTypes, nodes: nodes, nodesDraggable: !isAgentNetworkDesignerMode, onNodeClick: handleNodeClick, onNodesChange: onNodesChange, children: !isAwaitingLlm && (_jsxs(_Fragment, { children: [agentsInNetwork?.length && !isAgentNetworkDesignerMode && !isEditMode ? getLegend() : null, _jsx(Background, { id: `${id}-background` }), !isAgentNetworkDesignerMode && !isEditMode && getControls(), shouldShowRadialGuides ? getRadialGuides() : null] })) }), _jsx(ThoughtBubbleOverlay, { nodes: nodes, edges: thoughtBubbleEdgesForOverlay, showThoughtBubbles: showThoughtBubbles, isStreaming: isStreaming, onBubbleHoverChange: handleBubbleHoverChange })] }), isEditMode && isTemporaryNetwork && !isAwaitingLlm && (_jsxs(Box, { sx: {
638
647
  backdropFilter: "blur(8px)",
639
648
  backgroundColor: alpha(theme.palette.background.paper, 0.2),
640
649
  borderTop: `2px solid ${theme.palette.primary.main}`,
@@ -1,7 +1,7 @@
1
1
  import { NodeProps } from "@xyflow/react";
2
2
  import type { Node as RFNode } from "@xyflow/react";
3
3
  import { FC } from "react";
4
- import { AgentConversation } from "./AgentConversations.js";
4
+ import { AgentConversation } from "../AgentConversations.js";
5
5
  export interface AgentNodeProps extends Record<string, unknown> {
6
6
  readonly agentCounts?: Map<string, number>;
7
7
  readonly agentName: string;
@@ -28,10 +28,10 @@ import Tooltip from "@mui/material/Tooltip";
28
28
  import Typography from "@mui/material/Typography";
29
29
  import { Handle, Position } from "@xyflow/react";
30
30
  import { useState } from "react";
31
- import { DisplayAs } from "./const.js";
32
- import { usePalette, useSettingsStore } from "../../state/Settings.js";
33
- import { isLightColor } from "../../Theme/Theme.js";
34
- import { getZIndex } from "../../utils/zIndexLayers.js";
31
+ import { usePalette, useSettingsStore } from "../../../state/Settings.js";
32
+ import { isLightColor } from "../../../Theme/Theme.js";
33
+ import { getZIndex } from "../../../utils/zIndexLayers.js";
34
+ import { DisplayAs } from "../const.js";
35
35
  // A zero-width space character, used to give the browser a hint where to break long names
36
36
  const ZERO_WIDTH_SPACE = "\u200B";
37
37
  // Node dimensions
@@ -1,9 +1,9 @@
1
1
  import { Edge, Node as RFNode } from "@xyflow/react";
2
- import { AgentConversation } from "./AgentConversations.js";
2
+ import { AgentConversation } from "../AgentConversations.js";
3
3
  import { AgentNodeProps } from "./AgentNode.js";
4
- import { ThoughtBubbleEdgeShape } from "./ThoughtBubbleEdge.js";
5
- import { AgentIconSuggestions } from "../../controller/Types/AgentIconSuggestions.js";
6
- import { ConnectivityInfo } from "../../generated/neuro-san/NeuroSanClient.js";
4
+ import { AgentIconSuggestions } from "../../../controller/Types/AgentIconSuggestions.js";
5
+ import { ConnectivityInfo } from "../../../generated/neuro-san/NeuroSanClient.js";
6
+ import { ThoughtBubbleEdgeShape } from "../ThoughtBubbles/ThoughtBubbleEdge.js";
7
7
  export declare const MAX_GLOBAL_THOUGHT_BUBBLES = 5;
8
8
  /**
9
9
  * Result of the layout algorithms, containing the nodes and edges with their computed positions and properties.