@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.
- package/README.md +3 -3
- package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +9 -3
- package/dist/components/AgentChat/ChatCommon/ChatCommon.js +89 -23
- package/dist/components/AgentChat/ChatCommon/ChatHistory.d.ts +7 -0
- package/dist/components/AgentChat/ChatCommon/ChatHistory.js +1 -1
- package/dist/components/AgentChat/ChatCommon/ControlButtons.d.ts +4 -2
- package/dist/components/AgentChat/ChatCommon/ControlButtons.js +8 -2
- package/dist/components/AgentChat/ChatCommon/ConversationTurn.d.ts +5 -5
- package/dist/components/AgentChat/ChatCommon/ConversationTurn.js +1 -0
- package/dist/components/AgentChat/Common/Utils.d.ts +4 -0
- package/dist/components/AgentChat/Common/Utils.js +5 -1
- package/dist/components/Common/Breadcrumbs.js +1 -1
- package/dist/components/Common/notification.d.ts +4 -4
- package/dist/components/MultiAgentAccelerator/{AgentFlow.d.ts → AgentFlow/AgentFlow.d.ts} +15 -5
- package/dist/components/MultiAgentAccelerator/{AgentFlow.js → AgentFlow/AgentFlow.js} +44 -35
- package/dist/components/MultiAgentAccelerator/{AgentNode.d.ts → AgentFlow/AgentNode.d.ts} +1 -1
- package/dist/components/MultiAgentAccelerator/{AgentNode.js → AgentFlow/AgentNode.js} +4 -4
- package/dist/components/MultiAgentAccelerator/{GraphLayouts.d.ts → AgentFlow/GraphLayouts.d.ts} +4 -4
- package/dist/components/MultiAgentAccelerator/{GraphLayouts.js → AgentFlow/GraphLayouts.js} +6 -35
- package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.d.ts +21 -0
- package/dist/components/MultiAgentAccelerator/AgentFlow/GraphStructure.js +27 -0
- package/dist/components/MultiAgentAccelerator/AgentFlow/PlasmaEdge.d.ts +7 -0
- package/dist/components/MultiAgentAccelerator/{PlasmaEdge.js → AgentFlow/PlasmaEdge.js} +13 -8
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +116 -78
- package/dist/components/MultiAgentAccelerator/Schema/SlyData.d.ts +17 -0
- package/dist/components/MultiAgentAccelerator/Schema/SlyData.js +23 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +9 -4
- package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.d.ts +67 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/ImportNetworkModal.js +585 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.d.ts +2 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +19 -26
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +2 -1
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +24 -4
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +22 -0
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +74 -3
- package/dist/components/MultiAgentAccelerator/{ThoughtBubbleEdge.d.ts → ThoughtBubbles/ThoughtBubbleEdge.d.ts} +1 -1
- package/dist/components/MultiAgentAccelerator/{ThoughtBubbleOverlay.js → ThoughtBubbles/ThoughtBubbleOverlay.js} +1 -1
- package/dist/components/MultiAgentAccelerator/Tour/MainTourSteps.js +5 -0
- package/dist/components/MultiAgentAccelerator/const.d.ts +1 -0
- package/dist/components/MultiAgentAccelerator/const.js +2 -0
- package/dist/components/Settings/SettingsDialog.js +26 -10
- package/dist/controller/agent/Agent.d.ts +14 -6
- package/dist/controller/agent/Agent.js +18 -14
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/state/Settings.d.ts +81 -4
- package/dist/state/Settings.js +87 -7
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/dist/utils/BrowserNavigation.js +2 -0
- package/dist/utils/File.d.ts +4 -1
- package/dist/utils/File.js +4 -9
- package/dist/utils/text.js +3 -7
- package/package.json +9 -10
- package/dist/components/MultiAgentAccelerator/PlasmaEdge.d.ts +0 -3
- /package/dist/components/MultiAgentAccelerator/{ThoughtBubbleEdge.js → ThoughtBubbles/ThoughtBubbleEdge.js} +0 -0
- /package/dist/components/MultiAgentAccelerator/{ThoughtBubbleOverlay.d.ts → ThoughtBubbles/ThoughtBubbleOverlay.d.ts} +0 -0
|
@@ -20,9 +20,10 @@ 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 {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
23
|
+
import { cleanUpAgentName, KNOWN_MESSAGE_TYPES_FOR_PLASMA } from "../../AgentChat/Common/Utils.js";
|
|
24
|
+
import { BASE_RADIUS, DEFAULT_FRONTMAN_X_POS, DEFAULT_FRONTMAN_Y_POS, LEVEL_SPACING } from "../const.js";
|
|
25
|
+
import { getFrontman, getParentAgents, getParents } from "./GraphStructure.js";
|
|
26
|
+
import { isEditableAgent } from "../TemporaryNetworks.js";
|
|
26
27
|
export const MAX_GLOBAL_THOUGHT_BUBBLES = 5;
|
|
27
28
|
const FRONTMAN_OFFSET_X = (NODE_WIDTH * (FRONTMAN_SIZE_MULTIPLIER - 1)) / 2;
|
|
28
29
|
const FRONTMAN_OFFSET_Y = (NODE_HEIGHT * (FRONTMAN_SIZE_MULTIPLIER - 1)) / 2;
|
|
@@ -60,34 +61,6 @@ const areInSameConversation = (conversations, sourceAgent, targetAgent) => {
|
|
|
60
61
|
// Name for custom node
|
|
61
62
|
const AGENT_NODE_TYPE_NAME = "agentNode";
|
|
62
63
|
// #endregion: Constants
|
|
63
|
-
/**
|
|
64
|
-
* Returns the "origins" (node names) of the _immediate_ parents of a node in the agent network. Grandparents and
|
|
65
|
-
* higher are not included.
|
|
66
|
-
*
|
|
67
|
-
* @param node Node ID for which to find parents
|
|
68
|
-
* @param parentAgents Full list of parent agents in the network
|
|
69
|
-
* @returns The IDs of the immediate parent nodes for the given node or empty array if no parents are found (frontman)
|
|
70
|
-
*/
|
|
71
|
-
const getParents = (node, parentAgents) => {
|
|
72
|
-
return parentAgents.filter((agent) => agent.tools.includes(node)).map((parentNode) => parentNode.origin);
|
|
73
|
-
};
|
|
74
|
-
// "Parent agents" are those that have tools, aka "child agents"
|
|
75
|
-
const getParentAgents = (agentsInNetwork) => agentsInNetwork.length === 1 ? agentsInNetwork : agentsInNetwork.filter((agent) => agent.tools?.length > 0);
|
|
76
|
-
// "Child agents" are those that are declared as tools by other agents
|
|
77
|
-
const getChildAgents = (parentAgents) => {
|
|
78
|
-
const childAgentsSet = new Set();
|
|
79
|
-
parentAgents.forEach((agent) => {
|
|
80
|
-
agent?.tools?.forEach((tool) => {
|
|
81
|
-
childAgentsSet.add(tool);
|
|
82
|
-
});
|
|
83
|
-
});
|
|
84
|
-
return childAgentsSet;
|
|
85
|
-
};
|
|
86
|
-
// Frontman is defined as the agent that has no parent. There should be only one in the graph.
|
|
87
|
-
// Frontman is one of the parent agents.
|
|
88
|
-
// There is no explicit field that tells us which agent is the frontman, so we determine it by checking
|
|
89
|
-
// which parent agent does not appear in the set of child agents.
|
|
90
|
-
const getFrontman = (parentAgents, childAgents) => parentAgents.find((agent) => !childAgents.has(agent.origin));
|
|
91
64
|
// Generates the properties for an edge in the graph.
|
|
92
65
|
// Common for both radial and linear layouts.
|
|
93
66
|
const getEdgeProperties = (sourceId, targetId, sourceHandle, targetHandle, isAnimated) => {
|
|
@@ -109,8 +82,7 @@ export const layoutRadial = ({ agentCounts, agentIconSuggestions = null, agentsI
|
|
|
109
82
|
const nodeDepths = new Map();
|
|
110
83
|
const queue = [];
|
|
111
84
|
const parentAgents = getParentAgents(agentsInNetwork);
|
|
112
|
-
const
|
|
113
|
-
const frontman = getFrontman(parentAgents, childAgents);
|
|
85
|
+
const frontman = getFrontman(agentsInNetwork);
|
|
114
86
|
if (frontman) {
|
|
115
87
|
// Add the frontman node to the network
|
|
116
88
|
queue.push({ id: frontman.origin, depth: 0 });
|
|
@@ -220,8 +192,7 @@ export const layoutLinear = ({ agentCounts, agentIconSuggestions = null, agentsI
|
|
|
220
192
|
const edgesInNetwork = [];
|
|
221
193
|
// Do these calculations outside the loop for efficiency
|
|
222
194
|
const parentAgents = getParentAgents(agentsInNetwork);
|
|
223
|
-
const
|
|
224
|
-
const frontman = getFrontman(parentAgents, childAgents);
|
|
195
|
+
const frontman = getFrontman(agentsInNetwork);
|
|
225
196
|
agentsInNetwork.forEach(({ origin: nodeId }) => {
|
|
226
197
|
const parentIds = getParents(nodeId, parentAgents);
|
|
227
198
|
const isFrontman = frontman?.origin === nodeId;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { ConnectivityInfo } from "../../../generated/neuro-san/NeuroSanClient.js";
|
|
2
|
+
export declare const getParentAgents: <T extends ConnectivityInfo>(agents: readonly T[]) => readonly T[];
|
|
3
|
+
/**
|
|
4
|
+
* Determines the frontman of an agent network — the single source of truth for "who is the frontman".
|
|
5
|
+
*
|
|
6
|
+
* The frontman is the agent with no parent: a "parent" agent (one that has tools) that is not itself
|
|
7
|
+
* listed as a tool by any other agent. A lone single-agent network is its own frontman. Returns
|
|
8
|
+
* undefined when no agent qualifies (e.g. a fully cyclic network); callers decide how to fall back.
|
|
9
|
+
*
|
|
10
|
+
* Consumed by the graph layouts (to seed the depth-0 node) and by the network import flow.
|
|
11
|
+
*/
|
|
12
|
+
export declare const getFrontman: <T extends ConnectivityInfo>(agents: readonly T[]) => T | undefined;
|
|
13
|
+
/**
|
|
14
|
+
* Returns the "origins" (node names) of the _immediate_ parents of a node in the agent network. Grandparents and
|
|
15
|
+
* higher are not included.
|
|
16
|
+
*
|
|
17
|
+
* @param node Node ID for which to find parents
|
|
18
|
+
* @param parentAgents Full list of parent agents in the network
|
|
19
|
+
* @returns The IDs of the immediate parent nodes for the given node or empty array if no parents are found (frontman)
|
|
20
|
+
*/
|
|
21
|
+
export declare const getParents: (node: string, parentAgents: readonly ConnectivityInfo[]) => string[];
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
// "Parent agents" are those that have tools, aka agents with "child agents". A lone single-agent
|
|
2
|
+
// network is treated as its own parent.
|
|
3
|
+
export const getParentAgents = (agents) => agents.length === 1 ? agents : agents.filter((agent) => (agent.tools?.length ?? 0) > 0);
|
|
4
|
+
/**
|
|
5
|
+
* Determines the frontman of an agent network — the single source of truth for "who is the frontman".
|
|
6
|
+
*
|
|
7
|
+
* The frontman is the agent with no parent: a "parent" agent (one that has tools) that is not itself
|
|
8
|
+
* listed as a tool by any other agent. A lone single-agent network is its own frontman. Returns
|
|
9
|
+
* undefined when no agent qualifies (e.g. a fully cyclic network); callers decide how to fall back.
|
|
10
|
+
*
|
|
11
|
+
* Consumed by the graph layouts (to seed the depth-0 node) and by the network import flow.
|
|
12
|
+
*/
|
|
13
|
+
export const getFrontman = (agents) => {
|
|
14
|
+
const parentAgents = getParentAgents(agents);
|
|
15
|
+
// Child agents are everything referenced as a tool by a parent.
|
|
16
|
+
const childAgents = new Set(parentAgents.flatMap((agent) => agent.tools ?? []));
|
|
17
|
+
return parentAgents.find((agent) => agent.origin !== undefined && !childAgents.has(agent.origin));
|
|
18
|
+
};
|
|
19
|
+
/**
|
|
20
|
+
* Returns the "origins" (node names) of the _immediate_ parents of a node in the agent network. Grandparents and
|
|
21
|
+
* higher are not included.
|
|
22
|
+
*
|
|
23
|
+
* @param node Node ID for which to find parents
|
|
24
|
+
* @param parentAgents Full list of parent agents in the network
|
|
25
|
+
* @returns The IDs of the immediate parent nodes for the given node or empty array if no parents are found (frontman)
|
|
26
|
+
*/
|
|
27
|
+
export const getParents = (node, parentAgents) => parentAgents.filter((agent) => agent.tools.includes(node)).map((parentNode) => parentNode.origin);
|
|
@@ -16,7 +16,11 @@ limitations under the License.
|
|
|
16
16
|
*/
|
|
17
17
|
import { getBezierPath } from "@xyflow/react";
|
|
18
18
|
import { useEffect, useRef } from "react";
|
|
19
|
-
import { useSettingsStore } from "
|
|
19
|
+
import { useSettingsStore } from "../../../state/Settings.js";
|
|
20
|
+
//#region Constants
|
|
21
|
+
const DEFAULT_MAX_PARTICLES = 250;
|
|
22
|
+
const DEFAULT_PARTICLES_PER_FRAME = 25;
|
|
23
|
+
//#endregion Interfaces and Types
|
|
20
24
|
const createFunnelParticleOnPath = (pathEl, canvasOffset, baseProgress, plasmaColor) => {
|
|
21
25
|
const totalLength = pathEl.getTotalLength();
|
|
22
26
|
const speed = 0.02 + Math.random() * 0.003;
|
|
@@ -64,7 +68,7 @@ const createFunnelParticleOnPath = (pathEl, canvasOffset, baseProgress, plasmaCo
|
|
|
64
68
|
const isAlive = () => progress * totalLength < totalLength * 0.98;
|
|
65
69
|
return { update, draw, isAlive };
|
|
66
70
|
};
|
|
67
|
-
export const PlasmaEdge = ({
|
|
71
|
+
export const PlasmaEdge = ({ maxParticles = DEFAULT_MAX_PARTICLES, particlesPerFrame = DEFAULT_PARTICLES_PER_FRAME, sourcePosition, sourceX, sourceY, targetPosition, targetX, targetY, }) => {
|
|
68
72
|
const canvasRef = useRef(null);
|
|
69
73
|
const pathRef = useRef(null);
|
|
70
74
|
const animationRef = useRef(null);
|
|
@@ -97,12 +101,10 @@ export const PlasmaEdge = ({ sourceX, sourceY, targetX, targetY, sourcePosition,
|
|
|
97
101
|
ctx.setTransform(1, 0, 0, 1, 0, 0);
|
|
98
102
|
ctx.scale(dpr, dpr);
|
|
99
103
|
const canvasOffset = { x, y };
|
|
100
|
-
const MAX_PARTICLES = 250;
|
|
101
|
-
const PARTICLES_PER_FRAME = 25;
|
|
102
104
|
const animate = () => {
|
|
103
105
|
ctx.clearRect(0, 0, width, height);
|
|
104
|
-
for (let i = 0; i <
|
|
105
|
-
if (particles.current.length <
|
|
106
|
+
for (let i = 0; i < particlesPerFrame; i += 1) {
|
|
107
|
+
if (particles.current.length < maxParticles) {
|
|
106
108
|
const t = Math.random();
|
|
107
109
|
if (Math.random() < 1 - t) {
|
|
108
110
|
particles.current.push(createFunnelParticleOnPath(pathEl, canvasOffset, t, plasmaColor));
|
|
@@ -118,9 +120,12 @@ export const PlasmaEdge = ({ sourceX, sourceY, targetX, targetY, sourcePosition,
|
|
|
118
120
|
};
|
|
119
121
|
animate();
|
|
120
122
|
return () => {
|
|
121
|
-
if (animationRef.current !== undefined)
|
|
123
|
+
if (animationRef.current !== undefined) {
|
|
122
124
|
cancelAnimationFrame(animationRef.current);
|
|
125
|
+
animationRef.current = undefined;
|
|
126
|
+
}
|
|
127
|
+
particles.current = [];
|
|
123
128
|
};
|
|
124
|
-
}, [edgePath, width, height, plasmaColor, x, y]);
|
|
129
|
+
}, [edgePath, width, height, plasmaColor, x, y, particlesPerFrame, maxParticles]);
|
|
125
130
|
return (_jsxs(_Fragment, { children: [_jsx("foreignObject", { id: `foreign-object-${x}-${y}`, width: width, height: height, x: x, y: y, style: { pointerEvents: "none", overflow: "visible" }, children: _jsx("canvas", { id: `canvas-${x}-${y}`, ref: canvasRef }) }), _jsx("path", { id: `path-${edgePath}`, ref: pathRef, d: edgePath, fill: "none", stroke: "none", style: { position: "absolute", visibility: "hidden" } })] }));
|
|
126
131
|
};
|
|
@@ -15,8 +15,11 @@ 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";
|
|
18
19
|
import Box from "@mui/material/Box";
|
|
20
|
+
import CircularProgress from "@mui/material/CircularProgress";
|
|
19
21
|
import Grid from "@mui/material/Grid";
|
|
22
|
+
import Paper from "@mui/material/Paper";
|
|
20
23
|
import Slide from "@mui/material/Slide";
|
|
21
24
|
import { useTheme } from "@mui/material/styles";
|
|
22
25
|
import Tooltip from "@mui/material/Tooltip";
|
|
@@ -26,15 +29,16 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
26
29
|
import { useJoyride } from "react-joyride";
|
|
27
30
|
import { extractConversations } from "./AgentConversations.js";
|
|
28
31
|
import { getUpdatedAgentCounts } from "./AgentCounts.js";
|
|
29
|
-
import { AgentFlow } from "./AgentFlow.js";
|
|
32
|
+
import { AgentFlow } from "./AgentFlow/AgentFlow.js";
|
|
30
33
|
import { extractAgentNetworkDesignerProgress } from "./AgentNetworkDesigner.js";
|
|
31
|
-
import { AGENT_NETWORK_DEFINITION_KEY, AGENT_NETWORK_DESIGNER_ID, AGENT_NETWORK_HOCON, AGENT_NETWORK_NAME_KEY, GRACE_PERIOD_MS, SHOW_TOUR_DELAY_MS, TRIGGER_APP_TOUR_EVENT_NAME, } from "./const.js";
|
|
34
|
+
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";
|
|
32
36
|
import { Sidebar } from "./Sidebar/Sidebar.js";
|
|
33
|
-
import { extractTemporaryNetworksFromMessage, isTemporaryNetwork,
|
|
34
|
-
import {
|
|
37
|
+
import { extractTemporaryNetworksFromMessage, IMPORT_FAILURE_DETAIL, importNetworkFromJson, isTemporaryNetwork, notifySaveError, streamNetworkDesignerUpsert, } from "./TemporaryNetworks.js";
|
|
38
|
+
import { MAIN_TOUR_STEPS } from "./Tour/MainTourSteps.js";
|
|
39
|
+
import { getAgentFunction, getAgentNetworks, getConnectivity } from "../../controller/agent/Agent.js";
|
|
35
40
|
import { getAgentIconSuggestions, getNetworkIconSuggestions } from "../../controller/agent/IconSuggestions.js";
|
|
36
41
|
import { useAgentChatHistoryStore } from "../../state/ChatHistory.js";
|
|
37
|
-
import { useSettingsStore } from "../../state/Settings.js";
|
|
38
42
|
import { useTempNetworksStore } from "../../state/TemporaryNetworks.js";
|
|
39
43
|
import { TourPromptState, useTourStore } from "../../state/Tour.js";
|
|
40
44
|
import { getZIndex } from "../../utils/zIndexLayers.js";
|
|
@@ -43,47 +47,15 @@ import { LlmChatButton } from "../AgentChat/Common/LlmChatButton.js";
|
|
|
43
47
|
import { isLegacyAgentType } from "../AgentChat/Common/Types.js";
|
|
44
48
|
import { chatMessageFromChunk, cleanUpAgentName, removeTrailingUuid } from "../AgentChat/Common/Utils.js";
|
|
45
49
|
import { ConfirmationModal, StyledButton } from "../Common/ConfirmationModal.js";
|
|
46
|
-
import {
|
|
47
|
-
import { MAIN_TOUR_STEPS } from "./Tour/MainTourSteps.js";
|
|
50
|
+
import { MUIAlert } from "../Common/MUIAlert.js";
|
|
48
51
|
import { MUIDialog } from "../Common/MUIDialog.js";
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
import { closeNotification, NotificationType, sendNotification } from "../Common/notification.js";
|
|
53
|
+
import { BYOK } from "./Schema/SlyData.js";
|
|
54
|
+
import { getApiKey, LLM_PROVIDER_API_KEY_FIELD, useSettingsStore } from "../../state/Settings.js";
|
|
51
55
|
// Animation time for the left and right panels to slide in or out when launching the animation
|
|
52
56
|
const GROW_ANIMATION_TIME_MS = 800;
|
|
53
57
|
// Optimization to avoid creating a new empty map on every render
|
|
54
58
|
const EMPTY_THOUGHT_BUBBLE_EDGES = new Map();
|
|
55
|
-
// #region: Agent-save helpers
|
|
56
|
-
/**
|
|
57
|
-
* Extracts TemporaryNetworks from a single streamed chunk, merging into `accumulated`.
|
|
58
|
-
* Returns `accumulated` unchanged if the chunk yields no reservations or on parse error.
|
|
59
|
-
*/
|
|
60
|
-
const collectNetworksFromChunk = (chunk, updated, accumulated) => {
|
|
61
|
-
try {
|
|
62
|
-
const chatMessage = chatMessageFromChunk(chunk);
|
|
63
|
-
if (!chatMessage)
|
|
64
|
-
return accumulated;
|
|
65
|
-
// Always use the user's edited definition as the authoritative value.
|
|
66
|
-
const converted = extractTemporaryNetworksFromMessage(chatMessage, updated);
|
|
67
|
-
if (converted.length === 0)
|
|
68
|
-
return accumulated;
|
|
69
|
-
return mergeNetworks(accumulated, converted);
|
|
70
|
-
}
|
|
71
|
-
catch (e) {
|
|
72
|
-
console.warn("Failed to process chunk from network designer:", e);
|
|
73
|
-
return accumulated;
|
|
74
|
-
}
|
|
75
|
-
};
|
|
76
|
-
/** Logs and notifies about a save error. Suppresses AbortError (user-canceled). */
|
|
77
|
-
const notifySaveError = (agentName, e) => {
|
|
78
|
-
if (e instanceof DOMException && e.name === "AbortError")
|
|
79
|
-
return;
|
|
80
|
-
console.error("Failed to submit agent network update:", e);
|
|
81
|
-
const detail = e instanceof DOMException && e.name === "TimeoutError"
|
|
82
|
-
? "The request timed out waiting for the server. Please try again."
|
|
83
|
-
: String(e);
|
|
84
|
-
sendNotification(NotificationType.error, `Failed to update network "${agentName}".`, detail);
|
|
85
|
-
};
|
|
86
|
-
// #endregion: Agent-save helpers
|
|
87
59
|
/**
|
|
88
60
|
* Main Multi-Agent Accelerator component that contains the sidebar, agent flow, and chat components.
|
|
89
61
|
* @param backendNeuroSanApiUrl Initial URL of the backend Neuro-San API. User can change this in the UI.
|
|
@@ -107,6 +79,8 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
107
79
|
const temporaryNetworks = useTempNetworksStore((state) => state.tempNetworks);
|
|
108
80
|
// Track newly added temp networks so we can highlight them
|
|
109
81
|
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);
|
|
110
84
|
const [networkIconSuggestions, setNetworkIconSuggestions] = useState({});
|
|
111
85
|
const [agentsInNetwork, setAgentsInNetwork] = useState([]);
|
|
112
86
|
const [sampleQueries, setSampleQueries] = useState([]);
|
|
@@ -117,7 +91,8 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
117
91
|
const [selectedNetwork, setSelectedNetwork] = useState(null);
|
|
118
92
|
const [networkDescription, setNetworkDescription] = useState("");
|
|
119
93
|
const networkDisplayName = useMemo(() => (useNativeNames ? selectedNetwork : cleanUpAgentName(removeTrailingUuid(selectedNetwork))), [selectedNetwork, useNativeNames]);
|
|
120
|
-
|
|
94
|
+
// LLM providers for which BYOK keys are supported by the current network
|
|
95
|
+
const [supportedByokProviders, setSupportedByokProviders] = useState(new Set());
|
|
121
96
|
const neuroSanURL = useSettingsStore((state) => state.settings.externalServices.neuroSanUrl) ?? defaultNeuroSanUrl;
|
|
122
97
|
// Tracks how many times each agent has been involved in the conversation
|
|
123
98
|
const [agentCounts, setAgentCounts] = useState(new Map());
|
|
@@ -132,6 +107,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
132
107
|
// State to hold thought bubble edges - avoids duplicates across layout recalculations
|
|
133
108
|
const [thoughtBubbleEdges, setThoughtBubbleEdges] = useState(new Map());
|
|
134
109
|
const [confirmationModalOpen, setConfirmationModalOpen] = useState(false);
|
|
110
|
+
const [importModalOpen, setImportModalOpen] = useState(false);
|
|
135
111
|
const [tourModalOpen, setTourModalOpen] = useState(false);
|
|
136
112
|
const [haveShownTourModal, setHaveShownTourModal] = useState(false);
|
|
137
113
|
// Memoized key for agent names to trigger icon suggestion updates when the set of agents changes, not just
|
|
@@ -201,16 +177,14 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
201
177
|
* Builds the API keys object to be sent as extraSlyData with each request, based on the LLM providers required
|
|
202
178
|
* by the currently selected network. Only includes keys for providers that are required.
|
|
203
179
|
*/
|
|
204
|
-
const getApiKeys = () => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return { llm_config: llmConfig };
|
|
213
|
-
};
|
|
180
|
+
const getApiKeys = () => ({
|
|
181
|
+
llm_config: Object.fromEntries([...supportedByokProviders]
|
|
182
|
+
.map((provider) => [
|
|
183
|
+
LLM_PROVIDER_API_KEY_FIELD[provider],
|
|
184
|
+
getApiKey(apiKeys, provider),
|
|
185
|
+
])
|
|
186
|
+
.filter((entry) => Boolean(entry[1]))),
|
|
187
|
+
});
|
|
214
188
|
/**
|
|
215
189
|
* Builds the extraSlyData object to be sent with each request, including information for Agent Network Designer
|
|
216
190
|
* and (if required) API keys for LLM providers.
|
|
@@ -237,7 +211,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
237
211
|
return undefined;
|
|
238
212
|
};
|
|
239
213
|
// Whether any API keys are required
|
|
240
|
-
const anyApiKeysRequired =
|
|
214
|
+
const anyApiKeysRequired = supportedByokProviders.size > 0;
|
|
241
215
|
// Build base extraSlyData
|
|
242
216
|
const baseExtraSlyData = buildExtraSlyData();
|
|
243
217
|
// Add API keys to extraSlyData if needed, merging with baseExtraSlyData if it exists
|
|
@@ -252,7 +226,33 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
252
226
|
chatRef.current?.handleStop();
|
|
253
227
|
resetState();
|
|
254
228
|
}, [resetState]);
|
|
255
|
-
|
|
229
|
+
/**
|
|
230
|
+
* Maps an API key name to the corresponding LLM provider
|
|
231
|
+
*/
|
|
232
|
+
const apiKeyToProvider = useCallback((key) => {
|
|
233
|
+
switch (key) {
|
|
234
|
+
case "openai_api_key":
|
|
235
|
+
return "OpenAI";
|
|
236
|
+
case "anthropic_api_key":
|
|
237
|
+
return "Anthropic";
|
|
238
|
+
default:
|
|
239
|
+
return null;
|
|
240
|
+
}
|
|
241
|
+
}, []);
|
|
242
|
+
/**
|
|
243
|
+
* Given a SlyData schema, returns the set of LLM providers that are supported by the agent network.
|
|
244
|
+
* @note Semantics are "oneOf": a key must be supplied for at least one of the named providers
|
|
245
|
+
*/
|
|
246
|
+
const getSupportedLlmProviders = useCallback((schema) => {
|
|
247
|
+
const parsed = BYOK.safeParse(schema);
|
|
248
|
+
if (!parsed.success || !parsed.data.required?.includes("llm_config")) {
|
|
249
|
+
return new Set();
|
|
250
|
+
}
|
|
251
|
+
const providerKeys = Object.keys(parsed.data.properties?.llm_config?.properties ?? {});
|
|
252
|
+
return new Set(providerKeys
|
|
253
|
+
.map((key) => apiKeyToProvider(key))
|
|
254
|
+
.filter((provider) => provider !== null));
|
|
255
|
+
}, [apiKeyToProvider]);
|
|
256
256
|
useEffect(() => {
|
|
257
257
|
;
|
|
258
258
|
(async () => {
|
|
@@ -273,23 +273,14 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
273
273
|
const fetchAgentDetails = async () => {
|
|
274
274
|
// It is a Neuro-san agent, so get the function and connectivity info
|
|
275
275
|
try {
|
|
276
|
+
// Reset before fetching
|
|
277
|
+
setNetworkDescription("");
|
|
278
|
+
setSupportedByokProviders(new Set());
|
|
279
|
+
// Get agent function from server
|
|
276
280
|
const agentFunction = await getAgentFunction(neuroSanURL, selectedNetwork, username);
|
|
277
281
|
setNetworkDescription(agentFunction?.function?.description || "");
|
|
278
282
|
const schema = agentFunction?.function?.sly_data_schema;
|
|
279
|
-
|
|
280
|
-
const llmConfig = isRecord(schemaProperties) ? schemaProperties["llm_config"] : undefined;
|
|
281
|
-
const llmConfigRequired = isRecord(llmConfig) ? llmConfig["required"] : undefined;
|
|
282
|
-
setProviderKeysRequired(new Set((Array.isArray(llmConfigRequired) ? llmConfigRequired : []).flatMap((key) => {
|
|
283
|
-
if (key === "openai_api_key") {
|
|
284
|
-
return ["OpenAI"];
|
|
285
|
-
}
|
|
286
|
-
if (key === "anthropic_api_key") {
|
|
287
|
-
return ["Anthropic"];
|
|
288
|
-
}
|
|
289
|
-
console.warn(`Unknown API key requirement "${key}" for network ${selectedNetwork}. Skipping.`);
|
|
290
|
-
// Will get dropped by flatMap
|
|
291
|
-
return [];
|
|
292
|
-
})));
|
|
283
|
+
setSupportedByokProviders(getSupportedLlmProviders(schema));
|
|
293
284
|
}
|
|
294
285
|
catch (e) {
|
|
295
286
|
console.warn(`Unable to get agent details for network ${selectedNetwork}:`, e);
|
|
@@ -300,7 +291,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
300
291
|
if (selectedNetwork && !isLegacyAgentType(selectedNetwork)) {
|
|
301
292
|
void fetchAgentDetails();
|
|
302
293
|
}
|
|
303
|
-
}, [neuroSanURL, selectedNetwork, username]);
|
|
294
|
+
}, [getSupportedLlmProviders, neuroSanURL, selectedNetwork, username]);
|
|
304
295
|
useEffect(() => {
|
|
305
296
|
;
|
|
306
297
|
(async () => {
|
|
@@ -466,10 +457,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
466
457
|
*/
|
|
467
458
|
const onSaveAgent = useCallback(async (agentName, updated, agentNetworkName, signal) => {
|
|
468
459
|
try {
|
|
469
|
-
|
|
470
|
-
await sendNetworkDesignerUpdate(neuroSanURL, signal, agentName, updated, agentNetworkName, username, (chunk) => {
|
|
471
|
-
newNetworks = collectNetworksFromChunk(chunk, updated, newNetworks);
|
|
472
|
-
});
|
|
460
|
+
const newNetworks = await streamNetworkDesignerUpsert(neuroSanURL, signal, agentName, updated, agentNetworkName, username);
|
|
473
461
|
if (newNetworks.length === 0) {
|
|
474
462
|
sendNotification(NotificationType.error, `Failed to update network "${agentName}".`, "The network designer did not return a reservation. Please try again.");
|
|
475
463
|
return;
|
|
@@ -492,6 +480,35 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
492
480
|
notifySaveError(agentName, e);
|
|
493
481
|
}
|
|
494
482
|
}, [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]);
|
|
495
512
|
const onStreamingStarted = useCallback(() => {
|
|
496
513
|
// Reset agent counts
|
|
497
514
|
setAgentCounts(new Map());
|
|
@@ -511,7 +528,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
511
528
|
}, [resetState]);
|
|
512
529
|
const handleDeleteNetwork = (networkId, isExpired) => {
|
|
513
530
|
if (isExpired) {
|
|
514
|
-
// It's expired so just delete it without confirmation
|
|
531
|
+
// It's expired, so just delete it without confirmation
|
|
515
532
|
const tempNetworksWithoutThisOne = temporaryNetworks.filter((network) => network.agentInfo.agent_name !== networkId);
|
|
516
533
|
useTempNetworksStore.getState().setTempNetworks(tempNetworksWithoutThisOne);
|
|
517
534
|
useAgentChatHistoryStore.getState().resetHistory(networkId);
|
|
@@ -555,14 +572,17 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
555
572
|
}
|
|
556
573
|
}, [tourRequested, selectedNetwork, agentsInNetwork, networks, controls, setTourStatus]);
|
|
557
574
|
const getMissingApiKeys = () => {
|
|
558
|
-
|
|
575
|
+
// Calculate intersection of what we have (apiKeys) and what is required (providerKeysRequired)
|
|
576
|
+
const intersection = new Set([...supportedByokProviders].filter((provider) => getApiKey(apiKeys, provider)));
|
|
577
|
+
return intersection.size === 0 ? supportedByokProviders : new Set();
|
|
559
578
|
};
|
|
579
|
+
const hasMissingApiKeys = getMissingApiKeys().size > 0;
|
|
560
580
|
const getLeftPanel = () => {
|
|
561
581
|
return (_jsx(Slide, { id: "multi-agent-accelerator-grid-sidebar-slide", in: !enableZenMode || !isAwaitingLlm, direction: "right", timeout: GROW_ANIMATION_TIME_MS, onExited: () => {
|
|
562
582
|
setIsStreaming(true);
|
|
563
583
|
}, children: _jsx(Grid, { id: "multi-agent-accelerator-grid-sidebar", size: enableZenMode && isStreaming ? 0 : 3.25, sx: {
|
|
564
584
|
height: "100%",
|
|
565
|
-
}, children: _jsx(Sidebar, { id: "multi-agent-accelerator-sidebar", isAwaitingLlm: isAwaitingLlm, networkIconSuggestions: networkIconSuggestions, networks: networks, neuroSanServerURL: neuroSanURL, newlyAddedTemporaryNetworks: newlyAddedTemporaryNetworks, onDeleteNetwork: handleDeleteNetwork, onEditNetwork: handleEditNetwork, setSelectedNetwork: (newNetwork) => changeSelectedNetwork(newNetwork), temporaryNetworks: temporaryNetworks }) }) }));
|
|
585
|
+
}, children: _jsx(Sidebar, { id: "multi-agent-accelerator-sidebar", isAwaitingLlm: isAwaitingLlm, isImporting: isImporting, networkIconSuggestions: networkIconSuggestions, networks: networks, neuroSanServerURL: neuroSanURL, newlyAddedTemporaryNetworks: newlyAddedTemporaryNetworks, onDeleteNetwork: handleDeleteNetwork, onEditNetwork: handleEditNetwork, onImportClick: () => setImportModalOpen(true), setSelectedNetwork: (newNetwork) => changeSelectedNetwork(newNetwork), temporaryNetworks: temporaryNetworks }) }) }));
|
|
566
586
|
};
|
|
567
587
|
const getCenterPanel = () => {
|
|
568
588
|
return (_jsx(Grid, { id: "multi-agent-accelerator-grid-agent-flow", size: enableZenMode && isStreaming ? 18 : 8.25, sx: {
|
|
@@ -586,7 +606,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
586
606
|
[AGENT_NETWORK_DESIGNER_ID]: "Describe in plain language the network you would like to build.",
|
|
587
607
|
}, currentUser: username, customAgentGreetings: {
|
|
588
608
|
[AGENT_NETWORK_DESIGNER_ID]: "Let's build a network together!",
|
|
589
|
-
}, extraSlyData: extraSlyData, id: "agent-network-ui", isAwaitingLlm: isAwaitingLlm,
|
|
609
|
+
}, extraSlyData: extraSlyData, id: "agent-network-ui", isAwaitingLlm: isAwaitingLlm, hasMissingApiKeys: hasMissingApiKeys, networkDescription: networkDescription, neuroSanURL: neuroSanURL, onChunkReceived: onChunkReceived, onStreamingComplete: onStreamingComplete, onStreamingStarted: onStreamingStarted, ref: chatRef, sampleQueries: sampleQueries, setIsAwaitingLlm: setIsAwaitingLlm, selectedNetwork: selectedNetwork, setSelectedNetwork: changeSelectedNetwork }, selectedNetwork ?? "no-network") }) }));
|
|
590
610
|
};
|
|
591
611
|
const getStopButton = () => {
|
|
592
612
|
return (_jsx(_Fragment, { children: isAwaitingLlm && enableZenMode && (_jsx(Box, { id: "stop-button-container", sx: {
|
|
@@ -615,6 +635,17 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
615
635
|
setNetworkToBeDeleted(null);
|
|
616
636
|
setConfirmationModalOpen(false);
|
|
617
637
|
}, 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..." })] }) }));
|
|
618
649
|
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: () => {
|
|
619
650
|
setTourStatus(TourPromptState.DontShowAgain);
|
|
620
651
|
dismissTourModal();
|
|
@@ -656,7 +687,14 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
656
687
|
opacity: "95%",
|
|
657
688
|
width: "100%",
|
|
658
689
|
}, 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..." }))] }) }) }));
|
|
659
|
-
|
|
690
|
+
const getMissingApiKeysAlert = () => hasMissingApiKeys && (_jsx(MUIAlert, { closeable: false, id: "llm-chat-missing-api-keys-alert", severity: "warning", sx: {
|
|
691
|
+
my: 1.5,
|
|
692
|
+
"& .MuiAlert-message": {
|
|
693
|
+
width: "100%",
|
|
694
|
+
textAlign: "center",
|
|
695
|
+
},
|
|
696
|
+
}, 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(), getImportNetworkModal(), getImportBackdrop(), _jsxs(Grid, { id: "multi-agent-accelerator-grid", container: true, columns: 18, sx: {
|
|
660
698
|
display: "flex",
|
|
661
699
|
flex: 1,
|
|
662
700
|
height: "85%",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for sly_data BYOK structure
|
|
4
|
+
*/
|
|
5
|
+
export declare const BYOK: z.ZodObject<{
|
|
6
|
+
type: z.ZodOptional<z.ZodString>;
|
|
7
|
+
required: z.ZodOptional<z.ZodTuple<[z.ZodLiteral<"llm_config">], null>>;
|
|
8
|
+
properties: z.ZodOptional<z.ZodObject<{
|
|
9
|
+
llm_config: z.ZodOptional<z.ZodObject<{
|
|
10
|
+
type: z.ZodOptional<z.ZodString>;
|
|
11
|
+
properties: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
12
|
+
type: z.ZodOptional<z.ZodString>;
|
|
13
|
+
description: z.ZodOptional<z.ZodString>;
|
|
14
|
+
}, z.core.$loose>>>;
|
|
15
|
+
}, z.core.$loose>>;
|
|
16
|
+
}, z.core.$loose>>;
|
|
17
|
+
}, z.core.$loose>;
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import * as z from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* Zod schema for sly_data BYOK structure
|
|
4
|
+
*/
|
|
5
|
+
export const BYOK = z.looseObject({
|
|
6
|
+
type: z.string().optional(),
|
|
7
|
+
required: z.tuple([z.literal("llm_config")]).optional(),
|
|
8
|
+
properties: z
|
|
9
|
+
.looseObject({
|
|
10
|
+
llm_config: z
|
|
11
|
+
.looseObject({
|
|
12
|
+
type: z.string().optional(),
|
|
13
|
+
properties: z
|
|
14
|
+
.record(z.string(), z.looseObject({
|
|
15
|
+
type: z.string().optional(),
|
|
16
|
+
description: z.string().optional(),
|
|
17
|
+
}))
|
|
18
|
+
.optional(),
|
|
19
|
+
})
|
|
20
|
+
.optional(),
|
|
21
|
+
})
|
|
22
|
+
.optional(),
|
|
23
|
+
});
|
|
@@ -16,6 +16,7 @@ import { TreeItemProvider } from "@mui/x-tree-view/TreeItemProvider";
|
|
|
16
16
|
import { useTreeItem } from "@mui/x-tree-view/useTreeItem";
|
|
17
17
|
import { useRef } from "react";
|
|
18
18
|
import { downloadFile, toSafeFilename } from "../../../utils/File.js";
|
|
19
|
+
import { removeTrailingUuid } from "../../AgentChat/Common/Utils.js";
|
|
19
20
|
// Palette of colors we can use for tags
|
|
20
21
|
const TAG_COLORS = [
|
|
21
22
|
"--bs-accent2-light",
|
|
@@ -63,7 +64,7 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, onDele
|
|
|
63
64
|
const expirationTime = item?.temporaryNetworkExpirationTime;
|
|
64
65
|
const isTemporaryNetwork = Boolean(expirationTime);
|
|
65
66
|
const isExpired = isChild && isTemporaryNetwork && isTemporaryNetworkExpired(expirationTime);
|
|
66
|
-
const
|
|
67
|
+
const networkDefinition = item?.temporaryNetworkDefinition ?? null;
|
|
67
68
|
const iconNameSuggestion = item.iconSuggestion;
|
|
68
69
|
let muiIconElement;
|
|
69
70
|
// If the item is a child (i.e., a network), we want to render an icon next to it.
|
|
@@ -105,10 +106,14 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, onDele
|
|
|
105
106
|
.map((tag) => (_jsx(Chip, { label: tag, style: {
|
|
106
107
|
margin: "0.25rem",
|
|
107
108
|
backgroundColor: `var(${tagsToColors.get(tag) || TAG_COLORS[0]})`,
|
|
108
|
-
} }, tag))), placement: "right", arrow: true, children: _jsx(BookmarkIcon, { sx: { fontSize: "0.75rem", color: "var(--bs-accent1-medium)" } }) })) : null] }), isChild && isTemporaryNetwork && (_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: "0.25rem", marginLeft: "auto" }, children: [
|
|
109
|
+
} }, tag))), placement: "right", arrow: true, children: _jsx(BookmarkIcon, { sx: { fontSize: "0.75rem", color: "var(--bs-accent1-medium)" } }) })) : null] }), isChild && isTemporaryNetwork && (_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: "0.25rem", marginLeft: "auto" }, children: [networkDefinition && (_jsx(Tooltip, { title: isExpired ? "Expired" : "Download network definition", children: _jsx("span", { children: _jsx(IconButton, { onClick: (e) => {
|
|
109
110
|
e.stopPropagation();
|
|
110
|
-
|
|
111
|
-
|
|
111
|
+
// Strip the reservation UUID before building the filename so
|
|
112
|
+
// exported files carry a clean name (toSafeFilename would
|
|
113
|
+
// otherwise flatten the UUID's hyphens to underscores).
|
|
114
|
+
const cleanName = removeTrailingUuid(labelString);
|
|
115
|
+
const fileName = `${toSafeFilename(cleanName)}.json`;
|
|
116
|
+
downloadFile(JSON.stringify(networkDefinition, null, 2), fileName, "application/json");
|
|
112
117
|
}, disabled: isExpired, "aria-label": "Download network definition", size: "small", sx: {
|
|
113
118
|
padding: 0,
|
|
114
119
|
color: "var(--bs-secondary)",
|