@cognizant-ai-lab/ui-common 1.9.0 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/AgentChat/ChatCommon/ChatCommon.js +5 -4
- package/dist/components/AgentChat/ChatCommon/Thinking.js +2 -2
- package/dist/components/AgentChat/Common/Utils.d.ts +0 -11
- package/dist/components/AgentChat/Common/Utils.js +0 -16
- package/dist/components/Common/Footer.js +4 -0
- package/dist/components/MultiAgentAccelerator/AgentFlow/AgentFlow.js +56 -24
- package/dist/components/MultiAgentAccelerator/AgentFlow/AgentNode.js +7 -5
- package/dist/components/MultiAgentAccelerator/AgentFlow/GraphLayouts.js +10 -7
- package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.d.ts +1 -1
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +9 -56
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.d.ts +2 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +23 -31
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +0 -2
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +7 -6
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +2 -2
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +13 -13
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +3 -17
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +17 -40
- package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +2 -2
- package/dist/components/MultiAgentAccelerator/const.d.ts +11 -11
- package/dist/components/MultiAgentAccelerator/const.js +22 -22
- package/dist/state/TemporaryNetworks.js +12 -21
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/AgentName.d.ts +17 -0
- package/dist/utils/AgentName.js +46 -0
- package/package.json +1 -1
- package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.d.ts +0 -67
- package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.js +0 -585
|
@@ -50,16 +50,17 @@ 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 { toDisplayName } from "../../../utils/AgentName.js";
|
|
53
54
|
import { downloadFile, toSafeFilename } from "../../../utils/File.js";
|
|
54
55
|
import { hasOnlyWhitespace } from "../../../utils/text.js";
|
|
55
56
|
import { getZIndex } from "../../../utils/zIndexLayers.js";
|
|
56
57
|
import { ConfirmationModal } from "../../Common/ConfirmationModal.js";
|
|
57
58
|
import { AGENT_NETWORK_DESIGNER_ID } from "../../MultiAgentAccelerator/const.js";
|
|
58
59
|
import { givesFinalAnswer, isLegacyAgentType } from "../Common/Types.js";
|
|
59
|
-
import { chatMessageFromChunk, checkError
|
|
60
|
+
import { chatMessageFromChunk, checkError } from "../Common/Utils.js";
|
|
60
61
|
import { MicrophoneButton } from "../VoiceChat/MicrophoneButton.js";
|
|
61
62
|
import { cleanupAndStopSpeechRecognition, setupSpeechRecognition } from "../VoiceChat/VoiceChat.js";
|
|
62
|
-
//#endregion
|
|
63
|
+
//#endregion Types and Interfaces
|
|
63
64
|
//#region Constants
|
|
64
65
|
// Define fancy EMPTY constant to avoid linter error about using object literals as default props
|
|
65
66
|
const EMPTY = {};
|
|
@@ -81,7 +82,7 @@ const EXPORT_ROLE_LABELS = {
|
|
|
81
82
|
[MessageRole.Warning]: "Warning",
|
|
82
83
|
[MessageRole.Error]: "Error",
|
|
83
84
|
};
|
|
84
|
-
//#endregion
|
|
85
|
+
//#endregion Constants
|
|
85
86
|
/**
|
|
86
87
|
* Helper function to convert a message role to a label for exporting chat history.
|
|
87
88
|
* @param role The message role to convert
|
|
@@ -163,7 +164,7 @@ export const ChatCommon = ({ ref, ...props }) => {
|
|
|
163
164
|
};
|
|
164
165
|
// Keeps track of whether the agent completed its task
|
|
165
166
|
const succeeded = useRef(false);
|
|
166
|
-
const networkDisplayName = useMemo(() => (
|
|
167
|
+
const networkDisplayName = useMemo(() => toDisplayName(selectedNetwork, useNativeNames), [selectedNetwork, useNativeNames]);
|
|
167
168
|
useEffect(() => {
|
|
168
169
|
// Set up speech recognition
|
|
169
170
|
const handlers = setupSpeechRecognition(setChatInput, setVoiceInputState, speechRecognitionRef);
|
|
@@ -5,8 +5,8 @@ import { useMemo } from "react";
|
|
|
5
5
|
import ReactMarkdown from "react-markdown";
|
|
6
6
|
import { MessageRole } from "./ConversationTurn.js";
|
|
7
7
|
import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
|
|
8
|
+
import { toDisplayName } from "../../../utils/AgentName.js";
|
|
8
9
|
import { AccordionLite } from "../../Common/AccordionLite.js";
|
|
9
|
-
import { cleanUpAgentName, removeTrailingUuid } from "../Common/Utils.js";
|
|
10
10
|
/**
|
|
11
11
|
* Set of message types that should be included in the "thinking" section.
|
|
12
12
|
*/
|
|
@@ -19,7 +19,7 @@ const THINKING_MESSAGE_TYPES = new Set([
|
|
|
19
19
|
const DEFAULT_AGENT_NAME = "agent";
|
|
20
20
|
const formatTurn = (turn, useNativeNames) => {
|
|
21
21
|
const agentName = turn.agentName ?? DEFAULT_AGENT_NAME;
|
|
22
|
-
const displayName =
|
|
22
|
+
const displayName = toDisplayName(agentName, useNativeNames);
|
|
23
23
|
const headerLine = `**${displayName}**: ${turn.text || ""}`;
|
|
24
24
|
const structureLine = turn.structure == null ? "" : `\n\`${JSON.stringify(turn.structure, null, 2)}\``;
|
|
25
25
|
return `${headerLine}${structureLine}`;
|
|
@@ -3,14 +3,3 @@ 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
|
-
*/
|
|
10
|
-
export declare const removeTrailingUuid: (agentName: string) => string;
|
|
11
|
-
/**
|
|
12
|
-
* Convert FOO_BAR to more human "Foo Bar".
|
|
13
|
-
* @param agentName Agent name in SNAKE_CASE format.
|
|
14
|
-
* @returns User-friendly agent name.
|
|
15
|
-
*/
|
|
16
|
-
export declare const cleanUpAgentName: (agentName: string) => string;
|
|
@@ -14,7 +14,6 @@ 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";
|
|
18
17
|
import { ChatMessageType } from "../../../generated/neuro-san/NeuroSanClient.js";
|
|
19
18
|
// We ignore any messages that are not of these types
|
|
20
19
|
export const KNOWN_MESSAGE_TYPES = [
|
|
@@ -60,18 +59,3 @@ export const checkError = (chatMessageJson) => {
|
|
|
60
59
|
const { error, traceback, tool } = chatMessageJson;
|
|
61
60
|
return `Error occurred. Error: "${error}", traceback: "${traceback}", tool: "${tool}"`;
|
|
62
61
|
};
|
|
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
|
-
*/
|
|
67
|
-
export const removeTrailingUuid = (agentName) => {
|
|
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, "");
|
|
69
|
-
};
|
|
70
|
-
/**
|
|
71
|
-
* Convert FOO_BAR to more human "Foo Bar".
|
|
72
|
-
* @param agentName Agent name in SNAKE_CASE format.
|
|
73
|
-
* @returns User-friendly agent name.
|
|
74
|
-
*/
|
|
75
|
-
export const cleanUpAgentName = (agentName) => {
|
|
76
|
-
return startCase(agentName);
|
|
77
|
-
};
|
|
@@ -7,6 +7,8 @@ import { navigateToUrl } from "../../utils/BrowserNavigation.js";
|
|
|
7
7
|
const HeaderLine = styled("h6")({
|
|
8
8
|
fontWeight: "bold",
|
|
9
9
|
fontSize: "smaller",
|
|
10
|
+
margin: 0,
|
|
11
|
+
marginBottom: "0.9rem",
|
|
10
12
|
});
|
|
11
13
|
const LinkDivider = styled("div")({
|
|
12
14
|
border: "1px solid var(--bs-accent3-medium)",
|
|
@@ -16,6 +18,7 @@ const LinkDivider = styled("div")({
|
|
|
16
18
|
const FooterSectionHeader = styled("div")({
|
|
17
19
|
display: "inline-flex",
|
|
18
20
|
flexDirection: "column",
|
|
21
|
+
alignItems: "flex-start",
|
|
19
22
|
marginTop: "0.1rem",
|
|
20
23
|
});
|
|
21
24
|
const FooterItemLink = styled("a")(({ theme }) => ({
|
|
@@ -31,6 +34,7 @@ const FooterItemLink = styled("a")(({ theme }) => ({
|
|
|
31
34
|
const FooterContainer = styled("footer")({
|
|
32
35
|
backgroundColor: "var(--bs-primary)",
|
|
33
36
|
borderTop: "var(--bs-border-width) var(--bs-border-style) var(--bs-gray-lighter)",
|
|
37
|
+
alignItems: "flex-start",
|
|
34
38
|
columnGap: "60px",
|
|
35
39
|
display: "flex",
|
|
36
40
|
justifyContent: "center",
|
|
@@ -33,7 +33,7 @@ import ToggleButtonGroup from "@mui/material/ToggleButtonGroup";
|
|
|
33
33
|
import Tooltip from "@mui/material/Tooltip";
|
|
34
34
|
import Typography from "@mui/material/Typography";
|
|
35
35
|
import { applyNodeChanges, Background, ConnectionMode, ControlButton, Controls, ReactFlow, useReactFlow, useStore, } from "@xyflow/react";
|
|
36
|
-
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
36
|
+
import { useCallback, useEffect, useMemo, useRef, useState, } from "react";
|
|
37
37
|
import { AgentNode, NODE_HEIGHT, NODE_WIDTH } from "./AgentNode.js";
|
|
38
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";
|
|
39
39
|
import { addThoughtBubbleEdge, layoutLinear, layoutRadial } from "./GraphLayouts.js";
|
|
@@ -53,7 +53,8 @@ import { ThoughtBubbleEdge } from "../ThoughtBubbles/ThoughtBubbleEdge.js";
|
|
|
53
53
|
import { ThoughtBubbleOverlay } from "../ThoughtBubbles/ThoughtBubbleOverlay.js";
|
|
54
54
|
//#endregion: Types
|
|
55
55
|
//#region: Constants
|
|
56
|
-
|
|
56
|
+
const AGENT_SAVE_TIMEOUT_MS = 60_000;
|
|
57
|
+
const DOCK_STREAM_TIMEOUT_MS = 120_000;
|
|
57
58
|
const THOUGHT_BUBBLE_TIMEOUT_MS = 10_000;
|
|
58
59
|
// How long the dock's status banner stays visible before auto-dismissing. Error banners persist until dismissed.
|
|
59
60
|
// Exported for tests.
|
|
@@ -65,15 +66,17 @@ const DOCK_PROMPT_PLACEHOLDER = "Describe a change to the network";
|
|
|
65
66
|
* Streams the Agent Network Designer endpoint with a natural-language prompt and the current
|
|
66
67
|
* network definition, collecting any returned reservations.
|
|
67
68
|
*/
|
|
68
|
-
const
|
|
69
|
+
const applyNetworkDesignerChanges = async (neuroSanURL, signal, userPrompt, currentDefinition, agentNetworkName, currentUser) => {
|
|
69
70
|
let newNetworks = [];
|
|
70
71
|
await sendChatQuery(neuroSanURL, signal, userPrompt, AGENT_NETWORK_DESIGNER_ID, (chunk) => {
|
|
71
72
|
const chatMessage = chatMessageFromChunk(chunk);
|
|
72
|
-
if (!chatMessage)
|
|
73
|
+
if (!chatMessage) {
|
|
73
74
|
return;
|
|
75
|
+
}
|
|
74
76
|
const reservations = extractReservations(chatMessage);
|
|
75
|
-
if (reservations.length === 0)
|
|
77
|
+
if (reservations.length === 0) {
|
|
76
78
|
return;
|
|
79
|
+
}
|
|
77
80
|
const networkHocon = extractNetworkHocon(chatMessage);
|
|
78
81
|
const agentNetworkNameFromMessage = chatMessage.sly_data?.[AGENT_NETWORK_NAME_KEY];
|
|
79
82
|
const networkName = agentNetworkName ?? agentNetworkNameFromMessage;
|
|
@@ -319,26 +322,40 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
319
322
|
detail: "Nothing was changed. Your prompt is restored below.",
|
|
320
323
|
});
|
|
321
324
|
}, [showDockBanner]);
|
|
322
|
-
const
|
|
325
|
+
const openNodeEditor = useCallback((node) => {
|
|
323
326
|
// Popup is only available for temporary networks.
|
|
324
327
|
if (!isTemporaryNetwork)
|
|
325
328
|
return;
|
|
326
329
|
// Only llm_agent nodes support instructions/description editing.
|
|
327
330
|
if (!isEditableAgent(node.data.displayAs))
|
|
328
331
|
return;
|
|
329
|
-
// Find the
|
|
332
|
+
// Find the agent's existing instructions and description from the temp network definition.
|
|
330
333
|
const currentTempNetwork = networkId
|
|
331
334
|
? tempNetworks.find((n) => n.agentInfo.agent_name === networkId)
|
|
332
335
|
: undefined;
|
|
333
|
-
const
|
|
336
|
+
const currentAgentDefinition = currentTempNetwork?.agentNetworkDefinition?.find((e) => e.origin === node.id);
|
|
334
337
|
setSelectedAgent({
|
|
335
338
|
agentId: node.id,
|
|
336
339
|
agentName: node.data.agentName,
|
|
337
|
-
initialInstructions:
|
|
338
|
-
initialDescription:
|
|
340
|
+
initialInstructions: currentAgentDefinition?.instructions ?? "",
|
|
341
|
+
initialDescription: currentAgentDefinition?.description ?? "",
|
|
339
342
|
});
|
|
340
343
|
setIsPopupOpen(true);
|
|
341
344
|
}, [tempNetworks, isTemporaryNetwork, networkId]);
|
|
345
|
+
const handleNodeClick = useCallback((_event, node) => openNodeEditor(node), [openNodeEditor]);
|
|
346
|
+
// ReactFlow makes nodes focusable and selects them on Enter, but it never fires onNodeClick from the keyboard.
|
|
347
|
+
// Route Enter on a focused node to the editor so keyboard users can open a node the same way a click does.
|
|
348
|
+
const handleNodeKeyDown = useCallback((event) => {
|
|
349
|
+
if (event.key !== "Enter")
|
|
350
|
+
return;
|
|
351
|
+
const target = event.target;
|
|
352
|
+
if (!(target instanceof HTMLElement))
|
|
353
|
+
return;
|
|
354
|
+
const nodeId = target.closest(".react-flow__node")?.dataset["id"];
|
|
355
|
+
const node = nodeId ? nodes.find((n) => n.id === nodeId) : undefined;
|
|
356
|
+
if (node)
|
|
357
|
+
openNodeEditor(node);
|
|
358
|
+
}, [nodes, openNodeEditor]);
|
|
342
359
|
const handlePopupClose = useCallback(() => {
|
|
343
360
|
// If a save is in-flight, abort it immediately so the stream doesn't hang.
|
|
344
361
|
saveAbortControllerRef.current?.abort();
|
|
@@ -350,7 +367,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
350
367
|
* Applies the networks returned by the designer: upserts them and triggers navigation if needed.
|
|
351
368
|
* Returns true when a matching reservation was applied, false (and surfaces an error banner) otherwise.
|
|
352
369
|
*/
|
|
353
|
-
const
|
|
370
|
+
const saveUpdates = useCallback((newNetworksFromSave, currentAgentNetworkName) => {
|
|
354
371
|
if (newNetworksFromSave.length === 0) {
|
|
355
372
|
showDockBanner({
|
|
356
373
|
severity: "error",
|
|
@@ -359,6 +376,9 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
359
376
|
});
|
|
360
377
|
return false;
|
|
361
378
|
}
|
|
379
|
+
// Find the returned reservation that stands in for the current network (matched by name). When
|
|
380
|
+
// present, persist every returned network, then carry the open network's chat history over to the
|
|
381
|
+
// replacement and notify the parent so it can navigate to the new reservation.
|
|
362
382
|
const replacement = newNetworksFromSave.find((n) => n.agentNetworkName === currentAgentNetworkName);
|
|
363
383
|
if (replacement) {
|
|
364
384
|
useTempNetworksStore.getState().upsertTempNetworks(newNetworksFromSave);
|
|
@@ -377,7 +397,8 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
377
397
|
return false;
|
|
378
398
|
}, [networkId, onNetworkReplaced, showDockBanner]);
|
|
379
399
|
const handleDockApply = useCallback(async () => {
|
|
380
|
-
|
|
400
|
+
const readyToApplyEdit = Boolean(dockPrompt.trim() && neuroSanURL && currentUser);
|
|
401
|
+
if (!readyToApplyEdit)
|
|
381
402
|
return;
|
|
382
403
|
const currentTempNetwork = networkId
|
|
383
404
|
? tempNetworks.find((n) => n.agentInfo.agent_name === networkId)
|
|
@@ -390,11 +411,11 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
390
411
|
const timeoutId = setTimeout(() => {
|
|
391
412
|
hasTimedOut = true;
|
|
392
413
|
controller.abort();
|
|
393
|
-
},
|
|
414
|
+
}, DOCK_STREAM_TIMEOUT_MS);
|
|
394
415
|
try {
|
|
395
|
-
const newNetworks = await
|
|
396
|
-
const
|
|
397
|
-
if (
|
|
416
|
+
const newNetworks = await applyNetworkDesignerChanges(neuroSanURL, controller.signal, dockPrompt, currentDefinition, currentTempNetwork?.agentNetworkName, currentUser);
|
|
417
|
+
const appliedSuccess = saveUpdates(newNetworks, currentTempNetwork?.agentNetworkName);
|
|
418
|
+
if (appliedSuccess) {
|
|
398
419
|
setDockPrompt("");
|
|
399
420
|
showDockBanner({
|
|
400
421
|
severity: "success",
|
|
@@ -423,7 +444,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
423
444
|
dockAbortControllerRef.current = null;
|
|
424
445
|
setIsDockStreaming(false);
|
|
425
446
|
}
|
|
426
|
-
}, [
|
|
447
|
+
}, [saveUpdates, currentUser, dockPrompt, networkId, neuroSanURL, showDockBanner, tempNetworks]);
|
|
427
448
|
const handleExitEditMode = useCallback(() => {
|
|
428
449
|
if (isDockStreaming) {
|
|
429
450
|
dockAbortControllerRef.current?.abort();
|
|
@@ -432,6 +453,18 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
432
453
|
}
|
|
433
454
|
onExitEditMode?.();
|
|
434
455
|
}, [isDockStreaming, onExitEditMode]);
|
|
456
|
+
// Pressing Escape exits edit mode, mirroring the explicit exit button. Skip while the
|
|
457
|
+
// node popup is open so Escape closes the popup first rather than the whole edit mode.
|
|
458
|
+
useEffect(() => {
|
|
459
|
+
if (!isEditMode || isPopupOpen)
|
|
460
|
+
return undefined;
|
|
461
|
+
const handleEscape = (e) => {
|
|
462
|
+
if (e.key === "Escape")
|
|
463
|
+
handleExitEditMode();
|
|
464
|
+
};
|
|
465
|
+
document.addEventListener("keydown", handleEscape);
|
|
466
|
+
return () => document.removeEventListener("keydown", handleEscape);
|
|
467
|
+
}, [isEditMode, isPopupOpen, handleExitEditMode]);
|
|
435
468
|
const handlePopupSave = useCallback(async (agentName, instructionsText, descriptionText) => {
|
|
436
469
|
if (!selectedAgent)
|
|
437
470
|
return;
|
|
@@ -441,11 +474,11 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
441
474
|
: undefined;
|
|
442
475
|
// Produce a new array with the saved agent's fields updated; all other entries pass through unchanged.
|
|
443
476
|
const currentDefinitions = currentTempNetwork?.agentNetworkDefinition ?? [];
|
|
444
|
-
const
|
|
477
|
+
const updatedDefinitions = currentDefinitions.map((entry) => entry.origin === selectedAgent.agentId
|
|
445
478
|
? { ...entry, instructions: instructionsText, description: descriptionText }
|
|
446
479
|
: entry);
|
|
447
480
|
if (networkId) {
|
|
448
|
-
updateTempNetworkDefinition(networkId,
|
|
481
|
+
updateTempNetworkDefinition(networkId, updatedDefinitions);
|
|
449
482
|
}
|
|
450
483
|
if (!onSaveAgent) {
|
|
451
484
|
setIsPopupOpen(false);
|
|
@@ -454,10 +487,9 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
454
487
|
setIsSavingAgent(true);
|
|
455
488
|
const saveController = new AbortController();
|
|
456
489
|
saveAbortControllerRef.current = saveController;
|
|
457
|
-
const saveTimeoutId = setTimeout(() => saveController.abort(new DOMException("Save timed out", "TimeoutError")),
|
|
458
|
-
);
|
|
490
|
+
const saveTimeoutId = setTimeout(() => saveController.abort(new DOMException("Save timed out", "TimeoutError")), AGENT_SAVE_TIMEOUT_MS);
|
|
459
491
|
try {
|
|
460
|
-
await onSaveAgent(agentName,
|
|
492
|
+
await onSaveAgent(agentName, updatedDefinitions, currentTempNetwork?.agentNetworkName, saveController.signal);
|
|
461
493
|
}
|
|
462
494
|
catch (e) {
|
|
463
495
|
console.error(`Error saving network ${agentName}. See onSaveAgent implementation for details.`, e);
|
|
@@ -643,7 +675,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
643
675
|
borderBottom: "1px solid divider",
|
|
644
676
|
fill: theme.palette.text.primary,
|
|
645
677
|
},
|
|
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: {
|
|
678
|
+
}, 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, onKeyDown: handleNodeKeyDown, 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: {
|
|
647
679
|
backdropFilter: "blur(8px)",
|
|
648
680
|
backgroundColor: alpha(theme.palette.background.paper, 0.2),
|
|
649
681
|
borderTop: `2px solid ${theme.palette.primary.main}`,
|
|
@@ -681,7 +713,7 @@ export const AgentFlow = ({ agentCounts, agentIconSuggestions, agentsInNetwork,
|
|
|
681
713
|
px: 1,
|
|
682
714
|
py: 0.5,
|
|
683
715
|
alignItems: "center",
|
|
684
|
-
}, children: [_jsx(TextField, { fullWidth: true, placeholder: DOCK_PROMPT_PLACEHOLDER, variant: "outlined", size: "small", value: dockPrompt, onChange: (e) => setDockPrompt(e.target.value), onKeyDown: (e) => {
|
|
716
|
+
}, children: [_jsx(TextField, { autoFocus: true, fullWidth: true, placeholder: DOCK_PROMPT_PLACEHOLDER, variant: "outlined", size: "small", value: dockPrompt, onChange: (e) => setDockPrompt(e.target.value), onKeyDown: (e) => {
|
|
685
717
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
686
718
|
e.preventDefault();
|
|
687
719
|
void handleDockApply();
|
|
@@ -27,7 +27,6 @@ import { keyframes, styled, useTheme } from "@mui/material/styles";
|
|
|
27
27
|
import Tooltip from "@mui/material/Tooltip";
|
|
28
28
|
import Typography from "@mui/material/Typography";
|
|
29
29
|
import { Handle, Position } from "@xyflow/react";
|
|
30
|
-
import { useState } from "react";
|
|
31
30
|
import { usePalette, useSettingsStore } from "../../../state/Settings.js";
|
|
32
31
|
import { isLightColor } from "../../../Theme/Theme.js";
|
|
33
32
|
import { getZIndex } from "../../../utils/zIndexLayers.js";
|
|
@@ -154,7 +153,6 @@ export const AgentNode = (props) => {
|
|
|
154
153
|
const glowColor = isLightColor(theme.palette.background.default)
|
|
155
154
|
? theme.palette.common.black
|
|
156
155
|
: theme.palette.common.white;
|
|
157
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
158
156
|
// Insert zero-width spaces into the agent name to give the browser hints where to wrap.
|
|
159
157
|
const wrappedAgentName = agentName
|
|
160
158
|
// Allow wrap after underscore
|
|
@@ -165,7 +163,7 @@ export const AgentNode = (props) => {
|
|
|
165
163
|
.replaceAll(/(?<abbr>[A-Z])(?<word>[A-Z][a-z])/gu, `$<abbr>${ZERO_WIDTH_SPACE}$<word>`);
|
|
166
164
|
const height = NODE_HEIGHT * (isFrontman ? FRONTMAN_SIZE_MULTIPLIER : 1.0);
|
|
167
165
|
const width = height;
|
|
168
|
-
const getEditButton = () => (_jsx(Tooltip, { title: "Edit", placement: "top", disableInteractive: true, children: _jsx(IconButton, { sx: {
|
|
166
|
+
const getEditButton = () => (_jsx(Tooltip, { title: "Edit", placement: "top", disableInteractive: true, children: _jsx(IconButton, { className: "agent-node-edit-icon", "aria-hidden": true, tabIndex: -1, sx: {
|
|
169
167
|
position: "absolute",
|
|
170
168
|
top: 0,
|
|
171
169
|
right: 0,
|
|
@@ -197,13 +195,17 @@ export const AgentNode = (props) => {
|
|
|
197
195
|
width,
|
|
198
196
|
zIndex: getZIndex(1, theme),
|
|
199
197
|
}, children: wrappedAgentName }) }));
|
|
200
|
-
const getNode = () => (_jsxs(AnimatedNode, { id: agentId, "data-testid": agentId, glowColor: glowColor, isActive: isActiveAgent,
|
|
198
|
+
const getNode = () => (_jsxs(AnimatedNode, { id: agentId, "data-testid": agentId, glowColor: glowColor, isActive: isActiveAgent, sx: {
|
|
201
199
|
backgroundColor,
|
|
202
200
|
color,
|
|
203
201
|
cursor: isEditable ? "pointer" : "grab",
|
|
204
202
|
height,
|
|
205
203
|
width,
|
|
206
204
|
zIndex: getZIndex(1, theme),
|
|
207
|
-
|
|
205
|
+
// Reveal the edit icon on hover, or when a keyboard user tabs to the node.
|
|
206
|
+
"& .agent-node-edit-icon": { opacity: 0, transition: "opacity 0.15s ease-in-out" },
|
|
207
|
+
"&:hover .agent-node-edit-icon": { opacity: 1 },
|
|
208
|
+
".react-flow__node:focus-visible & .agent-node-edit-icon": { opacity: 1 },
|
|
209
|
+
}, children: [getDisplayAsIcon(), isEditable && getEditButton(), _jsx(Handle, { id: `${agentId}-left-handle`, position: Position.Left, type: "source", style: { visibility: handleVisibility } }), _jsx(Handle, { id: `${agentId}-right-handle`, position: Position.Right, type: "source", style: { visibility: handleVisibility } }), _jsx(Handle, { id: `${agentId}-top-handle`, position: Position.Top, type: "source", style: { visibility: handleVisibility } }), _jsx(Handle, { id: `${agentId}-bottom-handle`, position: Position.Bottom, type: "source", style: { visibility: handleVisibility } })] }));
|
|
208
210
|
return (_jsxs(_Fragment, { children: [getNode(), getNodeName()] }));
|
|
209
211
|
};
|
|
@@ -20,7 +20,8 @@ import dagre from "@dagrejs/dagre";
|
|
|
20
20
|
import { MarkerType } from "@xyflow/react";
|
|
21
21
|
import cloneDeep from "lodash-es/cloneDeep.js";
|
|
22
22
|
import { FRONTMAN_SIZE_MULTIPLIER, NODE_HEIGHT, NODE_WIDTH } from "./AgentNode.js";
|
|
23
|
-
import { cleanUpAgentName
|
|
23
|
+
import { cleanUpAgentName } from "../../../utils/AgentName.js";
|
|
24
|
+
import { KNOWN_MESSAGE_TYPES_FOR_PLASMA } from "../../AgentChat/Common/Utils.js";
|
|
24
25
|
import { BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING } from "../const.js";
|
|
25
26
|
import { getFrontman, getParentAgents, getParents } from "./GraphStructure.js";
|
|
26
27
|
import { isEditableAgent } from "../TemporaryNetworks.js";
|
|
@@ -75,6 +76,8 @@ const getEdgeProperties = (sourceId, targetId, sourceHandle, targetHandle, isAni
|
|
|
75
76
|
type: isAnimated ? "plasmaEdge" : undefined,
|
|
76
77
|
};
|
|
77
78
|
};
|
|
79
|
+
// An agent is editable if it belongs to a temporary network and is of an editable type.
|
|
80
|
+
const isNodeEditable = (displayAs, isTemporaryNetwork) => isTemporaryNetwork && isEditableAgent(displayAs);
|
|
78
81
|
export const layoutRadial = ({ agentCounts, agentIconSuggestions = null, agentsInNetwork, currentConversations, isAgentNetworkDesignerMode, isAwaitingLlm, isTemporaryNetwork = false, thoughtBubbleEdges, useNativeNames, }) => {
|
|
79
82
|
const nodesInNetwork = [];
|
|
80
83
|
const edgesInNetwork = [];
|
|
@@ -119,6 +122,7 @@ export const layoutRadial = ({ agentCounts, agentIconSuggestions = null, agentsI
|
|
|
119
122
|
const x = DEFAULT_FRONTMAN_X_POS + radius * Math.cos(angle);
|
|
120
123
|
const y = DEFAULT_FRONTMAN_Y_POS + radius * Math.sin(angle);
|
|
121
124
|
const isFrontman = frontman?.origin === nodeId;
|
|
125
|
+
const displayAs = agentsInNetwork.find((a) => a.origin === nodeId)?.display_as;
|
|
122
126
|
const parentNodes = getParents(nodeId, parentAgents);
|
|
123
127
|
// Create an edge from each parent node to this node
|
|
124
128
|
for (const parentNode of parentNodes) {
|
|
@@ -159,13 +163,12 @@ export const layoutRadial = ({ agentCounts, agentIconSuggestions = null, agentsI
|
|
|
159
163
|
agentCounts,
|
|
160
164
|
agentName: useNativeNames ? nodeId : cleanUpAgentName(nodeId),
|
|
161
165
|
depth,
|
|
162
|
-
displayAs
|
|
166
|
+
displayAs,
|
|
163
167
|
// Use current conversations for node highlighting (cleared at end)
|
|
164
168
|
getConversations: () => currentConversations,
|
|
165
169
|
isAwaitingLlm,
|
|
166
170
|
agentIconSuggestion: agentIconSuggestions?.[nodeId],
|
|
167
|
-
isEditable: isTemporaryNetwork
|
|
168
|
-
isEditableAgent(agentsInNetwork.find((a) => a.origin === nodeId)?.display_as),
|
|
171
|
+
isEditable: isNodeEditable(displayAs, isTemporaryNetwork),
|
|
169
172
|
},
|
|
170
173
|
// position: allow for Frontman being larger
|
|
171
174
|
position: isFrontman
|
|
@@ -193,7 +196,7 @@ export const layoutLinear = ({ agentCounts, agentIconSuggestions = null, agentsI
|
|
|
193
196
|
// Do these calculations outside the loop for efficiency
|
|
194
197
|
const parentAgents = getParentAgents(agentsInNetwork);
|
|
195
198
|
const frontman = getFrontman(agentsInNetwork);
|
|
196
|
-
agentsInNetwork.forEach(({ origin: nodeId }) => {
|
|
199
|
+
agentsInNetwork.forEach(({ display_as: displayAs, origin: nodeId }) => {
|
|
197
200
|
const parentIds = getParents(nodeId, parentAgents);
|
|
198
201
|
const isFrontman = frontman?.origin === nodeId;
|
|
199
202
|
nodesInNetwork.push({
|
|
@@ -202,13 +205,13 @@ export const layoutLinear = ({ agentCounts, agentIconSuggestions = null, agentsI
|
|
|
202
205
|
data: {
|
|
203
206
|
agentCounts,
|
|
204
207
|
agentName: useNativeNames ? nodeId : cleanUpAgentName(nodeId),
|
|
205
|
-
displayAs
|
|
208
|
+
displayAs,
|
|
206
209
|
// Use current conversations for node highlighting (cleared at end)
|
|
207
210
|
getConversations: () => currentConversations,
|
|
208
211
|
isAwaitingLlm,
|
|
209
212
|
depth: undefined, // Depth will be computed later,
|
|
210
213
|
agentIconSuggestion: agentIconSuggestions?.[nodeId],
|
|
211
|
-
isEditable:
|
|
214
|
+
isEditable: isNodeEditable(displayAs, isTemporaryNetwork),
|
|
212
215
|
},
|
|
213
216
|
position: isFrontman ? { x: DEFAULT_FRONTMAN_X_POS, y: DEFAULT_FRONTMAN_Y_POS } : { x: 0, y: 0 },
|
|
214
217
|
style: {
|
|
@@ -9,7 +9,7 @@ export declare const getParentAgents: <T extends ConnectivityInfo>(agents: reado
|
|
|
9
9
|
*
|
|
10
10
|
* Consumed by the graph layouts (to seed the depth-0 node) and by the network import flow.
|
|
11
11
|
*/
|
|
12
|
-
export declare const getFrontman:
|
|
12
|
+
export declare const getFrontman: (agents: readonly ConnectivityInfo[]) => ConnectivityInfo | undefined;
|
|
13
13
|
/**
|
|
14
14
|
* Returns the "origins" (node names) of the _immediate_ parents of a node in the agent network. Grandparents and
|
|
15
15
|
* higher are not included.
|
|
@@ -15,11 +15,8 @@ See the License for the specific language governing permissions and
|
|
|
15
15
|
limitations under the License.
|
|
16
16
|
*/
|
|
17
17
|
import StopCircle from "@mui/icons-material/StopCircle";
|
|
18
|
-
import Backdrop from "@mui/material/Backdrop";
|
|
19
18
|
import Box from "@mui/material/Box";
|
|
20
|
-
import CircularProgress from "@mui/material/CircularProgress";
|
|
21
19
|
import Grid from "@mui/material/Grid";
|
|
22
|
-
import Paper from "@mui/material/Paper";
|
|
23
20
|
import Slide from "@mui/material/Slide";
|
|
24
21
|
import { useTheme } from "@mui/material/styles";
|
|
25
22
|
import Tooltip from "@mui/material/Tooltip";
|
|
@@ -32,20 +29,20 @@ import { getUpdatedAgentCounts } from "./AgentCounts.js";
|
|
|
32
29
|
import { AgentFlow } from "./AgentFlow/AgentFlow.js";
|
|
33
30
|
import { extractAgentNetworkDesignerProgress } from "./AgentNetworkDesigner.js";
|
|
34
31
|
import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_HOCON, AGENT_NETWORK_NAME_KEY, EXPIRED_NETWORKS_CHECK_INTERVAL_MS, GRACE_PERIOD_MS, SHOW_TOUR_DELAY_MS, TRIGGER_APP_TOUR_EVENT_NAME, } from "./const.js";
|
|
35
|
-
import { ImportNetworkModal } from "./Sidebar/ImportNetworkModal.js";
|
|
36
32
|
import { Sidebar } from "./Sidebar/Sidebar.js";
|
|
37
|
-
import { extractTemporaryNetworksFromMessage,
|
|
33
|
+
import { extractTemporaryNetworksFromMessage, isTemporaryNetwork, notifySaveError, streamNetworkDesignerUpsert, } from "./TemporaryNetworks.js";
|
|
38
34
|
import { MAIN_TOUR_STEPS } from "./Tour/MainTourSteps.js";
|
|
39
35
|
import { getAgentFunction, getAgentNetworks, getConnectivity } from "../../controller/agent/Agent.js";
|
|
40
36
|
import { getAgentIconSuggestions, getNetworkIconSuggestions } from "../../controller/agent/IconSuggestions.js";
|
|
41
37
|
import { useAgentChatHistoryStore } from "../../state/ChatHistory.js";
|
|
42
38
|
import { useTempNetworksStore } from "../../state/TemporaryNetworks.js";
|
|
43
39
|
import { TourPromptState, useTourStore } from "../../state/Tour.js";
|
|
40
|
+
import { toDisplayName } from "../../utils/AgentName.js";
|
|
44
41
|
import { getZIndex } from "../../utils/zIndexLayers.js";
|
|
45
42
|
import { ChatCommon } from "../AgentChat/ChatCommon/ChatCommon.js";
|
|
46
43
|
import { LlmChatButton } from "../AgentChat/Common/LlmChatButton.js";
|
|
47
44
|
import { isLegacyAgentType } from "../AgentChat/Common/Types.js";
|
|
48
|
-
import { chatMessageFromChunk
|
|
45
|
+
import { chatMessageFromChunk } from "../AgentChat/Common/Utils.js";
|
|
49
46
|
import { ConfirmationModal, StyledButton } from "../Common/ConfirmationModal.js";
|
|
50
47
|
import { MUIAlert } from "../Common/MUIAlert.js";
|
|
51
48
|
import { MUIDialog } from "../Common/MUIDialog.js";
|
|
@@ -79,8 +76,6 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
79
76
|
const temporaryNetworks = useTempNetworksStore((state) => state.tempNetworks);
|
|
80
77
|
// Track newly added temp networks so we can highlight them
|
|
81
78
|
const [newlyAddedTemporaryNetworks, setNewlyAddedTemporaryNetworks] = useState(new Set());
|
|
82
|
-
// True while a file import is in-flight (after modal confirm, before the new network appears)
|
|
83
|
-
const [isImporting, setIsImporting] = useState(false);
|
|
84
79
|
const [networkIconSuggestions, setNetworkIconSuggestions] = useState({});
|
|
85
80
|
const [agentsInNetwork, setAgentsInNetwork] = useState([]);
|
|
86
81
|
const [sampleQueries, setSampleQueries] = useState([]);
|
|
@@ -90,7 +85,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
90
85
|
const [agentIconSuggestions, setAgentIconSuggestions] = useState(null);
|
|
91
86
|
const [selectedNetwork, setSelectedNetwork] = useState(null);
|
|
92
87
|
const [networkDescription, setNetworkDescription] = useState("");
|
|
93
|
-
const networkDisplayName = useMemo(() => (
|
|
88
|
+
const networkDisplayName = useMemo(() => toDisplayName(selectedNetwork, useNativeNames), [selectedNetwork, useNativeNames]);
|
|
94
89
|
// LLM providers for which BYOK keys are supported by the current network
|
|
95
90
|
const [supportedByokProviders, setSupportedByokProviders] = useState(new Set());
|
|
96
91
|
const neuroSanURL = useSettingsStore((state) => state.settings.externalServices.neuroSanUrl) ?? defaultNeuroSanUrl;
|
|
@@ -107,7 +102,6 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
107
102
|
// State to hold thought bubble edges - avoids duplicates across layout recalculations
|
|
108
103
|
const [thoughtBubbleEdges, setThoughtBubbleEdges] = useState(new Map());
|
|
109
104
|
const [confirmationModalOpen, setConfirmationModalOpen] = useState(false);
|
|
110
|
-
const [importModalOpen, setImportModalOpen] = useState(false);
|
|
111
105
|
const [tourModalOpen, setTourModalOpen] = useState(false);
|
|
112
106
|
const [haveShownTourModal, setHaveShownTourModal] = useState(false);
|
|
113
107
|
// Memoized key for agent names to trigger icon suggestion updates when the set of agents changes, not just
|
|
@@ -167,9 +161,8 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
167
161
|
// progresses. We read it back here to find the matching temp network and override agent_network_definition in the
|
|
168
162
|
// outgoing request — leaving what's in IndexedDB untouched.
|
|
169
163
|
const designerSlyData = useAgentChatHistoryStore((state) => state.history[AGENT_NETWORK_DESIGNER_ID]?.slyData);
|
|
170
|
-
const
|
|
171
|
-
|
|
172
|
-
: undefined;
|
|
164
|
+
const designerNetworkNameValue = designerSlyData?.[AGENT_NETWORK_NAME_KEY];
|
|
165
|
+
const designerNetworkName = isNetworkDesignerMode && typeof designerNetworkNameValue === "string" ? designerNetworkNameValue : undefined;
|
|
173
166
|
const designerTempNetwork = designerNetworkName
|
|
174
167
|
? temporaryNetworks.find((n) => n.agentNetworkName === designerNetworkName)
|
|
175
168
|
: undefined;
|
|
@@ -480,35 +473,6 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
480
473
|
notifySaveError(agentName, e);
|
|
481
474
|
}
|
|
482
475
|
}, [neuroSanURL, username, selectedNetwork]);
|
|
483
|
-
/**
|
|
484
|
-
* Handles an import from the ImportNetworkModal: converts the JSON content to a
|
|
485
|
-
* network definition, streams it through the network designer to obtain a reservation,
|
|
486
|
-
* then upserts the result into the temporary-networks store and navigates to the new network.
|
|
487
|
-
*/
|
|
488
|
-
const handleImportNetwork = useCallback(async (agentNetworkName, content) => {
|
|
489
|
-
setIsImporting(true);
|
|
490
|
-
try {
|
|
491
|
-
const result = await importNetworkFromJson(content, agentNetworkName, neuroSanURL, username);
|
|
492
|
-
if ("failure" in result) {
|
|
493
|
-
sendNotification(NotificationType.error, `Failed to import "${agentNetworkName}".`, IMPORT_FAILURE_DETAIL[result.failure]);
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
useTempNetworksStore.getState().upsertTempNetworks(result.networks);
|
|
497
|
-
// An import defines a single network, so the designer returns one reservation. Highlight and
|
|
498
|
-
// navigate to it; any further entries (none expected) are still upserted above.
|
|
499
|
-
const newNetworkName = result.networks[0]?.agentInfo.agent_name;
|
|
500
|
-
if (newNetworkName) {
|
|
501
|
-
setNewlyAddedTemporaryNetworks(new Set([newNetworkName]));
|
|
502
|
-
changeSelectedNetwork(newNetworkName);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
catch (e) {
|
|
506
|
-
notifySaveError(agentNetworkName, e);
|
|
507
|
-
}
|
|
508
|
-
finally {
|
|
509
|
-
setIsImporting(false);
|
|
510
|
-
}
|
|
511
|
-
}, [changeSelectedNetwork, neuroSanURL, username]);
|
|
512
476
|
const onStreamingStarted = useCallback(() => {
|
|
513
477
|
// Reset agent counts
|
|
514
478
|
setAgentCounts(new Map());
|
|
@@ -582,7 +546,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
582
546
|
setIsStreaming(true);
|
|
583
547
|
}, children: _jsx(Grid, { id: "multi-agent-accelerator-grid-sidebar", size: enableZenMode && isStreaming ? 0 : 3.25, sx: {
|
|
584
548
|
height: "100%",
|
|
585
|
-
}, children: _jsx(Sidebar, { id: "multi-agent-accelerator-sidebar", isAwaitingLlm: isAwaitingLlm,
|
|
549
|
+
}, 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 }) }) }));
|
|
586
550
|
};
|
|
587
551
|
const getCenterPanel = () => {
|
|
588
552
|
return (_jsx(Grid, { id: "multi-agent-accelerator-grid-agent-flow", size: enableZenMode && isStreaming ? 18 : 8.25, sx: {
|
|
@@ -620,7 +584,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
620
584
|
},
|
|
621
585
|
}, children: _jsx(StopCircle, { fontSize: "large", id: "stop-button-icon" }) }) }) })) }));
|
|
622
586
|
};
|
|
623
|
-
const getDeleteNetworkConfirmationModal = () => confirmationModalOpen ? (_jsx(ConfirmationModal, { id: "delete-network-confirmation-modal", content: `The network "${
|
|
587
|
+
const getDeleteNetworkConfirmationModal = () => confirmationModalOpen ? (_jsx(ConfirmationModal, { id: "delete-network-confirmation-modal", content: `The network "${toDisplayName(networkToBeDeleted)}" will be deleted. ` +
|
|
624
588
|
"This action cannot be undone. Are you sure you want to proceed?", handleCancel: () => {
|
|
625
589
|
setConfirmationModalOpen(false);
|
|
626
590
|
setNetworkToBeDeleted(null);
|
|
@@ -635,17 +599,6 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
635
599
|
setNetworkToBeDeleted(null);
|
|
636
600
|
setConfirmationModalOpen(false);
|
|
637
601
|
}, title: "Delete Network" })) : null;
|
|
638
|
-
const getImportNetworkModal = () => (_jsx(ImportNetworkModal, { existingNetworkNames: temporaryNetworks.map((n) => n.agentNetworkName), isOpen: importModalOpen, onClose: () => setImportModalOpen(false), onImport: handleImportNetwork }));
|
|
639
|
-
// Blocking overlay shown while an imported network is being created on the backend.
|
|
640
|
-
const getImportBackdrop = () => (_jsx(Backdrop, { id: "multi-agent-accelerator-import-backdrop", "data-testid": "multi-agent-accelerator-import-backdrop", open: isImporting, sx: { zIndex: getZIndex(3, theme) }, children: _jsxs(Paper, { elevation: 6, role: "status", "aria-live": "polite", sx: {
|
|
641
|
-
display: "flex",
|
|
642
|
-
alignItems: "center",
|
|
643
|
-
gap: 2,
|
|
644
|
-
px: 4,
|
|
645
|
-
py: 2.5,
|
|
646
|
-
borderRadius: 2,
|
|
647
|
-
maxWidth: 480,
|
|
648
|
-
}, children: [_jsx(CircularProgress, { id: "multi-agent-accelerator-import-spinner", size: 24 }), _jsx(Typography, { id: "multi-agent-accelerator-import-title", variant: "body1", sx: { fontWeight: "bold" }, children: "Importing network..." })] }) }));
|
|
649
602
|
const getTourModal = () => tourModalOpen && (_jsx(MUIDialog, { contentSx: { fontSize: "0.8rem", minWidth: "550px", paddingTop: "0" }, footer: _jsxs(_Fragment, { children: [_jsx(StyledButton, { id: "tour-dont-show-again", onClick: () => {
|
|
650
603
|
setTourStatus(TourPromptState.DontShowAgain);
|
|
651
604
|
dismissTourModal();
|
|
@@ -694,7 +647,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
694
647
|
textAlign: "center",
|
|
695
648
|
},
|
|
696
649
|
}, children: _jsxs(Typography, { sx: { fontSize: "larger" }, children: ["API key(s) required for at least one of these providers:", " ", _jsx("strong", { children: [...getMissingApiKeys()].join(", ") }), ". Please add the required key(s) in \"Settings\" to use this Network."] }) }));
|
|
697
|
-
return (_jsxs(_Fragment, { children: [getMissingApiKeysAlert(), Tour, getTourModal(), getProgressPopper(), getDeleteNetworkConfirmationModal(),
|
|
650
|
+
return (_jsxs(_Fragment, { children: [getMissingApiKeysAlert(), Tour, getTourModal(), getProgressPopper(), getDeleteNetworkConfirmationModal(), _jsxs(Grid, { id: "multi-agent-accelerator-grid", container: true, columns: 18, sx: {
|
|
698
651
|
display: "flex",
|
|
699
652
|
flex: 1,
|
|
700
653
|
height: "85%",
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { TreeItemProps } from "@mui/x-tree-view/TreeItem";
|
|
2
2
|
import { FC } from "react";
|
|
3
|
+
export declare const EDIT_NETWORK_LABEL = "Edit this network";
|
|
4
|
+
export declare const DOWNLOAD_NETWORK_DEFINITION_LABEL = "Download network definition";
|
|
3
5
|
export interface AgentNetworkNodeProps extends TreeItemProps {
|
|
4
6
|
readonly onDeleteNetwork?: (network: string, isExpired: boolean) => void;
|
|
5
7
|
readonly onEditNetwork?: (network: string) => void;
|