@cognizant-ai-lab/ui-common 1.4.2 → 1.5.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.
- package/dist/components/AgentChat/ChatCommon/AgentConnectivity.d.ts +14 -0
- package/dist/components/AgentChat/ChatCommon/AgentConnectivity.js +23 -0
- package/dist/components/AgentChat/{ChatCommon.d.ts → ChatCommon/ChatCommon.d.ts} +8 -4
- package/dist/components/AgentChat/{ChatCommon.js → ChatCommon/ChatCommon.js} +318 -307
- package/dist/components/AgentChat/ChatCommon/ChatHistory.d.ts +17 -0
- package/dist/components/AgentChat/ChatCommon/ChatHistory.js +27 -0
- package/dist/components/AgentChat/{ControlButtons.d.ts → ChatCommon/ControlButtons.d.ts} +1 -1
- package/dist/components/AgentChat/ChatCommon/ControlButtons.js +26 -0
- package/dist/components/AgentChat/{FormattedMarkdown.js → ChatCommon/FormattedMarkdown.js} +1 -1
- package/dist/components/AgentChat/ChatCommon/SampleQueries.d.ts +16 -0
- package/dist/components/AgentChat/ChatCommon/SampleQueries.js +29 -0
- package/dist/components/AgentChat/{SendButton.js → ChatCommon/SendButton.js} +1 -1
- package/dist/components/AgentChat/ChatCommon/UserQueryDisplay.d.ts +7 -0
- package/dist/components/AgentChat/{UserQueryDisplay.js → ChatCommon/UserQueryDisplay.js} +4 -3
- package/dist/components/AgentChat/{LlmChatButton.d.ts → Common/LlmChatButton.d.ts} +2 -2
- package/dist/components/AgentChat/{Utils.d.ts → Common/Utils.d.ts} +1 -1
- package/dist/components/AgentChat/{Utils.js → Common/Utils.js} +2 -1
- package/dist/components/AgentChat/VoiceChat/MicrophoneButton.js +1 -1
- package/dist/components/ChatBot/ChatBot.js +2 -2
- package/dist/components/Common/CustomerLogo.js +1 -1
- package/dist/components/Common/LlmChatOptionsButton.d.ts +1 -1
- package/dist/components/Common/MUIDialog.d.ts +1 -0
- package/dist/components/Common/MUIDialog.js +2 -2
- package/dist/components/MultiAgentAccelerator/AgentCounts.d.ts +2 -2
- package/dist/components/MultiAgentAccelerator/AgentFlow.d.ts +13 -1
- package/dist/components/MultiAgentAccelerator/AgentFlow.js +193 -20
- package/dist/components/MultiAgentAccelerator/AgentNetworkDesigner.d.ts +10 -0
- package/dist/components/MultiAgentAccelerator/AgentNetworkDesigner.js +20 -0
- package/dist/components/MultiAgentAccelerator/AgentNode.d.ts +1 -0
- package/dist/components/MultiAgentAccelerator/AgentNode.js +9 -4
- package/dist/components/MultiAgentAccelerator/AgentNodePopup.d.ts +33 -0
- package/dist/components/MultiAgentAccelerator/AgentNodePopup.js +81 -0
- package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +4 -4
- package/dist/components/MultiAgentAccelerator/GraphLayouts.js +12 -8
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +1 -0
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +101 -44
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +4 -4
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +1 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +29 -23
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +1 -1
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +14 -0
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +26 -1
- package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.js +8 -7
- package/dist/components/MultiAgentAccelerator/const.d.ts +24 -0
- package/dist/components/MultiAgentAccelerator/const.js +19 -0
- package/dist/controller/llm/LlmChat.js +1 -1
- package/dist/index.d.ts +8 -7
- package/dist/index.js +8 -7
- package/dist/state/ChatHistory.d.ts +50 -0
- package/dist/state/ChatHistory.js +98 -0
- package/dist/state/IndexedDBStorage.d.ts +14 -0
- package/dist/state/IndexedDBStorage.js +65 -0
- package/dist/state/TemporaryNetworks.d.ts +23 -0
- package/dist/state/TemporaryNetworks.js +43 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +8 -2
- package/dist/components/AgentChat/ControlButtons.js +0 -26
- package/dist/components/AgentChat/UserQueryDisplay.d.ts +0 -5
- /package/dist/components/AgentChat/{FormattedMarkdown.d.ts → ChatCommon/FormattedMarkdown.d.ts} +0 -0
- /package/dist/components/AgentChat/{Greetings.d.ts → ChatCommon/Greetings.d.ts} +0 -0
- /package/dist/components/AgentChat/{Greetings.js → ChatCommon/Greetings.js} +0 -0
- /package/dist/components/AgentChat/{SendButton.d.ts → ChatCommon/SendButton.d.ts} +0 -0
- /package/dist/components/AgentChat/{SyntaxHighlighterThemes.d.ts → ChatCommon/SyntaxHighlighterThemes.d.ts} +0 -0
- /package/dist/components/AgentChat/{SyntaxHighlighterThemes.js → ChatCommon/SyntaxHighlighterThemes.js} +0 -0
- /package/dist/components/AgentChat/{LlmChatButton.js → Common/LlmChatButton.js} +0 -0
- /package/dist/components/AgentChat/{Types.d.ts → Common/Types.d.ts} +0 -0
- /package/dist/components/AgentChat/{Types.js → Common/Types.js} +0 -0
|
@@ -18,48 +18,35 @@ import StopCircle from "@mui/icons-material/StopCircle";
|
|
|
18
18
|
import Box from "@mui/material/Box";
|
|
19
19
|
import Grid from "@mui/material/Grid";
|
|
20
20
|
import Slide from "@mui/material/Slide";
|
|
21
|
+
import { useTheme } from "@mui/material/styles";
|
|
22
|
+
import Typography from "@mui/material/Typography";
|
|
21
23
|
import { ReactFlowProvider } from "@xyflow/react";
|
|
22
24
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
23
25
|
import { extractConversations } from "./AgentConversations.js";
|
|
24
26
|
import { getUpdatedAgentCounts } from "./AgentCounts.js";
|
|
25
27
|
import { AgentFlow } from "./AgentFlow.js";
|
|
26
|
-
import {
|
|
28
|
+
import { extractAgentNetworkDesignerProgress } from "./AgentNetworkDesigner.js";
|
|
29
|
+
import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_HOCON, AGENT_NETWORK_NAME_KEY, } from "./const.js";
|
|
27
30
|
import { Sidebar } from "./Sidebar/Sidebar.js";
|
|
28
|
-
import { extractNetworkHocon, extractReservations } from "./TemporaryNetworks.js";
|
|
31
|
+
import { convertReservationsToNetworks, extractNetworkHocon, extractReservations } from "./TemporaryNetworks.js";
|
|
29
32
|
import { getAgentIconSuggestions, getAgentNetworks, getConnectivity, getNetworkIconSuggestions, } from "../../controller/agent/Agent.js";
|
|
30
33
|
import { useSettingsStore } from "../../state/Settings.js";
|
|
31
34
|
import { useTempNetworksStore } from "../../state/TemporaryNetworks.js";
|
|
32
35
|
import { useLocalStorage } from "../../utils/useLocalStorage.js";
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
35
|
-
import {
|
|
36
|
+
import { getZIndex } from "../../utils/zIndexLayers.js";
|
|
37
|
+
import { ChatCommon } from "../AgentChat/ChatCommon/ChatCommon.js";
|
|
38
|
+
import { SmallLlmChatButton } from "../AgentChat/Common/LlmChatButton.js";
|
|
39
|
+
import { chatMessageFromChunk, cleanUpAgentName, removeTrailingUuid } from "../AgentChat/Common/Utils.js";
|
|
36
40
|
import { ConfirmationModal } from "../Common/ConfirmationModal.js";
|
|
37
41
|
import { closeNotification, NotificationType, sendNotification } from "../Common/notification.js";
|
|
42
|
+
// Check for expired networks every this many milliseconds
|
|
43
|
+
const EXPIRED_NETWORKS_CHECK_INTERVAL_MS = 10 * 1000;
|
|
38
44
|
// Display expired temporary networks for this amount of time after they expire so users can see what happened
|
|
39
|
-
const GRACE_PERIOD_MS = 5 * 60 * 1000; // 5 minutes
|
|
45
|
+
export const GRACE_PERIOD_MS = 5 * 60 * 1000; // 5 minutes
|
|
40
46
|
// Animation time for the left and right panels to slide in or out when launching the animation
|
|
41
47
|
const GROW_ANIMATION_TIME_MS = 800;
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
* in the tree.
|
|
45
|
-
* @param agentReservations List of "agent reservations" (temporary networks) received from the backend
|
|
46
|
-
* @param networkHocon Optional network HOCON string that may be included in the same message as the
|
|
47
|
-
* reservations. Note: for now we assume that all reservations are associated with the same network definition.
|
|
48
|
-
* This will fail if ever we get multiple reservations for different networks in a single chat stream, but that is
|
|
49
|
-
* not a valid scenario currently; we are focusing on Agent Network Design which has a simple output.
|
|
50
|
-
* @returns List of TemporaryNetwork objects that can be displayed in the UI
|
|
51
|
-
*/
|
|
52
|
-
const convertReservationsToNetworks = (agentReservations, networkHocon) => {
|
|
53
|
-
return agentReservations.map((reservation) => ({
|
|
54
|
-
reservation,
|
|
55
|
-
agentInfo: {
|
|
56
|
-
agent_name: `${TEMPORARY_NETWORK_FOLDER}/${reservation.reservation_id}`,
|
|
57
|
-
origin: reservation.reservation_id,
|
|
58
|
-
status: "active",
|
|
59
|
-
},
|
|
60
|
-
networkHocon,
|
|
61
|
-
}));
|
|
62
|
-
};
|
|
48
|
+
// Optimization to avoid creating a new empty map on every render
|
|
49
|
+
const EMPTY_THOUGHT_BUBBLE_EDGES = new Map();
|
|
63
50
|
/**
|
|
64
51
|
* Main Multi-Agent Accelerator component that contains the sidebar, agent flow, and chat components.
|
|
65
52
|
* @param backendNeuroSanApiUrl Initial URL of the backend Neuro-San API. User can change this in the UI.
|
|
@@ -67,6 +54,8 @@ const convertReservationsToNetworks = (agentReservations, networkHocon) => {
|
|
|
67
54
|
* @param userInfo Information about the current user, including userName and userImage.
|
|
68
55
|
*/
|
|
69
56
|
export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
57
|
+
// MUI theme
|
|
58
|
+
const theme = useTheme();
|
|
70
59
|
const enableZenMode = useSettingsStore((state) => state.settings.behavior.enableZenMode);
|
|
71
60
|
// Stores whether are currently awaiting LLM response (for knowing when to show spinners)
|
|
72
61
|
const [isAwaitingLlm, setIsAwaitingLlm] = useState(false);
|
|
@@ -80,8 +69,12 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
80
69
|
const [newlyAddedTemporaryNetworks, setNewlyAddedTemporaryNetworks] = useState(new Set());
|
|
81
70
|
const [networkIconSuggestions, setNetworkIconSuggestions] = useState({});
|
|
82
71
|
const [agentsInNetwork, setAgentsInNetwork] = useState([]);
|
|
72
|
+
// Agents in network under construction by Agent Network Designer -
|
|
73
|
+
// updated in real time as we receive progress messages from the backend.
|
|
74
|
+
const [agentsInNetworkDesigner, setAgentsInNetworkDesigner] = useState([]);
|
|
83
75
|
const [agentIconSuggestions, setAgentIconSuggestions] = useState(null);
|
|
84
76
|
const [selectedNetwork, setSelectedNetwork] = useState(null);
|
|
77
|
+
const networkDisplayName = useMemo(() => cleanUpAgentName(removeTrailingUuid(selectedNetwork)), [selectedNetwork]);
|
|
85
78
|
// Track whether we've shown the info popup so we don't keep bugging the user with it
|
|
86
79
|
const [haveShownPopup, setHaveShownPopup] = useState(false);
|
|
87
80
|
const [customURLLocalStorage, setCustomURLLocalStorage] = useLocalStorage("customAgentNetworkURL", null);
|
|
@@ -109,11 +102,30 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
109
102
|
}, []);
|
|
110
103
|
// Reference to the ChatCommon component to allow external stop button to call its handleStop method
|
|
111
104
|
const chatRef = useRef(null);
|
|
105
|
+
// Special mode of operation where user is using Agent Network Designer to create a new network
|
|
106
|
+
const isNetworkDesignerMode = selectedNetwork === AGENT_NETWORK_DESIGNER_ID;
|
|
107
|
+
// Whether the currently selected network is a temporary network (agent reservation)
|
|
108
|
+
const isSelectedNetworkTemporary = selectedNetwork !== null && temporaryNetworks.some((n) => n.agentInfo.agent_name === selectedNetwork);
|
|
109
|
+
// For temp networks, agent_network_definition and agent_network_name live in localStorage (not IndexedDB slyData).
|
|
110
|
+
// Supply them as extraSlyData so ChatCommon bounces them back to the backend on every request.
|
|
111
|
+
const currentTempNetwork = isSelectedNetworkTemporary
|
|
112
|
+
? temporaryNetworks.find((n) => n.agentInfo.agent_name === selectedNetwork)
|
|
113
|
+
: undefined;
|
|
114
|
+
const extraSlyData = currentTempNetwork
|
|
115
|
+
? {
|
|
116
|
+
[AGENT_NETWORK_DEFINITION_KEY]: currentTempNetwork.agentNetworkDefinition,
|
|
117
|
+
// Use the name the backend originally sent, not the local UUID-based key.
|
|
118
|
+
...(currentTempNetwork.agentNetworkName
|
|
119
|
+
? { [AGENT_NETWORK_NAME_KEY]: currentTempNetwork.agentNetworkName }
|
|
120
|
+
: {}),
|
|
121
|
+
...(currentTempNetwork.networkHocon ? { [AGENT_NETWORK_HOCON]: currentTempNetwork.networkHocon } : {}),
|
|
122
|
+
}
|
|
123
|
+
: undefined;
|
|
112
124
|
// Handle external stop button click - stops streaming and exits zen mode
|
|
113
125
|
const handleExternalStop = useCallback(() => {
|
|
114
126
|
chatRef.current?.handleStop();
|
|
115
127
|
resetState();
|
|
116
|
-
}, []);
|
|
128
|
+
}, [resetState]);
|
|
117
129
|
useEffect(() => {
|
|
118
130
|
;
|
|
119
131
|
(async () => {
|
|
@@ -159,8 +171,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
159
171
|
closeNotification();
|
|
160
172
|
}
|
|
161
173
|
catch (e) {
|
|
162
|
-
|
|
163
|
-
sendNotification(NotificationType.error, "Connection error", `Unable to get agent list for "${networkName}". Verify that ${neuroSanURL} is a valid ` +
|
|
174
|
+
sendNotification(NotificationType.error, "Connection error", `Unable to get agent list for "${networkDisplayName}". Verify that ${neuroSanURL} is a valid ` +
|
|
164
175
|
`Multi-Agent Accelerator Server. Error: ${e}.`);
|
|
165
176
|
setAgentsInNetwork([]);
|
|
166
177
|
}
|
|
@@ -169,7 +180,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
169
180
|
setAgentsInNetwork([]);
|
|
170
181
|
}
|
|
171
182
|
})();
|
|
172
|
-
}, [neuroSanURL, selectedNetwork]);
|
|
183
|
+
}, [networkDisplayName, neuroSanURL, selectedNetwork, userInfo.userName]);
|
|
173
184
|
useEffect(() => {
|
|
174
185
|
;
|
|
175
186
|
(async () => {
|
|
@@ -185,7 +196,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
185
196
|
}
|
|
186
197
|
}
|
|
187
198
|
})();
|
|
188
|
-
}, [agentNamesKey]);
|
|
199
|
+
}, [agentNamesKey, agentsInNetwork]);
|
|
189
200
|
// Set up handler to allow Escape key to stop the interaction with the LLM.
|
|
190
201
|
useEffect(() => {
|
|
191
202
|
if (!isAwaitingLlm) {
|
|
@@ -224,7 +235,7 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
224
235
|
setSelectedNetwork(null);
|
|
225
236
|
agentCountsRef.current = new Map();
|
|
226
237
|
}
|
|
227
|
-
},
|
|
238
|
+
}, EXPIRED_NETWORKS_CHECK_INTERVAL_MS);
|
|
228
239
|
return () => clearInterval(interval);
|
|
229
240
|
}, [temporaryNetworks, selectedNetwork]);
|
|
230
241
|
const onChunkReceived = useCallback((chunk) => {
|
|
@@ -241,26 +252,37 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
241
252
|
}
|
|
242
253
|
// Agent hit counts
|
|
243
254
|
agentCountsRef.current = getUpdatedAgentCounts(agentCountsRef.current, chatMessage?.origin);
|
|
255
|
+
// Agent network designer progress messages
|
|
256
|
+
if (isNetworkDesignerMode) {
|
|
257
|
+
const networkInProgress = extractAgentNetworkDesignerProgress(chatMessage);
|
|
258
|
+
if (networkInProgress?.length > 0) {
|
|
259
|
+
setAgentsInNetworkDesigner(networkInProgress);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
244
262
|
// Temporary networks/reservations
|
|
245
263
|
const reservationsResult = extractReservations(chatMessage);
|
|
246
264
|
// Handle agent reservations (temporary networks) that come in through the chat stream.
|
|
247
265
|
if (reservationsResult?.length > 0) {
|
|
248
266
|
// Retrieve network definition, if present
|
|
249
267
|
const networkHocon = extractNetworkHocon(chatMessage);
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
268
|
+
const agentNetworkDefinition = chatMessage.sly_data?.[AGENT_NETWORK_DEFINITION_KEY];
|
|
269
|
+
// Capture the backend's canonical name so we can bounce it back unchanged on future requests.
|
|
270
|
+
const agentNetworkName = chatMessage.sly_data?.[AGENT_NETWORK_NAME_KEY];
|
|
271
|
+
const newTemporaryNetworks = convertReservationsToNetworks(reservationsResult, networkHocon, agentNetworkDefinition, agentNetworkName);
|
|
272
|
+
const upserted = useTempNetworksStore.getState().upsertTempNetworks(newTemporaryNetworks);
|
|
273
|
+
// Record the new temporary networks so we can highlight them for the user.
|
|
274
|
+
// For now, we only care about the first one since that's the only active use case
|
|
275
|
+
setNewlyAddedTemporaryNetworks(new Set(upserted.map((network) => network.agentInfo.agent_name)));
|
|
256
276
|
}
|
|
257
277
|
return true;
|
|
258
|
-
}, []);
|
|
278
|
+
}, [isNetworkDesignerMode]);
|
|
259
279
|
const onStreamingStarted = useCallback(() => {
|
|
260
280
|
// Reset agent counts
|
|
261
281
|
agentCountsRef.current = new Map();
|
|
262
282
|
// Reset newly added temporary networks
|
|
263
283
|
setNewlyAddedTemporaryNetworks(new Set());
|
|
284
|
+
// Reset Agent Network Designer preview
|
|
285
|
+
setAgentsInNetworkDesigner([]);
|
|
264
286
|
// Show info popup only once per session
|
|
265
287
|
if (!haveShownPopup) {
|
|
266
288
|
sendNotification(NotificationType.info, "Agents working", "Click the stop button or hit Escape to exit.");
|
|
@@ -273,8 +295,9 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
273
295
|
// When streaming is complete, clean up any refs and state
|
|
274
296
|
conversationsRef.current = null;
|
|
275
297
|
setCurrentConversations(null);
|
|
298
|
+
setAgentsInNetworkDesigner([]);
|
|
276
299
|
resetState();
|
|
277
|
-
}, [
|
|
300
|
+
}, [resetState]);
|
|
278
301
|
const [confirmationModalOpen, setConfirmationModalOpen] = useState(false);
|
|
279
302
|
const handleDeleteNetwork = (networkId, isExpired) => {
|
|
280
303
|
if (isExpired) {
|
|
@@ -308,14 +331,23 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
308
331
|
height: "100%",
|
|
309
332
|
maxWidth: 1000,
|
|
310
333
|
margin: "0 auto",
|
|
311
|
-
}, children: _jsx(AgentFlow, { agentCounts: agentCountsRef.current, agentsInNetwork: agentsInNetwork, agentIconSuggestions: agentIconSuggestions, id: "multi-agent-accelerator-agent-flow", currentConversations: currentConversations, isAwaitingLlm: isAwaitingLlm, isStreaming: isStreaming,
|
|
334
|
+
}, children: _jsx(AgentFlow, { agentCounts: agentCountsRef.current, agentsInNetwork: agentsInNetwork, agentIconSuggestions: agentIconSuggestions, currentUser: userInfo.userName, id: "multi-agent-accelerator-agent-flow", currentConversations: currentConversations, isAwaitingLlm: isAwaitingLlm, isStreaming: isStreaming, isSelectedNetworkTemporary: isSelectedNetworkTemporary, networkId: isSelectedNetworkTemporary ? selectedNetwork : undefined, neuroSanURL: neuroSanURL, onNetworkReplaced: (oldNetworkId, newNetworkId) => {
|
|
335
|
+
if (selectedNetwork === oldNetworkId) {
|
|
336
|
+
agentCountsRef.current = new Map();
|
|
337
|
+
setSelectedNetwork(newNetworkId);
|
|
338
|
+
}
|
|
339
|
+
}, thoughtBubbleEdges: thoughtBubbleEdges, setThoughtBubbleEdges: setThoughtBubbleEdges }, "multi-agent-accelerator-agent-flow") }) }) }));
|
|
312
340
|
};
|
|
313
341
|
const getRightPanel = () => {
|
|
314
342
|
return (_jsx(Slide, { id: "multi-agent-accelerator-grid-agent-chat-common-slide", in: !enableZenMode || !isAwaitingLlm, direction: "left", timeout: GROW_ANIMATION_TIME_MS, onExited: () => {
|
|
315
343
|
setIsStreaming(true);
|
|
316
344
|
}, children: _jsx(Grid, { id: "multi-agent-accelerator-grid-agent-chat-common", size: enableZenMode && isStreaming ? 0 : 6.5, sx: {
|
|
317
345
|
height: "100%",
|
|
318
|
-
}, children: _jsx(ChatCommon, {
|
|
346
|
+
}, children: _jsx(ChatCommon, { agentGreetings: {
|
|
347
|
+
[AGENT_NETWORK_DESIGNER_ID]: "Let's build a network together!",
|
|
348
|
+
}, agentPlaceholders: {
|
|
349
|
+
[AGENT_NETWORK_DESIGNER_ID]: "Describe in plain language the network you would like to build.",
|
|
350
|
+
}, currentUser: userInfo.userName, extraSlyData: extraSlyData, id: "agent-network-ui", isAwaitingLlm: isAwaitingLlm, neuroSanURL: neuroSanURL, onChunkReceived: onChunkReceived, onStreamingComplete: onStreamingComplete, onStreamingStarted: onStreamingStarted, ref: chatRef, setIsAwaitingLlm: setIsAwaitingLlm, targetAgent: selectedNetwork, userImage: userInfo.userImage }, selectedNetwork ?? "no-network") }) }));
|
|
319
351
|
};
|
|
320
352
|
const getStopButton = () => {
|
|
321
353
|
return (_jsx(_Fragment, { children: isAwaitingLlm && enableZenMode && (_jsx(Box, { id: "stop-button-container", sx: {
|
|
@@ -336,7 +368,32 @@ export const MultiAgentAccelerator = ({ backendNeuroSanApiUrl, userInfo, }) => {
|
|
|
336
368
|
setNetworkToBeDeleted(null);
|
|
337
369
|
setConfirmationModalOpen(false);
|
|
338
370
|
}, title: "Delete Network" })) : null;
|
|
339
|
-
|
|
371
|
+
/**
|
|
372
|
+
* Popper to show real-time progress of the Agent Network Designer output as we receive it from the backend.
|
|
373
|
+
* Only displayed when Agent Network Designer is active.
|
|
374
|
+
*/
|
|
375
|
+
const getProgressPopper = () => (_jsx(Box, { sx: {
|
|
376
|
+
display: isStreaming && isNetworkDesignerMode ? "block" : "none",
|
|
377
|
+
position: "absolute",
|
|
378
|
+
top: 0,
|
|
379
|
+
left: 0,
|
|
380
|
+
width: "600px",
|
|
381
|
+
height: "600px",
|
|
382
|
+
zIndex: getZIndex(2, theme),
|
|
383
|
+
}, children: _jsx(ReactFlowProvider, { children: _jsxs(Box, { sx: {
|
|
384
|
+
alignItems: "center",
|
|
385
|
+
background: "var(--bs-secondary)",
|
|
386
|
+
border: "4px solid var(--bs-yellow)",
|
|
387
|
+
display: "flex",
|
|
388
|
+
flexDirection: "column",
|
|
389
|
+
height: "100%",
|
|
390
|
+
justifyContent: "center",
|
|
391
|
+
margin: "0 auto",
|
|
392
|
+
maxWidth: 1000,
|
|
393
|
+
opacity: "95%",
|
|
394
|
+
width: "100%",
|
|
395
|
+
}, children: [_jsx(Typography, { variant: "h6", sx: { padding: "0.5rem 1rem", fontWeight: "bold", color: "white" }, children: "Network Preview" }), agentsInNetworkDesigner?.length > 0 ? (_jsx(AgentFlow, { id: "and-network-preview", agentsInNetwork: agentsInNetworkDesigner, isAgentNetworkDesignerMode: true, isAwaitingLlm: false, isStreaming: false, thoughtBubbleEdges: EMPTY_THOUGHT_BUBBLE_EDGES }, "and-network-preview")) : (_jsx(Typography, { variant: "body1", sx: { color: "white" }, children: "Awaiting status from Agent Network Designer..." }))] }) }) }));
|
|
396
|
+
return (_jsxs(_Fragment, { children: [getProgressPopper(), getConfirmationModal(), _jsxs(Grid, { id: "multi-agent-accelerator-grid", container: true, columns: 18, sx: {
|
|
340
397
|
display: "flex",
|
|
341
398
|
flex: 1,
|
|
342
399
|
height: "85%",
|
|
@@ -15,7 +15,7 @@ import { TreeItemProvider } from "@mui/x-tree-view/TreeItemProvider";
|
|
|
15
15
|
import { useTreeItem } from "@mui/x-tree-view/useTreeItem";
|
|
16
16
|
import { useRef } from "react";
|
|
17
17
|
import { downloadFile, toSafeFilename } from "../../../utils/File.js";
|
|
18
|
-
import { cleanUpAgentName } from "../../AgentChat/Utils.js";
|
|
18
|
+
import { cleanUpAgentName } from "../../AgentChat/Common/Utils.js";
|
|
19
19
|
// Palette of colors we can use for tags
|
|
20
20
|
const TAG_COLORS = [
|
|
21
21
|
"--bs-accent2-light",
|
|
@@ -79,7 +79,7 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networ
|
|
|
79
79
|
else if (iconNameSuggestion) {
|
|
80
80
|
console.warn(`Icon "${iconNameSuggestion}" not found in MUI icons library.`);
|
|
81
81
|
}
|
|
82
|
-
return (_jsx(TreeItemProvider, { ...getContextProviderProps(), children: _jsxs(TreeItemRoot, { ...getRootProps(), ref: rootRef, children: [_jsx(TreeItemContent, { ...getContentProps(), sx: {
|
|
82
|
+
return (_jsx(TreeItemProvider, { ...getContextProviderProps(), children: _jsxs(TreeItemRoot, { ...getRootProps(), ref: rootRef, "data-itemid": itemId, children: [_jsx(TreeItemContent, { ...getContentProps(), sx: {
|
|
83
83
|
cursor: isExpired ? "not-allowed" : "pointer",
|
|
84
84
|
}, children: _jsxs(Box, { sx: { display: "flex", alignItems: "center", justifyContent: "space-between", width: "100%" }, children: [_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: "0.25rem" }, children: [_jsx(Tooltip, { title: isChild && isExpired
|
|
85
85
|
? "Expired"
|
|
@@ -97,7 +97,7 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networ
|
|
|
97
97
|
.map((tag) => (_jsx(Chip, { label: tag, style: {
|
|
98
98
|
margin: "0.25rem",
|
|
99
99
|
backgroundColor: `var(${tagsToColors.get(tag) || TAG_COLORS[0]})`,
|
|
100
|
-
} }, tag))), placement: "right", arrow: true, children: _jsx(BookmarkIcon, { sx: { fontSize: "0.75rem", color: "var(--bs-accent1-medium)" } }) })) : null, isTemporaryNetwork && networkHocon && (_jsx(Tooltip, { title: isExpired ? "
|
|
100
|
+
} }, tag))), placement: "right", arrow: true, children: _jsx(BookmarkIcon, { sx: { fontSize: "0.75rem", color: "var(--bs-accent1-medium)" } }) })) : null, isTemporaryNetwork && networkHocon && (_jsx(Tooltip, { title: isExpired ? "Expired" : "Download network definition", children: _jsx("span", { children: _jsx(IconButton, { onClick: (e) => {
|
|
101
101
|
e.stopPropagation();
|
|
102
102
|
if (isExpired) {
|
|
103
103
|
return;
|
|
@@ -119,5 +119,5 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, networ
|
|
|
119
119
|
cursor: "pointer",
|
|
120
120
|
fontSize: "1rem",
|
|
121
121
|
"&:hover": { color: theme.palette.warning.main },
|
|
122
|
-
} }) }))] }) }
|
|
122
|
+
} }) }))] }) }), children && _jsx(TreeItemGroupTransition, { ...getGroupTransitionProps() })] }) }));
|
|
123
123
|
};
|
|
@@ -2,6 +2,7 @@ import { FC } from "react";
|
|
|
2
2
|
import { NetworkIconSuggestions } from "../../../controller/Types/NetworkIconSuggestions.js";
|
|
3
3
|
import { AgentInfo } from "../../../generated/neuro-san/NeuroSanClient.js";
|
|
4
4
|
import { TemporaryNetwork } from "../../../state/TemporaryNetworks.js";
|
|
5
|
+
export declare const SPARKLE_HIGHLIGHT_CLASS = "sparkle-highlight";
|
|
5
6
|
export interface SidebarProps {
|
|
6
7
|
readonly customURLCallback: (url: string) => void;
|
|
7
8
|
readonly customURLLocalStorage?: string;
|
|
@@ -14,6 +14,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
14
14
|
See the License for the specific language governing permissions and
|
|
15
15
|
limitations under the License.
|
|
16
16
|
*/
|
|
17
|
+
import AddBoxRounded from "@mui/icons-material/AddBoxRounded";
|
|
17
18
|
import CheckCircleOutlineIcon from "@mui/icons-material/CheckCircleOutline";
|
|
18
19
|
import ClearIcon from "@mui/icons-material/Clear";
|
|
19
20
|
import HighlightOff from "@mui/icons-material/HighlightOff";
|
|
@@ -34,7 +35,7 @@ import { buildTreeViewItems } from "./TreeBuilder.js";
|
|
|
34
35
|
import { testConnection } from "../../../controller/agent/Agent.js";
|
|
35
36
|
import { useEnvironmentStore } from "../../../state/Environment.js";
|
|
36
37
|
import { getZIndex } from "../../../utils/zIndexLayers.js";
|
|
37
|
-
import { TEMPORARY_NETWORK_FOLDER } from "../const.js";
|
|
38
|
+
import { AGENT_NETWORK_DESIGNER_ID, TEMPORARY_NETWORK_FOLDER } from "../const.js";
|
|
38
39
|
// Animation for the sparkle effect when a new temporary network is added.
|
|
39
40
|
const sparkle = keyframes `
|
|
40
41
|
0% {
|
|
@@ -75,6 +76,8 @@ const PrimaryButton = styled(Button)({
|
|
|
75
76
|
marginLeft: "0.5rem",
|
|
76
77
|
marginTop: "2px",
|
|
77
78
|
});
|
|
79
|
+
// Name for sparkle animation CSS class
|
|
80
|
+
export const SPARKLE_HIGHLIGHT_CLASS = "sparkle-highlight";
|
|
78
81
|
// Styled component for Sidebar aside element, including styles for the sparkle highlight animation
|
|
79
82
|
// when a new temporary network is added.
|
|
80
83
|
const SidebarAside = styled("aside")({
|
|
@@ -83,7 +86,7 @@ const SidebarAside = styled("aside")({
|
|
|
83
86
|
height: "100%",
|
|
84
87
|
overflowY: "auto",
|
|
85
88
|
paddingRight: "0.75rem",
|
|
86
|
-
|
|
89
|
+
[`& .${SPARKLE_HIGHLIGHT_CLASS}`]: {
|
|
87
90
|
background: "linear-gradient(90deg, gold, orange, cyan, magenta, gold)",
|
|
88
91
|
backgroundSize: "400% 100%",
|
|
89
92
|
animation: `${sparkle} 5s ease`,
|
|
@@ -94,11 +97,14 @@ const SidebarAside = styled("aside")({
|
|
|
94
97
|
});
|
|
95
98
|
// Styled component for the sidebar heading, which is sticky at the top of the sidebar.
|
|
96
99
|
const SidebarHeading = styled("h2")(({ theme }) => ({
|
|
100
|
+
alignItems: "center",
|
|
97
101
|
backgroundColor: theme.palette.background.default,
|
|
98
102
|
borderBottomStyle: "solid",
|
|
99
103
|
borderBottomWidth: "1px",
|
|
104
|
+
display: "flex",
|
|
100
105
|
fontSize: "1.125rem",
|
|
101
106
|
fontWeight: "bold",
|
|
107
|
+
justifyContent: "space-between",
|
|
102
108
|
marginBottom: "0.25rem",
|
|
103
109
|
paddingBottom: "0.75rem",
|
|
104
110
|
position: "sticky",
|
|
@@ -129,8 +135,7 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
|
|
|
129
135
|
const settingsPopoverOpen = Boolean(settingsAnchorEl);
|
|
130
136
|
const [expandedItems, setExpandedItems] = useState([]);
|
|
131
137
|
// Theming/Dark mode
|
|
132
|
-
const
|
|
133
|
-
const darkMode = theme.palette.mode === "dark";
|
|
138
|
+
const darkMode = useTheme().palette.mode === "dark";
|
|
134
139
|
const handleSettingsClick = (event) => {
|
|
135
140
|
// On open of Settings popover, reset the connection status to idle
|
|
136
141
|
setConnectionStatus(CONNECTION_STATUS.IDLE);
|
|
@@ -186,18 +191,17 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
|
|
|
186
191
|
setUrlInput("");
|
|
187
192
|
setConnectionStatus(CONNECTION_STATUS.IDLE);
|
|
188
193
|
};
|
|
189
|
-
//
|
|
194
|
+
// Fetch Neuro-san version on load and whenever the saved URL changes
|
|
190
195
|
useEffect(() => {
|
|
191
196
|
const fetchVersion = async () => {
|
|
192
197
|
// We aren't really trying to test the connection here, just getting the version.
|
|
193
|
-
const result = await testConnection(
|
|
198
|
+
const result = await testConnection(customURLLocalStorage || backendNeuroSanApiUrl);
|
|
194
199
|
if (result.success) {
|
|
195
200
|
setTestConnectionResult(result);
|
|
196
|
-
setConnectionStatus(CONNECTION_STATUS.SUCCESS);
|
|
197
201
|
}
|
|
198
202
|
};
|
|
199
203
|
void fetchVersion();
|
|
200
|
-
}, []);
|
|
204
|
+
}, [customURLLocalStorage, backendNeuroSanApiUrl]);
|
|
201
205
|
const { treeViewItems, nodeIndex } = buildTreeViewItems(networks, temporaryNetworks);
|
|
202
206
|
const temporaryNetworkExpirationTimes = temporaryNetworks.reduce((acc, tempNetwork) => {
|
|
203
207
|
acc[tempNetwork.agentInfo.agent_name] = new Date(tempNetwork.reservation.expiration_time_in_seconds * 1000);
|
|
@@ -228,23 +232,22 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
|
|
|
228
232
|
useEffect(() => {
|
|
229
233
|
let highlightTimeout;
|
|
230
234
|
let removeHighlightTimeout;
|
|
231
|
-
// If we got a new temporary network,
|
|
235
|
+
// If we got a new temporary network, expand the temporary category in the tree view
|
|
232
236
|
if (newlyAddedTemporaryNetworks?.size > 0) {
|
|
233
237
|
const firstItem = newlyAddedTemporaryNetworks.values().next().value;
|
|
234
|
-
setSelectedItem(firstItem);
|
|
235
|
-
setSelectedNetwork(firstItem);
|
|
236
238
|
setExpandedItems((prev) => prev.includes(TEMPORARY_NETWORK_FOLDER) ? prev : [...prev, TEMPORARY_NETWORK_FOLDER]);
|
|
237
239
|
highlightTimeout = setTimeout(() => {
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
240
|
+
/* Scroll the selected node into view and add an animation to draw the user's attention to it.
|
|
241
|
+
Hacky: use a DOM query to find the node. I tried the various ways to do this programmatically
|
|
242
|
+
in MUI RichTreeView including the imperative API (https://mui.com/x/react-tree-view/rich-tree-view/selection/#imperative-api)
|
|
243
|
+
but couldn't get it to work so resorting to this for now.
|
|
244
|
+
*/
|
|
245
|
+
const temporaryNetworkNode = document.querySelector(`[data-itemid="${firstItem}"]`);
|
|
246
|
+
if (temporaryNetworkNode) {
|
|
247
|
+
temporaryNetworkNode.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "nearest" });
|
|
248
|
+
temporaryNetworkNode.classList.add(SPARKLE_HIGHLIGHT_CLASS);
|
|
246
249
|
removeHighlightTimeout = setTimeout(() => {
|
|
247
|
-
|
|
250
|
+
temporaryNetworkNode.classList.remove(SPARKLE_HIGHLIGHT_CLASS);
|
|
248
251
|
}, 5000);
|
|
249
252
|
}
|
|
250
253
|
}, 50);
|
|
@@ -254,9 +257,12 @@ export const Sidebar = ({ customURLCallback, customURLLocalStorage, id, isAwaiti
|
|
|
254
257
|
clearTimeout(removeHighlightTimeout);
|
|
255
258
|
};
|
|
256
259
|
}, [newlyAddedTemporaryNetworks]);
|
|
257
|
-
return (_jsxs(_Fragment, { children: [_jsxs(SidebarAside, { id: `${id}-sidebar`, children: [_jsxs(SidebarHeading, { id: `${id}-heading`, children: ["Agent Networks",
|
|
258
|
-
|
|
259
|
-
|
|
260
|
+
return (_jsxs(_Fragment, { children: [_jsxs(SidebarAside, { id: `${id}-sidebar`, children: [_jsxs(SidebarHeading, { id: `${id}-heading`, children: ["Agent Networks", _jsxs(Box, { sx: { display: "flex" }, children: [_jsx(Button, { "aria-label": "Add New Network", disabled: isAwaitingLlm, id: "add-network-btn", onClick: () => {
|
|
261
|
+
setSelectedItem(AGENT_NETWORK_DESIGNER_ID);
|
|
262
|
+
setSelectedNetwork(AGENT_NETWORK_DESIGNER_ID);
|
|
263
|
+
}, sx: { display: "inline-block", minWidth: "40px" }, children: _jsx(Tooltip, { title: "Create your own agent network", placement: "top", children: _jsx(AddBoxRounded, { id: "add-network-icon", sx: { color: isAwaitingLlm ? "rgba(0, 0, 0, 0.12)" : "var(--bs-secondary)" } }) }) }), _jsx(Button, { "aria-label": "Agent Network Settings", disabled: isAwaitingLlm, id: "agent-network-settings-btn", onClick: handleSettingsClick, sx: { display: "inline-block", minWidth: "40px" }, children: _jsx(Tooltip, { id: "agent-network-settings-tooltip", placement: "top", title: `${customURLLocalStorage || backendNeuroSanApiUrl}\nversion: ${testConnectionResult?.version || "unknown"}`, children: _jsx(SettingsIcon, { id: "agent-network-settings-icon", sx: {
|
|
264
|
+
color: isAwaitingLlm ? "rgba(0, 0, 0, 0.12)" : "var(--bs-secondary)",
|
|
265
|
+
} }) }) })] })] }), _jsx(RichTreeView, { items: treeViewItems, expandedItems: expandedItems, onExpandedItemsChange: (_event, itemIds) => setExpandedItems(itemIds), multiSelect: false, onSelectedItemsChange: handleSelectedItemsChange, selectedItems: selectedItem, disableSelection: isAwaitingLlm, slots: {
|
|
260
266
|
item: AgentNetworkTreeItem,
|
|
261
267
|
},
|
|
262
268
|
// Pass custom props to tree items via slotProps.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { cleanUpAgentName, removeTrailingUuid } from "../../AgentChat/Utils.js";
|
|
1
|
+
import { cleanUpAgentName, removeTrailingUuid } from "../../AgentChat/Common/Utils.js";
|
|
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
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { AgentNetworkDefinitionEntry } from "./const.js";
|
|
1
2
|
import { ChatMessage } from "../../generated/neuro-san/NeuroSanClient.js";
|
|
3
|
+
import { TemporaryNetwork } from "../../state/TemporaryNetworks.js";
|
|
2
4
|
/**
|
|
3
5
|
* Definition of a temporary network. No schema for this provided by backend so we second-guess it here.
|
|
4
6
|
* @see https://github.com/cognizant-ai-lab/neuro-san/issues/743
|
|
@@ -31,3 +33,15 @@ export declare const extractReservations: (message: ChatMessage) => AgentReserva
|
|
|
31
33
|
* @return The network HOCON as a string, or null if not found or not the right type of message.
|
|
32
34
|
*/
|
|
33
35
|
export declare const extractNetworkHocon: (message: ChatMessage) => string | null;
|
|
36
|
+
/**
|
|
37
|
+
* Converts a list of agent reservations received from the backend into TemporaryNetwork objects that can be
|
|
38
|
+
* displayed in the UI.
|
|
39
|
+
* @param agentReservations List of "agent reservations" (temporary networks) received from the backend
|
|
40
|
+
* @param networkHocon Optional network HOCON string associated with the reservations.
|
|
41
|
+
* @param agentNetworkDefinition Optional agent network definition entries.
|
|
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
|
+
* @returns List of TemporaryNetwork objects ready for the store.
|
|
45
|
+
*/
|
|
46
|
+
export declare const convertReservationsToNetworks: (agentReservations: AgentReservation[], networkHocon: string | null, agentNetworkDefinition?: AgentNetworkDefinitionEntry[], agentNetworkName?: string) => TemporaryNetwork[];
|
|
47
|
+
export { extractNetworkNameFromReservationId } from "../../state/TemporaryNetworks.js";
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { AGENT_NETWORK_HOCON, AGENT_RESERVATIONS_KEY } from "./const.js";
|
|
1
|
+
import { AGENT_NETWORK_HOCON, AGENT_RESERVATIONS_KEY, TEMPORARY_NETWORK_FOLDER, } from "./const.js";
|
|
2
2
|
import { ChatMessageType } from "../../generated/neuro-san/NeuroSanClient.js";
|
|
3
|
+
import { extractNetworkNameFromReservationId } from "../../state/TemporaryNetworks.js";
|
|
3
4
|
/**
|
|
4
5
|
* Extracts agent reservations from a chat message, if they exist.
|
|
5
6
|
* @param message The chat message to extract reservations from. We expect reservations to be present in messages of
|
|
@@ -33,3 +34,27 @@ export const extractNetworkHocon = (message) => {
|
|
|
33
34
|
return null;
|
|
34
35
|
}
|
|
35
36
|
};
|
|
37
|
+
/**
|
|
38
|
+
* Converts a list of agent reservations received from the backend into TemporaryNetwork objects that can be
|
|
39
|
+
* displayed in the UI.
|
|
40
|
+
* @param agentReservations List of "agent reservations" (temporary networks) received from the backend
|
|
41
|
+
* @param networkHocon Optional network HOCON string associated with the reservations.
|
|
42
|
+
* @param agentNetworkDefinition Optional agent network definition entries.
|
|
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
|
+
* @returns List of TemporaryNetwork objects ready for the store.
|
|
46
|
+
*/
|
|
47
|
+
export const convertReservationsToNetworks = (agentReservations, networkHocon, agentNetworkDefinition, agentNetworkName) => {
|
|
48
|
+
return agentReservations.map((reservation) => ({
|
|
49
|
+
reservation,
|
|
50
|
+
agentInfo: {
|
|
51
|
+
agent_name: `${TEMPORARY_NETWORK_FOLDER}/${reservation.reservation_id}`,
|
|
52
|
+
},
|
|
53
|
+
// Use the explicit name when provided; fall back to extracting it from the reservation_id so that
|
|
54
|
+
// networks are always deduplicated by name even when the backend omits AGENT_NETWORK_NAME_KEY.
|
|
55
|
+
agentNetworkName: agentNetworkName ?? extractNetworkNameFromReservationId(reservation.reservation_id),
|
|
56
|
+
networkHocon,
|
|
57
|
+
agentNetworkDefinition,
|
|
58
|
+
}));
|
|
59
|
+
};
|
|
60
|
+
export { extractNetworkNameFromReservationId } from "../../state/TemporaryNetworks.js";
|
|
@@ -115,12 +115,12 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
|
|
|
115
115
|
// Handle bubble lifecycle (appear/disappear animations)
|
|
116
116
|
useEffect(() => {
|
|
117
117
|
const currentEdgeIds = new Set(thoughtBubbleEdges.map((e) => e.id));
|
|
118
|
-
const previousBubbleIds = new Set(bubbleStates.keys());
|
|
119
|
-
// Find new bubbles that should appear
|
|
120
|
-
const newBubbles = thoughtBubbleEdges.filter((e) => !previousBubbleIds.has(e.id));
|
|
121
|
-
// Find bubbles that should disappear
|
|
122
|
-
const removingBubbles = Array.from(previousBubbleIds).filter((id) => !currentEdgeIds.has(id));
|
|
123
118
|
setBubbleStates((prev) => {
|
|
119
|
+
const previousBubbleIds = new Set(prev.keys());
|
|
120
|
+
// Find new bubbles that should appear
|
|
121
|
+
const newBubbles = thoughtBubbleEdges.filter((e) => !previousBubbleIds.has(e.id));
|
|
122
|
+
// Find bubbles that should disappear
|
|
123
|
+
const removingBubbles = Array.from(previousBubbleIds).filter((id) => !currentEdgeIds.has(id));
|
|
124
124
|
const newState = new Map(prev);
|
|
125
125
|
// Add new bubbles in entering state. Record when they entered so we can delay showing
|
|
126
126
|
// connecting lines until the bubble's entrance animation delay.
|
|
@@ -155,9 +155,10 @@ export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true,
|
|
|
155
155
|
}, [thoughtBubbleEdges]);
|
|
156
156
|
// Cleanup timeouts on unmount
|
|
157
157
|
useEffect(() => {
|
|
158
|
+
const timeouts = animationTimeouts.current;
|
|
158
159
|
return () => {
|
|
159
|
-
|
|
160
|
-
|
|
160
|
+
timeouts.forEach((timeout) => clearTimeout(timeout));
|
|
161
|
+
timeouts.clear();
|
|
161
162
|
};
|
|
162
163
|
}, []);
|
|
163
164
|
// Sort edges to prioritize frontman's edges first
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ConnectivityInfo } from "../../generated/neuro-san/NeuroSanClient.js";
|
|
1
2
|
export declare const DEFAULT_FRONTMAN_X_POS = 150;
|
|
2
3
|
export declare const DEFAULT_FRONTMAN_Y_POS = 450;
|
|
3
4
|
export declare const BASE_RADIUS = 100;
|
|
@@ -5,3 +6,26 @@ export declare const LEVEL_SPACING = 150;
|
|
|
5
6
|
export declare const TEMPORARY_NETWORK_FOLDER = "temporary";
|
|
6
7
|
export declare const AGENT_RESERVATIONS_KEY = "agent_reservations";
|
|
7
8
|
export declare const AGENT_NETWORK_HOCON = "agent_network_hocon_text";
|
|
9
|
+
export declare const AGENT_PROGRESS_CONNECTIVITY_KEY = "connectivity_info";
|
|
10
|
+
export declare const AGENT_NETWORK_DESIGNER_ID = "agent_network_designer";
|
|
11
|
+
export declare const AGENT_NETWORK_DEFINITION_KEY = "agent_network_definition";
|
|
12
|
+
export declare const AGENT_NETWORK_NAME_KEY = "agent_network_name";
|
|
13
|
+
/**
|
|
14
|
+
* A single agent entry within an agent network definition, as received in sly_data from the backend.
|
|
15
|
+
* Extends ConnectivityInfo with editable instructions and description fields for the Agent Network Designer.
|
|
16
|
+
*/
|
|
17
|
+
export type AgentNetworkDefinitionEntry = ConnectivityInfo & {
|
|
18
|
+
readonly instructions?: string;
|
|
19
|
+
readonly description?: string;
|
|
20
|
+
};
|
|
21
|
+
/** 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;
|
|
@@ -27,3 +27,22 @@ export const TEMPORARY_NETWORK_FOLDER = "temporary";
|
|
|
27
27
|
export const AGENT_RESERVATIONS_KEY = "agent_reservations";
|
|
28
28
|
// We expect the agent network HOCON to be stored in sly_data under this key, if it is provided by the backend
|
|
29
29
|
export const AGENT_NETWORK_HOCON = "agent_network_hocon_text";
|
|
30
|
+
// The key in the message structure where connectivity info is stored for agent progress messages
|
|
31
|
+
export const AGENT_PROGRESS_CONNECTIVITY_KEY = "connectivity_info";
|
|
32
|
+
// Agent name for the special "Agent Network Designer" network
|
|
33
|
+
export const AGENT_NETWORK_DESIGNER_ID = "agent_network_designer";
|
|
34
|
+
// The key in sly_data where the agent network definition is stored
|
|
35
|
+
export const AGENT_NETWORK_DEFINITION_KEY = "agent_network_definition";
|
|
36
|
+
// The key in sly_data where the agent network name is stored
|
|
37
|
+
export const AGENT_NETWORK_NAME_KEY = "agent_network_name";
|
|
38
|
+
/** 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;
|
|
@@ -24,7 +24,7 @@ export var StreamingUnit;
|
|
|
24
24
|
})(StreamingUnit || (StreamingUnit = {}));
|
|
25
25
|
const handleStreamingCallback = async (res, callback, streamingUnit) => {
|
|
26
26
|
const reader = res.body.getReader();
|
|
27
|
-
const utf8decoder = new TextDecoder("
|
|
27
|
+
const utf8decoder = new TextDecoder("utf-8");
|
|
28
28
|
let buffer = "";
|
|
29
29
|
while (true) {
|
|
30
30
|
const { done, value } = await reader.read();
|