@cognizant-ai-lab/ui-common 1.8.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/README.md +3 -3
- package/dist/components/AgentChat/ChatCommon/ChatCommon.d.ts +9 -3
- package/dist/components/AgentChat/ChatCommon/ChatCommon.js +94 -27
- 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/ChatCommon/Thinking.js +2 -2
- package/dist/components/AgentChat/Common/Utils.d.ts +0 -7
- package/dist/components/AgentChat/Common/Utils.js +0 -12
- package/dist/components/Common/Breadcrumbs.js +1 -1
- package/dist/components/Common/Footer.js +4 -0
- 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} +99 -58
- package/dist/components/MultiAgentAccelerator/{AgentNode.d.ts → AgentFlow/AgentNode.d.ts} +1 -1
- package/dist/components/MultiAgentAccelerator/{AgentNode.js → AgentFlow/AgentNode.js} +11 -9
- package/dist/components/MultiAgentAccelerator/{GraphLayouts.d.ts → AgentFlow/GraphLayouts.d.ts} +4 -4
- package/dist/components/MultiAgentAccelerator/{GraphLayouts.js → AgentFlow/GraphLayouts.js} +15 -41
- 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 +74 -83
- 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.d.ts +2 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/AgentNetworkTreeItem.js +22 -25
- package/dist/components/MultiAgentAccelerator/Sidebar/Sidebar.js +16 -22
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.d.ts +1 -0
- package/dist/components/MultiAgentAccelerator/Sidebar/TreeBuilder.js +27 -7
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.d.ts +8 -0
- package/dist/components/MultiAgentAccelerator/TemporaryNetworks.js +61 -13
- 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 +7 -2
- package/dist/components/MultiAgentAccelerator/const.d.ts +11 -10
- package/dist/components/MultiAgentAccelerator/const.js +22 -20
- 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/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/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
|
@@ -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
|
};
|
|
@@ -26,64 +26,33 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
|
26
26
|
import { useJoyride } from "react-joyride";
|
|
27
27
|
import { extractConversations } from "./AgentConversations.js";
|
|
28
28
|
import { getUpdatedAgentCounts } from "./AgentCounts.js";
|
|
29
|
-
import { AgentFlow } from "./AgentFlow.js";
|
|
29
|
+
import { AgentFlow } from "./AgentFlow/AgentFlow.js";
|
|
30
30
|
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";
|
|
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";
|
|
32
32
|
import { Sidebar } from "./Sidebar/Sidebar.js";
|
|
33
|
-
import { extractTemporaryNetworksFromMessage, isTemporaryNetwork,
|
|
34
|
-
import {
|
|
33
|
+
import { extractTemporaryNetworksFromMessage, isTemporaryNetwork, notifySaveError, streamNetworkDesignerUpsert, } from "./TemporaryNetworks.js";
|
|
34
|
+
import { MAIN_TOUR_STEPS } from "./Tour/MainTourSteps.js";
|
|
35
|
+
import { getAgentFunction, getAgentNetworks, getConnectivity } from "../../controller/agent/Agent.js";
|
|
35
36
|
import { getAgentIconSuggestions, getNetworkIconSuggestions } from "../../controller/agent/IconSuggestions.js";
|
|
36
37
|
import { useAgentChatHistoryStore } from "../../state/ChatHistory.js";
|
|
37
|
-
import { useSettingsStore } from "../../state/Settings.js";
|
|
38
38
|
import { useTempNetworksStore } from "../../state/TemporaryNetworks.js";
|
|
39
39
|
import { TourPromptState, useTourStore } from "../../state/Tour.js";
|
|
40
|
+
import { toDisplayName } from "../../utils/AgentName.js";
|
|
40
41
|
import { getZIndex } from "../../utils/zIndexLayers.js";
|
|
41
42
|
import { ChatCommon } from "../AgentChat/ChatCommon/ChatCommon.js";
|
|
42
43
|
import { LlmChatButton } from "../AgentChat/Common/LlmChatButton.js";
|
|
43
44
|
import { isLegacyAgentType } from "../AgentChat/Common/Types.js";
|
|
44
|
-
import { chatMessageFromChunk
|
|
45
|
+
import { chatMessageFromChunk } from "../AgentChat/Common/Utils.js";
|
|
45
46
|
import { ConfirmationModal, StyledButton } from "../Common/ConfirmationModal.js";
|
|
46
|
-
import {
|
|
47
|
-
import { MAIN_TOUR_STEPS } from "./Tour/MainTourSteps.js";
|
|
47
|
+
import { MUIAlert } from "../Common/MUIAlert.js";
|
|
48
48
|
import { MUIDialog } from "../Common/MUIDialog.js";
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
import { closeNotification, NotificationType, sendNotification } from "../Common/notification.js";
|
|
50
|
+
import { BYOK } from "./Schema/SlyData.js";
|
|
51
|
+
import { getApiKey, LLM_PROVIDER_API_KEY_FIELD, useSettingsStore } from "../../state/Settings.js";
|
|
51
52
|
// Animation time for the left and right panels to slide in or out when launching the animation
|
|
52
53
|
const GROW_ANIMATION_TIME_MS = 800;
|
|
53
54
|
// Optimization to avoid creating a new empty map on every render
|
|
54
55
|
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
56
|
/**
|
|
88
57
|
* Main Multi-Agent Accelerator component that contains the sidebar, agent flow, and chat components.
|
|
89
58
|
* @param backendNeuroSanApiUrl Initial URL of the backend Neuro-San API. User can change this in the UI.
|
|
@@ -116,8 +85,9 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
116
85
|
const [agentIconSuggestions, setAgentIconSuggestions] = useState(null);
|
|
117
86
|
const [selectedNetwork, setSelectedNetwork] = useState(null);
|
|
118
87
|
const [networkDescription, setNetworkDescription] = useState("");
|
|
119
|
-
const networkDisplayName = useMemo(() => (
|
|
120
|
-
|
|
88
|
+
const networkDisplayName = useMemo(() => toDisplayName(selectedNetwork, useNativeNames), [selectedNetwork, useNativeNames]);
|
|
89
|
+
// LLM providers for which BYOK keys are supported by the current network
|
|
90
|
+
const [supportedByokProviders, setSupportedByokProviders] = useState(new Set());
|
|
121
91
|
const neuroSanURL = useSettingsStore((state) => state.settings.externalServices.neuroSanUrl) ?? defaultNeuroSanUrl;
|
|
122
92
|
// Tracks how many times each agent has been involved in the conversation
|
|
123
93
|
const [agentCounts, setAgentCounts] = useState(new Map());
|
|
@@ -191,9 +161,8 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
191
161
|
// progresses. We read it back here to find the matching temp network and override agent_network_definition in the
|
|
192
162
|
// outgoing request — leaving what's in IndexedDB untouched.
|
|
193
163
|
const designerSlyData = useAgentChatHistoryStore((state) => state.history[AGENT_NETWORK_DESIGNER_ID]?.slyData);
|
|
194
|
-
const
|
|
195
|
-
|
|
196
|
-
: undefined;
|
|
164
|
+
const designerNetworkNameValue = designerSlyData?.[AGENT_NETWORK_NAME_KEY];
|
|
165
|
+
const designerNetworkName = isNetworkDesignerMode && typeof designerNetworkNameValue === "string" ? designerNetworkNameValue : undefined;
|
|
197
166
|
const designerTempNetwork = designerNetworkName
|
|
198
167
|
? temporaryNetworks.find((n) => n.agentNetworkName === designerNetworkName)
|
|
199
168
|
: undefined;
|
|
@@ -201,16 +170,14 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
201
170
|
* Builds the API keys object to be sent as extraSlyData with each request, based on the LLM providers required
|
|
202
171
|
* by the currently selected network. Only includes keys for providers that are required.
|
|
203
172
|
*/
|
|
204
|
-
const getApiKeys = () => {
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
return { llm_config: llmConfig };
|
|
213
|
-
};
|
|
173
|
+
const getApiKeys = () => ({
|
|
174
|
+
llm_config: Object.fromEntries([...supportedByokProviders]
|
|
175
|
+
.map((provider) => [
|
|
176
|
+
LLM_PROVIDER_API_KEY_FIELD[provider],
|
|
177
|
+
getApiKey(apiKeys, provider),
|
|
178
|
+
])
|
|
179
|
+
.filter((entry) => Boolean(entry[1]))),
|
|
180
|
+
});
|
|
214
181
|
/**
|
|
215
182
|
* Builds the extraSlyData object to be sent with each request, including information for Agent Network Designer
|
|
216
183
|
* and (if required) API keys for LLM providers.
|
|
@@ -237,7 +204,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
237
204
|
return undefined;
|
|
238
205
|
};
|
|
239
206
|
// Whether any API keys are required
|
|
240
|
-
const anyApiKeysRequired =
|
|
207
|
+
const anyApiKeysRequired = supportedByokProviders.size > 0;
|
|
241
208
|
// Build base extraSlyData
|
|
242
209
|
const baseExtraSlyData = buildExtraSlyData();
|
|
243
210
|
// Add API keys to extraSlyData if needed, merging with baseExtraSlyData if it exists
|
|
@@ -252,7 +219,33 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
252
219
|
chatRef.current?.handleStop();
|
|
253
220
|
resetState();
|
|
254
221
|
}, [resetState]);
|
|
255
|
-
|
|
222
|
+
/**
|
|
223
|
+
* Maps an API key name to the corresponding LLM provider
|
|
224
|
+
*/
|
|
225
|
+
const apiKeyToProvider = useCallback((key) => {
|
|
226
|
+
switch (key) {
|
|
227
|
+
case "openai_api_key":
|
|
228
|
+
return "OpenAI";
|
|
229
|
+
case "anthropic_api_key":
|
|
230
|
+
return "Anthropic";
|
|
231
|
+
default:
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}, []);
|
|
235
|
+
/**
|
|
236
|
+
* Given a SlyData schema, returns the set of LLM providers that are supported by the agent network.
|
|
237
|
+
* @note Semantics are "oneOf": a key must be supplied for at least one of the named providers
|
|
238
|
+
*/
|
|
239
|
+
const getSupportedLlmProviders = useCallback((schema) => {
|
|
240
|
+
const parsed = BYOK.safeParse(schema);
|
|
241
|
+
if (!parsed.success || !parsed.data.required?.includes("llm_config")) {
|
|
242
|
+
return new Set();
|
|
243
|
+
}
|
|
244
|
+
const providerKeys = Object.keys(parsed.data.properties?.llm_config?.properties ?? {});
|
|
245
|
+
return new Set(providerKeys
|
|
246
|
+
.map((key) => apiKeyToProvider(key))
|
|
247
|
+
.filter((provider) => provider !== null));
|
|
248
|
+
}, [apiKeyToProvider]);
|
|
256
249
|
useEffect(() => {
|
|
257
250
|
;
|
|
258
251
|
(async () => {
|
|
@@ -273,23 +266,14 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
273
266
|
const fetchAgentDetails = async () => {
|
|
274
267
|
// It is a Neuro-san agent, so get the function and connectivity info
|
|
275
268
|
try {
|
|
269
|
+
// Reset before fetching
|
|
270
|
+
setNetworkDescription("");
|
|
271
|
+
setSupportedByokProviders(new Set());
|
|
272
|
+
// Get agent function from server
|
|
276
273
|
const agentFunction = await getAgentFunction(neuroSanURL, selectedNetwork, username);
|
|
277
274
|
setNetworkDescription(agentFunction?.function?.description || "");
|
|
278
275
|
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
|
-
})));
|
|
276
|
+
setSupportedByokProviders(getSupportedLlmProviders(schema));
|
|
293
277
|
}
|
|
294
278
|
catch (e) {
|
|
295
279
|
console.warn(`Unable to get agent details for network ${selectedNetwork}:`, e);
|
|
@@ -300,7 +284,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
300
284
|
if (selectedNetwork && !isLegacyAgentType(selectedNetwork)) {
|
|
301
285
|
void fetchAgentDetails();
|
|
302
286
|
}
|
|
303
|
-
}, [neuroSanURL, selectedNetwork, username]);
|
|
287
|
+
}, [getSupportedLlmProviders, neuroSanURL, selectedNetwork, username]);
|
|
304
288
|
useEffect(() => {
|
|
305
289
|
;
|
|
306
290
|
(async () => {
|
|
@@ -466,10 +450,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
466
450
|
*/
|
|
467
451
|
const onSaveAgent = useCallback(async (agentName, updated, agentNetworkName, signal) => {
|
|
468
452
|
try {
|
|
469
|
-
|
|
470
|
-
await sendNetworkDesignerUpdate(neuroSanURL, signal, agentName, updated, agentNetworkName, username, (chunk) => {
|
|
471
|
-
newNetworks = collectNetworksFromChunk(chunk, updated, newNetworks);
|
|
472
|
-
});
|
|
453
|
+
const newNetworks = await streamNetworkDesignerUpsert(neuroSanURL, signal, agentName, updated, agentNetworkName, username);
|
|
473
454
|
if (newNetworks.length === 0) {
|
|
474
455
|
sendNotification(NotificationType.error, `Failed to update network "${agentName}".`, "The network designer did not return a reservation. Please try again.");
|
|
475
456
|
return;
|
|
@@ -511,7 +492,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
511
492
|
}, [resetState]);
|
|
512
493
|
const handleDeleteNetwork = (networkId, isExpired) => {
|
|
513
494
|
if (isExpired) {
|
|
514
|
-
// It's expired so just delete it without confirmation
|
|
495
|
+
// It's expired, so just delete it without confirmation
|
|
515
496
|
const tempNetworksWithoutThisOne = temporaryNetworks.filter((network) => network.agentInfo.agent_name !== networkId);
|
|
516
497
|
useTempNetworksStore.getState().setTempNetworks(tempNetworksWithoutThisOne);
|
|
517
498
|
useAgentChatHistoryStore.getState().resetHistory(networkId);
|
|
@@ -555,8 +536,11 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
555
536
|
}
|
|
556
537
|
}, [tourRequested, selectedNetwork, agentsInNetwork, networks, controls, setTourStatus]);
|
|
557
538
|
const getMissingApiKeys = () => {
|
|
558
|
-
|
|
539
|
+
// Calculate intersection of what we have (apiKeys) and what is required (providerKeysRequired)
|
|
540
|
+
const intersection = new Set([...supportedByokProviders].filter((provider) => getApiKey(apiKeys, provider)));
|
|
541
|
+
return intersection.size === 0 ? supportedByokProviders : new Set();
|
|
559
542
|
};
|
|
543
|
+
const hasMissingApiKeys = getMissingApiKeys().size > 0;
|
|
560
544
|
const getLeftPanel = () => {
|
|
561
545
|
return (_jsx(Slide, { id: "multi-agent-accelerator-grid-sidebar-slide", in: !enableZenMode || !isAwaitingLlm, direction: "right", timeout: GROW_ANIMATION_TIME_MS, onExited: () => {
|
|
562
546
|
setIsStreaming(true);
|
|
@@ -586,7 +570,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
586
570
|
[AGENT_NETWORK_DESIGNER_ID]: "Describe in plain language the network you would like to build.",
|
|
587
571
|
}, currentUser: username, customAgentGreetings: {
|
|
588
572
|
[AGENT_NETWORK_DESIGNER_ID]: "Let's build a network together!",
|
|
589
|
-
}, extraSlyData: extraSlyData, id: "agent-network-ui", isAwaitingLlm: isAwaitingLlm,
|
|
573
|
+
}, 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
574
|
};
|
|
591
575
|
const getStopButton = () => {
|
|
592
576
|
return (_jsx(_Fragment, { children: isAwaitingLlm && enableZenMode && (_jsx(Box, { id: "stop-button-container", sx: {
|
|
@@ -600,7 +584,7 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
600
584
|
},
|
|
601
585
|
}, children: _jsx(StopCircle, { fontSize: "large", id: "stop-button-icon" }) }) }) })) }));
|
|
602
586
|
};
|
|
603
|
-
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. ` +
|
|
604
588
|
"This action cannot be undone. Are you sure you want to proceed?", handleCancel: () => {
|
|
605
589
|
setConfirmationModalOpen(false);
|
|
606
590
|
setNetworkToBeDeleted(null);
|
|
@@ -656,7 +640,14 @@ export const MultiAgentAccelerator = ({ defaultNeuroSanUrl, username, }) => {
|
|
|
656
640
|
opacity: "95%",
|
|
657
641
|
width: "100%",
|
|
658
642
|
}, 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
|
-
|
|
643
|
+
const getMissingApiKeysAlert = () => hasMissingApiKeys && (_jsx(MUIAlert, { closeable: false, id: "llm-chat-missing-api-keys-alert", severity: "warning", sx: {
|
|
644
|
+
my: 1.5,
|
|
645
|
+
"& .MuiAlert-message": {
|
|
646
|
+
width: "100%",
|
|
647
|
+
textAlign: "center",
|
|
648
|
+
},
|
|
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."] }) }));
|
|
650
|
+
return (_jsxs(_Fragment, { children: [getMissingApiKeysAlert(), Tour, getTourModal(), getProgressPopper(), getDeleteNetworkConfirmationModal(), _jsxs(Grid, { id: "multi-agent-accelerator-grid", container: true, columns: 18, sx: {
|
|
660
651
|
display: "flex",
|
|
661
652
|
flex: 1,
|
|
662
653
|
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
|
+
});
|
|
@@ -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;
|
|
@@ -9,13 +9,18 @@ import Edit from "@mui/icons-material/Edit";
|
|
|
9
9
|
import Box from "@mui/material/Box";
|
|
10
10
|
import Chip from "@mui/material/Chip";
|
|
11
11
|
import IconButton from "@mui/material/IconButton";
|
|
12
|
+
import { styled } from "@mui/material/styles";
|
|
12
13
|
import Tooltip from "@mui/material/Tooltip";
|
|
13
14
|
import { useTreeItemModel } from "@mui/x-tree-view/hooks";
|
|
14
15
|
import { TreeItemContent, TreeItemGroupTransition, TreeItemLabel, TreeItemRoot, } from "@mui/x-tree-view/TreeItem";
|
|
15
16
|
import { TreeItemProvider } from "@mui/x-tree-view/TreeItemProvider";
|
|
16
17
|
import { useTreeItem } from "@mui/x-tree-view/useTreeItem";
|
|
17
18
|
import { useRef } from "react";
|
|
18
|
-
import { downloadFile
|
|
19
|
+
import { downloadFile } from "../../../utils/File.js";
|
|
20
|
+
//#region Constants
|
|
21
|
+
// Accessible names shared between each action's tooltip title and its aria-label.
|
|
22
|
+
export const EDIT_NETWORK_LABEL = "Edit this network";
|
|
23
|
+
export const DOWNLOAD_NETWORK_DEFINITION_LABEL = "Download network definition";
|
|
19
24
|
// Palette of colors we can use for tags
|
|
20
25
|
const TAG_COLORS = [
|
|
21
26
|
"--bs-accent2-light",
|
|
@@ -28,6 +33,17 @@ const TAG_COLORS = [
|
|
|
28
33
|
"--bs-secondary",
|
|
29
34
|
"--bs-yellow",
|
|
30
35
|
];
|
|
36
|
+
// Shared styling for the row's action buttons (download, edit, ...). Disabled state keeps the base
|
|
37
|
+
// color and only dims via opacity.
|
|
38
|
+
const ActionIconButton = styled(IconButton)({
|
|
39
|
+
padding: 0,
|
|
40
|
+
color: "var(--bs-secondary)",
|
|
41
|
+
"&:hover": { color: "var(--bs-secondary-dark)" },
|
|
42
|
+
"&.Mui-disabled": {
|
|
43
|
+
opacity: 0.3,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
//#endregion Types and Interfaces
|
|
31
47
|
// Keep track of which tags have which colors so that the same tag always has the same color
|
|
32
48
|
const tagsToColors = new Map();
|
|
33
49
|
/**
|
|
@@ -45,8 +61,6 @@ const isTemporaryNetworkExpired = (expirationDate) => {
|
|
|
45
61
|
*/
|
|
46
62
|
export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, onDeleteNetwork, onEditNetwork, }) => {
|
|
47
63
|
const item = useTreeItemModel(itemId);
|
|
48
|
-
// We know all labels are strings because we set them that way in the tree view items
|
|
49
|
-
const labelString = label;
|
|
50
64
|
const { getContextProviderProps, getRootProps, getContentProps, getLabelProps, getGroupTransitionProps } = useTreeItem({ itemId, children, label, disabled });
|
|
51
65
|
const rootRef = useRef(null);
|
|
52
66
|
const isParent = Array.isArray(children) && children.length > 0;
|
|
@@ -105,37 +119,20 @@ export const AgentNetworkTreeItem = ({ children, disabled, itemId, label, onDele
|
|
|
105
119
|
.map((tag) => (_jsx(Chip, { label: tag, style: {
|
|
106
120
|
margin: "0.25rem",
|
|
107
121
|
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: [networkHocon && (_jsx(Tooltip, { title: isExpired ? "Expired" :
|
|
122
|
+
} }, tag))), placement: "right", arrow: true, children: _jsx(BookmarkIcon, { sx: { fontSize: "0.75rem", color: "var(--bs-accent1-medium)" } }) })) : null] }), isChild && isTemporaryNetwork && (_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: "0.25rem", marginLeft: "auto" }, children: [networkHocon && (_jsx(Tooltip, { title: isExpired ? "Expired" : DOWNLOAD_NETWORK_DEFINITION_LABEL, children: _jsx("span", { children: _jsx(ActionIconButton, { onClick: (e) => {
|
|
123
|
+
// The button is disabled while expired, so no need to guard here.
|
|
109
124
|
e.stopPropagation();
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
}, disabled: isExpired, "aria-label": "Download network definition", size: "small", sx: {
|
|
113
|
-
padding: 0,
|
|
114
|
-
color: "var(--bs-secondary)",
|
|
115
|
-
"&:hover": { color: "var(--bs-secondary-dark)" },
|
|
116
|
-
"&.Mui-disabled": {
|
|
117
|
-
color: "var(--bs-secondary)",
|
|
118
|
-
opacity: 0.3,
|
|
119
|
-
},
|
|
120
|
-
}, children: _jsx(DownloadIcon, { sx: { fontSize: "0.75rem" } }) }) }) })), _jsx(Tooltip, { title: "Edit this network", children: _jsx("span", { children: _jsx(IconButton, { onClick: (e) => {
|
|
125
|
+
downloadFile(networkHocon, item.downloadFileName, "text/plain");
|
|
126
|
+
}, disabled: isExpired, "aria-label": DOWNLOAD_NETWORK_DEFINITION_LABEL, size: "small", children: _jsx(DownloadIcon, { sx: { fontSize: "0.75rem" } }) }) }) })), _jsx(Tooltip, { title: EDIT_NETWORK_LABEL, children: _jsx("span", { children: _jsx(ActionIconButton, { onClick: (e) => {
|
|
121
127
|
e.stopPropagation();
|
|
122
128
|
onEditNetwork?.(itemId);
|
|
123
|
-
}, disabled: isExpired, size: "small", sx: {
|
|
124
|
-
padding: 0,
|
|
125
|
-
color: "var(--bs-secondary)",
|
|
126
|
-
"&:hover": { color: "var(--bs-secondary-dark)" },
|
|
127
|
-
"&.Mui-disabled": {
|
|
128
|
-
color: "var(--bs-secondary)",
|
|
129
|
-
opacity: 0.3,
|
|
130
|
-
},
|
|
131
|
-
}, children: _jsx(Edit, { sx: { fontSize: "0.75rem" } }) }) }) }), _jsx(Tooltip, { title: "Delete network", children: _jsx(Delete, { onClick: (e) => {
|
|
129
|
+
}, disabled: isExpired, "aria-label": EDIT_NETWORK_LABEL, size: "small", children: _jsx(Edit, { sx: { fontSize: "0.75rem" } }) }) }) }), _jsx(Tooltip, { title: "Delete network", children: _jsx(Delete, { onClick: (e) => {
|
|
132
130
|
e.stopPropagation();
|
|
133
131
|
onDeleteNetwork?.(itemId, isExpired);
|
|
134
132
|
}, sx: {
|
|
135
133
|
color: "var(--bs-secondary)",
|
|
136
134
|
"&:hover": { color: (theme) => theme.palette.warning.main },
|
|
137
135
|
"&.Mui-disabled": {
|
|
138
|
-
color: "var(--bs-secondary)",
|
|
139
136
|
opacity: 0.3,
|
|
140
137
|
},
|
|
141
138
|
cursor: "pointer",
|
|
@@ -120,26 +120,17 @@ export const Sidebar = ({ id, isAwaitingLlm, networkIconSuggestions, networks, n
|
|
|
120
120
|
const [selectedItem, setSelectedItem] = useState(null);
|
|
121
121
|
// Display option for agent/network names
|
|
122
122
|
const useNativeNames = useSettingsStore((state) => state.settings.appearance.useNativeNames);
|
|
123
|
-
const [neuroSanServerStatus, setNeuroSanServerStatus] = useState(
|
|
123
|
+
const [neuroSanServerStatus, setNeuroSanServerStatus] = useState(null);
|
|
124
124
|
useEffect(() => {
|
|
125
|
+
// Track whether the current request is still valid. If the component unmounts or the URL changes,
|
|
126
|
+
// we want to ignore any results from previous requests.
|
|
125
127
|
let isCurrentRequest = true;
|
|
126
128
|
const checkStatus = async () => {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
setNeuroSanServerStatus(result.success
|
|
133
|
-
? { onlineStatus: "online", version: result.version }
|
|
134
|
-
: { error: result.status, httpStatus: result.httpStatus, onlineStatus: "offline", version: null });
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
if (!isCurrentRequest) {
|
|
138
|
-
return;
|
|
139
|
-
}
|
|
140
|
-
const errorString = error instanceof Error ? error.message : String(error);
|
|
141
|
-
setNeuroSanServerStatus({ error: errorString, onlineStatus: "offline", version: null });
|
|
129
|
+
const result = await testConnection(neuroSanServerURL);
|
|
130
|
+
if (!isCurrentRequest) {
|
|
131
|
+
return;
|
|
142
132
|
}
|
|
133
|
+
setNeuroSanServerStatus(result);
|
|
143
134
|
};
|
|
144
135
|
void checkStatus();
|
|
145
136
|
const intervalId = setInterval(checkStatus, NEURO_SAN_PING_INTERVAL_MS);
|
|
@@ -207,25 +198,28 @@ export const Sidebar = ({ id, isAwaitingLlm, networkIconSuggestions, networks, n
|
|
|
207
198
|
};
|
|
208
199
|
}, [newlyAddedTemporaryNetworks]);
|
|
209
200
|
const toStatusColor = () => {
|
|
210
|
-
switch (neuroSanServerStatus
|
|
211
|
-
case
|
|
201
|
+
switch (neuroSanServerStatus?.success) {
|
|
202
|
+
case true:
|
|
212
203
|
return "green";
|
|
213
|
-
case
|
|
204
|
+
case false:
|
|
214
205
|
return "red";
|
|
215
|
-
case
|
|
206
|
+
case undefined:
|
|
216
207
|
default:
|
|
217
208
|
return "unknown";
|
|
218
209
|
}
|
|
219
210
|
};
|
|
211
|
+
const mapStatus = (statusText) => (statusText === "ok" ? "online" : (statusText ?? "unknown"));
|
|
220
212
|
const getStatusLight = () => (_jsx(ServerStatusTooltip, { slotProps: {
|
|
221
213
|
popper: {
|
|
222
214
|
"aria-label": "Neuro-san server status",
|
|
223
215
|
},
|
|
224
|
-
}, title: _jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 0.5 }, children: [_jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "Status:" }), " ", neuroSanServerStatus
|
|
216
|
+
}, title: _jsxs(Box, { sx: { display: "flex", flexDirection: "column", gap: 0.5 }, children: [_jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "Status:" }), " ", mapStatus(neuroSanServerStatus?.statusText)] }), _jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "URL:" }), " ", neuroSanServerURL || "unknown"] }), _jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "Service:" }), " ", neuroSanServerStatus?.healthCheckResponse?.service || "unknown"] }), _jsx(Typography, { sx: { fontWeight: "bold" }, variant: "body2", children: "Versions:" }), _jsxs(Box, { sx: { ml: 1 }, children: [_jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "Neuro SAN:" }), " ", neuroSanServerStatus?.healthCheckResponse?.versions?.["neuro-san"] ?? "unknown"] }), _jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "Neuro SAN Studio:" }), " ", neuroSanServerStatus?.healthCheckResponse?.versions?.["neuro-san-studio"] ?? "unknown"] })] }), !neuroSanServerStatus?.success && neuroSanServerStatus?.httpStatus && (_jsx(_Fragment, { children: _jsxs(Typography, { variant: "body2", children: [_jsx("strong", { children: "HTTP status:" }), " ", `${neuroSanServerStatus.httpStatus} (${httpStatus[neuroSanServerStatus.httpStatus] ?? "Unknown status"})`] }) }))] }), placement: "right", children: _jsx("span", { children: _jsx(StatusLight, { id: `${id}-agent-network-status-light`, statusValue: toStatusColor() }) }) }));
|
|
225
217
|
const getAddNetworkButton = () => (_jsx(Box, { sx: { display: "flex" }, children: _jsx(Button, { "aria-label": "Add New Network", disabled: isAwaitingLlm, id: "add-network-btn", onClick: () => {
|
|
226
218
|
setSelectedItem(AGENT_NETWORK_DESIGNER_ID);
|
|
227
219
|
setSelectedNetwork(AGENT_NETWORK_DESIGNER_ID);
|
|
228
|
-
}, 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:
|
|
220
|
+
}, 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: (theme) => ({
|
|
221
|
+
color: isAwaitingLlm ? theme.palette.action.disabled : "var(--bs-secondary)",
|
|
222
|
+
}) }) }) }) }));
|
|
229
223
|
const getSidebarHeading = () => (_jsxs(SidebarHeading, { id: `${id}-heading`, children: [_jsxs(Box, { sx: { display: "flex", alignItems: "center", gap: (theme) => theme.spacing(1.5) }, children: [getStatusLight(), "Agent Networks"] }), getAddNetworkButton()] }));
|
|
230
224
|
const getSidebarNetworksTree = () => (_jsx(RichTreeView, { disableSelection: isAwaitingLlm, expandedItems: expandedItems, items: treeViewItems, multiSelect: false, onExpandedItemsChange: (_event, itemIds) => setExpandedItems(itemIds), onSelectedItemsChange: handleSelectedItemsChange, selectedItems: selectedItem, slotProps: {
|
|
231
225
|
item: { onDeleteNetwork, onEditNetwork: handleEditNetworkWithSelect },
|
|
@@ -8,6 +8,7 @@ import { TemporaryNetwork } from "../../../state/TemporaryNetworks.js";
|
|
|
8
8
|
export interface AgentNetworkTreeItemModel extends Omit<TreeViewDefaultItemModelProperties, "children"> {
|
|
9
9
|
readonly children?: readonly AgentNetworkTreeItemModel[];
|
|
10
10
|
readonly displayName: string;
|
|
11
|
+
readonly downloadFileName?: string;
|
|
11
12
|
readonly iconSuggestion?: string;
|
|
12
13
|
readonly isNetwork: boolean;
|
|
13
14
|
readonly tags?: readonly string[];
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { cleanUpAgentName, removeTrailingUuid } from "
|
|
2
|
-
|
|
1
|
+
import { cleanUpAgentName, removeTrailingUuid, toDisplayName } from "../../../utils/AgentName.js";
|
|
2
|
+
import { toSafeFilename } from "../../../utils/File.js";
|
|
3
|
+
//#endregion Types and Interfaces
|
|
3
4
|
/**
|
|
4
5
|
* Recursively searches for a tree item with the specified ID within the given list of tree items.
|
|
5
6
|
* @param items - The list of tree items to search through
|
|
@@ -21,13 +22,30 @@ export const findTreeItemById = (items, itemId) => {
|
|
|
21
22
|
return undefined;
|
|
22
23
|
};
|
|
23
24
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
* @param itemName - The raw agent name from the API
|
|
25
|
+
* Computes the display name for a network (leaf) node.
|
|
26
|
+
* @param label - The label to use for the tree item (usually derived from the agent name)
|
|
27
27
|
* @param useNativeNames - Whether to use native names or cleaned-up names for display
|
|
28
|
+
* @param displayNameOverride - For temporary networks, `label` is the raw reservation_id, which is canonicalized and
|
|
29
|
+
* may have lost the word separators (_/-) that cleanUpAgentName relies on. When provided (the temp network's
|
|
30
|
+
* agentNetworkName), it is used as the basis for the cleaned name instead of the path part.
|
|
28
31
|
* @returns The display name to show in the tree view
|
|
29
32
|
*/
|
|
30
|
-
const
|
|
33
|
+
const toLeafDisplayName = (label, useNativeNames, displayNameOverride) => {
|
|
34
|
+
// Native mode shows the raw agent name part, unmodified.
|
|
35
|
+
if (useNativeNames) {
|
|
36
|
+
return label;
|
|
37
|
+
}
|
|
38
|
+
const cleanedName = cleanUpAgentName(displayNameOverride ?? removeTrailingUuid(label));
|
|
39
|
+
return cleanedName;
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Computes the filename used when exporting a network's HOCON to disk. The trailing reservation
|
|
43
|
+
* UUID is stripped first so the export carries a clean name (toSafeFilename would otherwise flatten
|
|
44
|
+
* the UUID's hyphens to underscores).
|
|
45
|
+
* @param label - The label to use for the tree item (usually derived from the agent name)
|
|
46
|
+
* @returns The sanitized `.hocon` filename for the exported network HOCON
|
|
47
|
+
*/
|
|
48
|
+
const toDownloadFileName = (label) => `${toSafeFilename(removeTrailingUuid(label))}.hocon`;
|
|
31
49
|
/**
|
|
32
50
|
* Converts an AgentInfo object into a tree item model representing a network (leaf node).
|
|
33
51
|
* @param network - The AgentInfo object containing details about the network
|
|
@@ -39,7 +57,8 @@ const toDisplayName = (itemName, useNativeNames) => useNativeNames ? itemName :
|
|
|
39
57
|
const toNetworkLeaf = (network, label, useNativeNames, metadata = {}) => ({
|
|
40
58
|
id: network.agent_name,
|
|
41
59
|
label,
|
|
42
|
-
displayName:
|
|
60
|
+
displayName: toLeafDisplayName(label, useNativeNames, metadata.displayNameOverride),
|
|
61
|
+
downloadFileName: metadata.temporaryNetworkHocon ? toDownloadFileName(label) : undefined,
|
|
43
62
|
iconSuggestion: metadata.iconSuggestion,
|
|
44
63
|
isNetwork: true,
|
|
45
64
|
tags: network.tags,
|
|
@@ -158,6 +177,7 @@ export const buildTreeViewItems = (useNativeNames, regularNetworks = [], tempora
|
|
|
158
177
|
iconSuggestion: "HourglassTop",
|
|
159
178
|
temporaryNetworkExpirationTime: new Date(temporaryNetwork.reservation.expiration_time_in_seconds * 1000),
|
|
160
179
|
temporaryNetworkHocon: temporaryNetwork.networkHocon,
|
|
180
|
+
displayNameOverride: temporaryNetwork.agentNetworkName,
|
|
161
181
|
});
|
|
162
182
|
}
|
|
163
183
|
const treeViewItems = tree.uncategorizedItems.length > 0
|