@cognizant-ai-lab/ui-common 1.3.3
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.d.ts +94 -0
- package/dist/components/AgentChat/ChatCommon.js +581 -0
- package/dist/components/AgentChat/ControlButtons.d.ts +16 -0
- package/dist/components/AgentChat/ControlButtons.js +24 -0
- package/dist/components/AgentChat/FormattedMarkdown.d.ts +32 -0
- package/dist/components/AgentChat/FormattedMarkdown.js +82 -0
- package/dist/components/AgentChat/Greetings.d.ts +1 -0
- package/dist/components/AgentChat/Greetings.js +38 -0
- package/dist/components/AgentChat/LlmChatButton.d.ts +12 -0
- package/dist/components/AgentChat/LlmChatButton.js +33 -0
- package/dist/components/AgentChat/SendButton.d.ts +12 -0
- package/dist/components/AgentChat/SendButton.js +28 -0
- package/dist/components/AgentChat/SyntaxHighlighterThemes.d.ts +14 -0
- package/dist/components/AgentChat/SyntaxHighlighterThemes.js +27 -0
- package/dist/components/AgentChat/Types.d.ts +17 -0
- package/dist/components/AgentChat/Types.js +26 -0
- package/dist/components/AgentChat/UserQueryDisplay.d.ts +5 -0
- package/dist/components/AgentChat/UserQueryDisplay.js +33 -0
- package/dist/components/AgentChat/Utils.d.ts +11 -0
- package/dist/components/AgentChat/Utils.js +64 -0
- package/dist/components/AgentChat/VoiceChat/MicrophoneButton.d.ts +29 -0
- package/dist/components/AgentChat/VoiceChat/MicrophoneButton.js +55 -0
- package/dist/components/AgentChat/VoiceChat/VoiceChat.d.ts +33 -0
- package/dist/components/AgentChat/VoiceChat/VoiceChat.js +180 -0
- package/dist/components/Authentication/Auth.d.ts +14 -0
- package/dist/components/Authentication/Auth.js +58 -0
- package/dist/components/ChatBot/ChatBot.d.ts +20 -0
- package/dist/components/ChatBot/ChatBot.js +75 -0
- package/dist/components/Common/Breadcrumbs.d.ts +6 -0
- package/dist/components/Common/Breadcrumbs.js +36 -0
- package/dist/components/Common/LlmChatOptionsButton.d.ts +9 -0
- package/dist/components/Common/LlmChatOptionsButton.js +31 -0
- package/dist/components/Common/LoadingSpinner.d.ts +10 -0
- package/dist/components/Common/LoadingSpinner.js +24 -0
- package/dist/components/Common/MUIAccordion.d.ts +17 -0
- package/dist/components/Common/MUIAccordion.js +76 -0
- package/dist/components/Common/MUIAlert.d.ts +11 -0
- package/dist/components/Common/MUIAlert.js +41 -0
- package/dist/components/Common/MUIDialog.d.ts +16 -0
- package/dist/components/Common/MUIDialog.js +40 -0
- package/dist/components/Common/Navbar.d.ts +15 -0
- package/dist/components/Common/Navbar.js +137 -0
- package/dist/components/Common/PageLoader.d.ts +5 -0
- package/dist/components/Common/PageLoader.js +26 -0
- package/dist/components/Common/Snackbar.d.ts +5 -0
- package/dist/components/Common/Snackbar.js +84 -0
- package/dist/components/Common/confirmationModal.d.ts +14 -0
- package/dist/components/Common/confirmationModal.js +65 -0
- package/dist/components/Common/notification.d.ts +18 -0
- package/dist/components/Common/notification.js +79 -0
- package/dist/components/ErrorPage/ErrorBoundary.d.ts +38 -0
- package/dist/components/ErrorPage/ErrorBoundary.js +77 -0
- package/dist/components/ErrorPage/ErrorPage.d.ts +12 -0
- package/dist/components/ErrorPage/ErrorPage.js +46 -0
- package/dist/components/MultiAgentAccelerator/AgentFlow.d.ts +21 -0
- package/dist/components/MultiAgentAccelerator/AgentFlow.js +394 -0
- package/dist/components/MultiAgentAccelerator/AgentNode.d.ts +18 -0
- package/dist/components/MultiAgentAccelerator/AgentNode.js +129 -0
- package/dist/components/MultiAgentAccelerator/GraphLayouts.d.ts +33 -0
- package/dist/components/MultiAgentAccelerator/GraphLayouts.js +297 -0
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.d.ts +17 -0
- package/dist/components/MultiAgentAccelerator/MultiAgentAccelerator.js +208 -0
- package/dist/components/MultiAgentAccelerator/PlasmaEdge.d.ts +3 -0
- package/dist/components/MultiAgentAccelerator/PlasmaEdge.js +124 -0
- package/dist/components/MultiAgentAccelerator/Sidebar.d.ts +12 -0
- package/dist/components/MultiAgentAccelerator/Sidebar.js +204 -0
- package/dist/components/MultiAgentAccelerator/ThoughtBubbleEdge.d.ts +12 -0
- package/dist/components/MultiAgentAccelerator/ThoughtBubbleEdge.js +15 -0
- package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.d.ts +11 -0
- package/dist/components/MultiAgentAccelerator/ThoughtBubbleOverlay.js +466 -0
- package/dist/components/MultiAgentAccelerator/const.d.ts +7 -0
- package/dist/components/MultiAgentAccelerator/const.js +39 -0
- package/dist/const.d.ts +10 -0
- package/dist/const.js +30 -0
- package/dist/controller/agent/Agent.d.ts +56 -0
- package/dist/controller/agent/Agent.js +162 -0
- package/dist/controller/llm/LlmChat.d.ts +18 -0
- package/dist/controller/llm/LlmChat.js +65 -0
- package/dist/controller/llm/endpoints.d.ts +1 -0
- package/dist/controller/llm/endpoints.js +17 -0
- package/dist/generated/neuro-san/NeuroSanClient.d.ts +413 -0
- package/dist/generated/neuro-san/NeuroSanClient.js +28 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +52 -0
- package/dist/state/UserInfo.d.ts +16 -0
- package/dist/state/UserInfo.js +27 -0
- package/dist/state/environment.d.ts +18 -0
- package/dist/state/environment.js +33 -0
- package/dist/tsconfig.build.tsbuildinfo +1 -0
- package/dist/utils/Authentication.d.ts +31 -0
- package/dist/utils/Authentication.js +94 -0
- package/dist/utils/BrowserNavigation.d.ts +5 -0
- package/dist/utils/BrowserNavigation.js +22 -0
- package/dist/utils/Theme.d.ts +7 -0
- package/dist/utils/Theme.js +7 -0
- package/dist/utils/agentConversations.d.ts +24 -0
- package/dist/utils/agentConversations.js +113 -0
- package/dist/utils/text.d.ts +28 -0
- package/dist/utils/text.js +64 -0
- package/dist/utils/title.d.ts +1 -0
- package/dist/utils/title.js +20 -0
- package/dist/utils/types.d.ts +17 -0
- package/dist/utils/types.js +16 -0
- package/dist/utils/useLocalStorage.d.ts +1 -0
- package/dist/utils/useLocalStorage.js +55 -0
- package/dist/utils/zIndexLayers.d.ts +2 -0
- package/dist/utils/zIndexLayers.js +29 -0
- package/package.json +69 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { styled } from "@mui/material";
|
|
3
|
+
import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
4
|
+
import { ChatMessageType } from "../../generated/neuro-san/NeuroSanClient.js";
|
|
5
|
+
// Note: Removed BubblePosition interface - no longer needed for right-side positioning
|
|
6
|
+
// #endregion: Types
|
|
7
|
+
// #region: Constants
|
|
8
|
+
const BUBBLE_DISTANCE_FROM_RIGHT_EDGE = 20; // Fixed distance from right edge
|
|
9
|
+
const BUBBLE_HEIGHT = 78;
|
|
10
|
+
const BUBBLE_HEIGHT_PLUS_SPACING = BUBBLE_HEIGHT + 10;
|
|
11
|
+
const BUBBLE_STACK_OFFSET_TOP = 70;
|
|
12
|
+
const BUBBLE_WIDTH = 260;
|
|
13
|
+
const LAYOUT_BUBBLES_ANIMATION_DELAY_MS = 120; // Delay between each bubble's animation start
|
|
14
|
+
// Constants for connecting lines
|
|
15
|
+
const CONNECTING_LINE_OPACITY = 0.3; // Semi-transparent connecting line
|
|
16
|
+
// #endregion: Constants
|
|
17
|
+
// #region: Styled Components
|
|
18
|
+
const OverlayContainer = styled("div")({
|
|
19
|
+
position: "absolute",
|
|
20
|
+
left: 0,
|
|
21
|
+
top: 0,
|
|
22
|
+
width: "100%",
|
|
23
|
+
height: "100%",
|
|
24
|
+
pointerEvents: "none",
|
|
25
|
+
zIndex: 10000,
|
|
26
|
+
});
|
|
27
|
+
const ThoughtBubble = styled("div", {
|
|
28
|
+
shouldForwardProp: (prop) => !["isHovered", "isTruncated", "animationDelay", "bubbleIndex", "isVisible", "isExiting"].includes(prop),
|
|
29
|
+
})(({ theme, isHovered, isTruncated, animationDelay, bubbleIndex, isVisible = true, isExiting = false }) => ({
|
|
30
|
+
// Colors / theme
|
|
31
|
+
// TODO: Add dark mode support? For now both light and dark mode use the same bubble style and look fine.
|
|
32
|
+
background: "linear-gradient(135deg, rgba(255,255,255,0.98) 0%, rgba(250,250,250,0.95) 100%)",
|
|
33
|
+
border: "var(--bs-border-width) var(--bs-border-style) var(--bs-border-color)",
|
|
34
|
+
borderRadius: "var(--bs-border-radius-lg)",
|
|
35
|
+
color: "var(--bs-primary)",
|
|
36
|
+
fontFamily: theme.typography.fontFamily, // TODO: Easy to pull from theme. Rest we need to revisit.
|
|
37
|
+
fontSize: "var(--bs-body-font-size-extra-small)",
|
|
38
|
+
fontWeight: "var(--bs-body-font-weight)",
|
|
39
|
+
padding: "10px 14px",
|
|
40
|
+
// Positioning - restore original right-side layout
|
|
41
|
+
position: "absolute",
|
|
42
|
+
right: BUBBLE_DISTANCE_FROM_RIGHT_EDGE,
|
|
43
|
+
top: BUBBLE_STACK_OFFSET_TOP + bubbleIndex * BUBBLE_HEIGHT_PLUS_SPACING, // Stack vertically with spacing
|
|
44
|
+
transform: "none",
|
|
45
|
+
// Dimensions
|
|
46
|
+
// Only expand height when hovered AND text is truncated
|
|
47
|
+
height: isHovered && isTruncated ? BUBBLE_HEIGHT : "auto",
|
|
48
|
+
maxHeight: BUBBLE_HEIGHT, // Max 3 lines always
|
|
49
|
+
minHeight: "auto", // Let height adjust to content
|
|
50
|
+
minWidth: "100px",
|
|
51
|
+
width: BUBBLE_WIDTH,
|
|
52
|
+
// Other styles
|
|
53
|
+
boxShadow: isHovered
|
|
54
|
+
? "0 4px 20px rgba(0,0,0,0.12), 0 2px 6px rgba(0,0,0,0.08)"
|
|
55
|
+
: "0 2px 12px rgba(0,0,0,0.06), 0 1px 3px rgba(0,0,0,0.08)",
|
|
56
|
+
zIndex: isHovered ? 10002 : 10000,
|
|
57
|
+
lineHeight: 1.4,
|
|
58
|
+
backdropFilter: "blur(12px)",
|
|
59
|
+
WebkitBackdropFilter: "blur(12px)",
|
|
60
|
+
transition: `box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1),
|
|
61
|
+
z-index 0.15s cubic-bezier(0.4, 0, 0.2, 1),
|
|
62
|
+
transform 0.15s cubic-bezier(0.4, 0, 0.2, 1)`,
|
|
63
|
+
cursor: isTruncated ? "pointer" : "default",
|
|
64
|
+
userSelect: isHovered && isTruncated ? "text" : "none",
|
|
65
|
+
animation: isExiting
|
|
66
|
+
? "fadeOutDown 0.4s cubic-bezier(0.4, 0, 0.1, 1) both"
|
|
67
|
+
: `fadeInUp 0.6s cubic-bezier(0.2, 0, 0.2, 1) ${animationDelay}ms both`,
|
|
68
|
+
opacity: isVisible ? (isExiting ? 0 : 1) : 0,
|
|
69
|
+
pointerEvents: "auto",
|
|
70
|
+
wordBreak: "break-word",
|
|
71
|
+
overflow: "hidden", // Always hide overflow
|
|
72
|
+
// Enable vertical scrolling only when hovered and truncated
|
|
73
|
+
overflowY: isHovered && isTruncated ? "auto" : "hidden",
|
|
74
|
+
whiteSpace: "normal",
|
|
75
|
+
}));
|
|
76
|
+
const TruncatedText = styled("div")(({ isHovered, isTruncated }) => ({
|
|
77
|
+
display: isHovered && isTruncated ? "block" : "-webkit-box",
|
|
78
|
+
WebkitLineClamp: isHovered && isTruncated ? "unset" : 3,
|
|
79
|
+
WebkitBoxOrient: isHovered && isTruncated ? "unset" : "vertical",
|
|
80
|
+
overflow: "hidden",
|
|
81
|
+
textOverflow: "ellipsis",
|
|
82
|
+
}));
|
|
83
|
+
// #endregion: Styled Components
|
|
84
|
+
export const ThoughtBubbleOverlay = ({ nodes, edges, showThoughtBubbles = true, isStreaming = false, onBubbleHoverChange, }) => {
|
|
85
|
+
// hoveredBubbleId: id of currently hovered bubble (or null)
|
|
86
|
+
const [hoveredBubbleId, setHoveredBubbleId] = useState(null);
|
|
87
|
+
// truncatedBubbles: set of edge ids whose text overflows the collapsed box
|
|
88
|
+
const [truncatedBubbles, setTruncatedBubbles] = useState(new Set());
|
|
89
|
+
// bubbleStates: track animation state of each bubble and when it's entered (used to delay line rendering
|
|
90
|
+
// until the bubble's entrance animation delay has passed)
|
|
91
|
+
const [bubbleStates, setBubbleStates] = useState(new Map());
|
|
92
|
+
// hoverTimeoutRef: used to debounce clearing of hovered state on mouse leave
|
|
93
|
+
const hoverTimeoutRef = useRef(null);
|
|
94
|
+
// textRefs: mapping of edge id -> DOM node for measuring scrollHeight/clientHeight
|
|
95
|
+
const textRefs = useRef(new Map());
|
|
96
|
+
// animationTimeouts: track timeouts for bubble removal
|
|
97
|
+
const animationTimeouts = useRef(new Map());
|
|
98
|
+
// Refs for SVG lines to update without re-rendering
|
|
99
|
+
const lineRefs = useRef(new Map());
|
|
100
|
+
// Ref to the overlay container so we can observe layout changes more precisely
|
|
101
|
+
const overlayRef = useRef(null);
|
|
102
|
+
// rAF ref for scheduling post-paint updates
|
|
103
|
+
const rafRef = useRef(null);
|
|
104
|
+
const mountedRef = useRef(true);
|
|
105
|
+
// Filter edges with meaningful text (memoized to prevent infinite re-renders)
|
|
106
|
+
const thoughtBubbleEdges = useMemo(() => edges.filter((e) => {
|
|
107
|
+
if (typeof e?.data?.text !== "string") {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
return e.data.text;
|
|
111
|
+
}), [edges]);
|
|
112
|
+
// Find frontman node (depth === 0, similar to isFrontman logic in AgentNode.tsx)
|
|
113
|
+
const frontmanNode = useMemo(() => {
|
|
114
|
+
if (!nodes || !Array.isArray(nodes) || nodes.length === 0)
|
|
115
|
+
return null;
|
|
116
|
+
return nodes.find((n) => n.data?.depth === 0);
|
|
117
|
+
}, [nodes]);
|
|
118
|
+
// Handle bubble lifecycle (appear/disappear animations)
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
const currentEdgeIds = new Set(thoughtBubbleEdges.map((e) => e.id));
|
|
121
|
+
const previousBubbleIds = new Set(bubbleStates.keys());
|
|
122
|
+
// Find new bubbles that should appear
|
|
123
|
+
const newBubbles = thoughtBubbleEdges.filter((e) => !previousBubbleIds.has(e.id));
|
|
124
|
+
// Find bubbles that should disappear
|
|
125
|
+
const removingBubbles = Array.from(previousBubbleIds).filter((id) => !currentEdgeIds.has(id));
|
|
126
|
+
setBubbleStates((prev) => {
|
|
127
|
+
const newState = new Map(prev);
|
|
128
|
+
// Add new bubbles in entering state. Record when they entered so we can delay showing
|
|
129
|
+
// connecting lines until the bubble's entrance animation delay.
|
|
130
|
+
const now = Date.now();
|
|
131
|
+
newBubbles.forEach((edge) => {
|
|
132
|
+
newState.set(edge.id, { isVisible: true, isExiting: false, enteredAt: now });
|
|
133
|
+
});
|
|
134
|
+
// Mark removing bubbles as exiting
|
|
135
|
+
removingBubbles.forEach((id) => {
|
|
136
|
+
const currentState = newState.get(id);
|
|
137
|
+
if (currentState) {
|
|
138
|
+
newState.set(id, { ...currentState, isExiting: true });
|
|
139
|
+
// Clear any existing timeout
|
|
140
|
+
const existingTimeout = animationTimeouts.current.get(id);
|
|
141
|
+
if (existingTimeout) {
|
|
142
|
+
clearTimeout(existingTimeout);
|
|
143
|
+
}
|
|
144
|
+
// Schedule removal after exit animation
|
|
145
|
+
const timeout = window.setTimeout(() => {
|
|
146
|
+
setBubbleStates((s) => {
|
|
147
|
+
const updatedState = new Map(s);
|
|
148
|
+
updatedState.delete(id);
|
|
149
|
+
return updatedState;
|
|
150
|
+
});
|
|
151
|
+
animationTimeouts.current.delete(id);
|
|
152
|
+
}, 400); // Match exit animation duration (0.4s)
|
|
153
|
+
animationTimeouts.current.set(id, timeout);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
return newState;
|
|
157
|
+
});
|
|
158
|
+
}, [thoughtBubbleEdges]);
|
|
159
|
+
// Cleanup timeouts on unmount
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
return () => {
|
|
162
|
+
animationTimeouts.current.forEach((timeout) => clearTimeout(timeout));
|
|
163
|
+
animationTimeouts.current.clear();
|
|
164
|
+
};
|
|
165
|
+
}, []);
|
|
166
|
+
// Sort edges to prioritize frontman's edges first
|
|
167
|
+
const sortedEdges = useMemo(() => {
|
|
168
|
+
if (!frontmanNode)
|
|
169
|
+
return thoughtBubbleEdges;
|
|
170
|
+
const frontmanEdges = thoughtBubbleEdges.filter((e) => e.source === frontmanNode.id || e.target === frontmanNode.id);
|
|
171
|
+
const otherEdges = thoughtBubbleEdges.filter((e) => e.source !== frontmanNode.id && e.target !== frontmanNode.id);
|
|
172
|
+
return [...frontmanEdges, ...otherEdges];
|
|
173
|
+
}, [thoughtBubbleEdges, frontmanNode]);
|
|
174
|
+
// Determine which agents are currently "active" using the same logic as AgentNode.
|
|
175
|
+
// An agent is active if any current conversation includes that agent's id.
|
|
176
|
+
const activeAgentIds = useMemo(() => {
|
|
177
|
+
const set = new Set();
|
|
178
|
+
if (!nodes || !Array.isArray(nodes))
|
|
179
|
+
return set;
|
|
180
|
+
for (const node of nodes) {
|
|
181
|
+
const getConversations = node.data?.getConversations;
|
|
182
|
+
if (typeof getConversations === "function") {
|
|
183
|
+
const convs = getConversations();
|
|
184
|
+
if (Array.isArray(convs)) {
|
|
185
|
+
const hasSelf = convs.some((conv) => Boolean(conv?.agents?.has?.(node.id)));
|
|
186
|
+
if (hasSelf) {
|
|
187
|
+
set.add(node.id);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return set;
|
|
193
|
+
}, [nodes]);
|
|
194
|
+
// Check truncation after render, but only when nothing is hovered
|
|
195
|
+
// (to avoid measuring expanded bubbles)
|
|
196
|
+
useEffect(() => {
|
|
197
|
+
// Skip truncation check if any bubble is hovered
|
|
198
|
+
if (hoveredBubbleId !== null)
|
|
199
|
+
return;
|
|
200
|
+
const newTruncated = new Set();
|
|
201
|
+
textRefs.current.forEach((element, edgeId) => {
|
|
202
|
+
// If scrollHeight > clientHeight then the content overflows (truncated)
|
|
203
|
+
if (element && element.scrollHeight > element.clientHeight) {
|
|
204
|
+
newTruncated.add(edgeId);
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
setTruncatedBubbles((prev) => {
|
|
208
|
+
// Only update if something changed
|
|
209
|
+
if (prev.size !== newTruncated.size)
|
|
210
|
+
return newTruncated;
|
|
211
|
+
// Check if the contents are the same
|
|
212
|
+
for (const id of newTruncated) {
|
|
213
|
+
if (!prev.has(id))
|
|
214
|
+
return newTruncated;
|
|
215
|
+
}
|
|
216
|
+
return prev;
|
|
217
|
+
});
|
|
218
|
+
}, [hoveredBubbleId, sortedEdges, textRefs]); // Re-check when edges change or hover state changes
|
|
219
|
+
// Notify parent when hover state changes
|
|
220
|
+
const handleHoverChange = useCallback((bubbleId) => {
|
|
221
|
+
// Clear any pending timeout
|
|
222
|
+
if (hoverTimeoutRef.current) {
|
|
223
|
+
clearTimeout(hoverTimeoutRef.current);
|
|
224
|
+
hoverTimeoutRef.current = null;
|
|
225
|
+
}
|
|
226
|
+
if (bubbleId === null) {
|
|
227
|
+
// Delay clearing the hover state when mouse leaves to prevent accidental unhover
|
|
228
|
+
// "window." to satisfy typescript
|
|
229
|
+
hoverTimeoutRef.current = window.setTimeout(() => {
|
|
230
|
+
setHoveredBubbleId(null);
|
|
231
|
+
if (onBubbleHoverChange) {
|
|
232
|
+
onBubbleHoverChange(null);
|
|
233
|
+
}
|
|
234
|
+
}, 200); // 200ms delay before clearing hover
|
|
235
|
+
}
|
|
236
|
+
else {
|
|
237
|
+
// Immediately set hover when mouse enters
|
|
238
|
+
setHoveredBubbleId(bubbleId);
|
|
239
|
+
if (onBubbleHoverChange) {
|
|
240
|
+
onBubbleHoverChange(bubbleId);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
}, [onBubbleHoverChange]);
|
|
244
|
+
// Calculate line coordinates - measurement only. Can be called from rAF/update loop.
|
|
245
|
+
const calculateLineCoordinates = useCallback((edge, bubbleIndex, agentRectCache) => {
|
|
246
|
+
// Skip HUMAN conversation types - no lines for human bubbles
|
|
247
|
+
if (edge.data?.type === ChatMessageType.HUMAN) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
// Get actual bubble DOM position (fresh every time)
|
|
251
|
+
const bubbleElement = document.querySelector(`[data-bubble-id="${CSS.escape(edge.id)}"]`);
|
|
252
|
+
let bubbleX;
|
|
253
|
+
let bubbleY;
|
|
254
|
+
if (bubbleElement) {
|
|
255
|
+
const bubbleRect = bubbleElement.getBoundingClientRect();
|
|
256
|
+
// Use the left edge center of the bubble (where line should start)
|
|
257
|
+
bubbleX = Math.round(bubbleRect.left);
|
|
258
|
+
bubbleY = Math.round(bubbleRect.top + bubbleRect.height / 2);
|
|
259
|
+
}
|
|
260
|
+
else {
|
|
261
|
+
// Fallback: calculate approximate viewport position
|
|
262
|
+
bubbleX = window.innerWidth - BUBBLE_DISTANCE_FROM_RIGHT_EDGE - BUBBLE_WIDTH;
|
|
263
|
+
bubbleY = BUBBLE_STACK_OFFSET_TOP + bubbleIndex * BUBBLE_HEIGHT_PLUS_SPACING + BUBBLE_HEIGHT / 2;
|
|
264
|
+
}
|
|
265
|
+
// Determine which agents to point to. If the edge supplies an `agents` array in
|
|
266
|
+
// data (provided by AgentFlow), use that. Otherwise fallback to the explicit
|
|
267
|
+
// edge.target/edge.source pair (single target).
|
|
268
|
+
let agentIds = Array.isArray(edge.data?.agents)
|
|
269
|
+
? edge.data?.agents
|
|
270
|
+
: [edge.target || edge.source].filter(Boolean);
|
|
271
|
+
if (agentIds.length === 0)
|
|
272
|
+
return null;
|
|
273
|
+
// Filter out any agents that are not currently active (we only draw lines to active agents).
|
|
274
|
+
// Always apply filtering based on the activeAgentIds set.
|
|
275
|
+
agentIds = agentIds.filter((id) => activeAgentIds.has(id));
|
|
276
|
+
if (agentIds.length === 0)
|
|
277
|
+
return null;
|
|
278
|
+
// For each agent id, find its visual element and calculate mid-point.
|
|
279
|
+
const results = [];
|
|
280
|
+
for (const agentId of agentIds) {
|
|
281
|
+
// Find the agent element by its data-id attribute
|
|
282
|
+
const agentElements = document.querySelectorAll(`[data-id="${CSS.escape(agentId)}"].react-flow__node`);
|
|
283
|
+
const foundAgentEl = agentElements?.[0] || null;
|
|
284
|
+
let agentX = 0;
|
|
285
|
+
let agentY = 0;
|
|
286
|
+
if (foundAgentEl) {
|
|
287
|
+
// Prefer the cached rect when present; otherwise compute and cache it.
|
|
288
|
+
const cachedRect = agentRectCache?.get(agentId);
|
|
289
|
+
const containerRect = cachedRect ?? foundAgentEl.getBoundingClientRect();
|
|
290
|
+
if (cachedRect == null) {
|
|
291
|
+
agentRectCache?.set(agentId, containerRect);
|
|
292
|
+
}
|
|
293
|
+
if (containerRect) {
|
|
294
|
+
agentX = Math.round(containerRect.left + containerRect.width / 2);
|
|
295
|
+
agentY = Math.round(containerRect.top + containerRect.height / 2);
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
results.push({ x1: bubbleX, y1: bubbleY, x2: agentX, y2: agentY, targetAgent: agentId });
|
|
299
|
+
}
|
|
300
|
+
return results;
|
|
301
|
+
}, [activeAgentIds]);
|
|
302
|
+
// Get all bubbles to render (including exiting ones)
|
|
303
|
+
const allBubbleIds = Array.from(bubbleStates.keys());
|
|
304
|
+
// Memoize the resolved edges so updateAllLines (and effects depending on it)
|
|
305
|
+
// doesn't get recreated on every render unnecessarily.
|
|
306
|
+
const renderableBubbles = useMemo(() => allBubbleIds
|
|
307
|
+
.map((id) => sortedEdges.find((e) => e.id === id) ?? edges.find((e) => e.id === id))
|
|
308
|
+
.filter((edge) => edge !== undefined), [allBubbleIds, sortedEdges, edges]);
|
|
309
|
+
// Update all SVG lines imperatively after paint. This reads DOM (getBoundingClientRect)
|
|
310
|
+
// and writes attributes on existing <line> elements stored in `lineRefs`.
|
|
311
|
+
const updateAllLines = useCallback(() => {
|
|
312
|
+
try {
|
|
313
|
+
renderableBubbles.forEach((edge, index) => {
|
|
314
|
+
const bubbleState = bubbleStates.get(edge.id) || { isVisible: true, isExiting: false, enteredAt: 0 };
|
|
315
|
+
if (!bubbleState.isVisible || bubbleState.isExiting)
|
|
316
|
+
return;
|
|
317
|
+
// Respect the entrance animation delay gating (lines appear only after bubble start)
|
|
318
|
+
const elapsed = Date.now() - (bubbleState?.enteredAt ?? 0);
|
|
319
|
+
const animationDelay = index * LAYOUT_BUBBLES_ANIMATION_DELAY_MS;
|
|
320
|
+
if (elapsed < animationDelay)
|
|
321
|
+
return;
|
|
322
|
+
const coordsArray = calculateLineCoordinates(edge, index);
|
|
323
|
+
if (!coordsArray || coordsArray.length === 0)
|
|
324
|
+
return;
|
|
325
|
+
for (const coords of coordsArray) {
|
|
326
|
+
const lineKey = `${edge.id}-${coords.targetAgent}`;
|
|
327
|
+
const lineEl = lineRefs.current.get(lineKey);
|
|
328
|
+
if (lineEl) {
|
|
329
|
+
// Update attributes imperatively
|
|
330
|
+
lineEl.setAttribute("x1", String(coords.x1));
|
|
331
|
+
lineEl.setAttribute("y1", String(coords.y1));
|
|
332
|
+
lineEl.setAttribute("x2", String(coords.x2));
|
|
333
|
+
lineEl.setAttribute("y2", String(coords.y2));
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
catch (err) {
|
|
339
|
+
// Guard against unexpected DOM errors during measurement
|
|
340
|
+
// Do not throw to avoid breaking the app
|
|
341
|
+
console.debug("ThoughtBubbleOverlay: updateAllLines error", err);
|
|
342
|
+
}
|
|
343
|
+
}, [renderableBubbles, bubbleStates, calculateLineCoordinates]);
|
|
344
|
+
// Schedule post-paint updates with rAF and ResizeObserver. Also optionally run a
|
|
345
|
+
// continuous loop while `isStreaming` is true to keep lines in sync during streaming.
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
mountedRef.current = true;
|
|
348
|
+
const schedule = () => {
|
|
349
|
+
if (rafRef.current != null)
|
|
350
|
+
return;
|
|
351
|
+
rafRef.current = requestAnimationFrame(() => {
|
|
352
|
+
rafRef.current = null;
|
|
353
|
+
if (!mountedRef.current)
|
|
354
|
+
return;
|
|
355
|
+
updateAllLines();
|
|
356
|
+
});
|
|
357
|
+
};
|
|
358
|
+
// Initial schedule
|
|
359
|
+
schedule();
|
|
360
|
+
// Resize observer to detect layout/size changes
|
|
361
|
+
const ro = new ResizeObserver(() => schedule());
|
|
362
|
+
try {
|
|
363
|
+
if (overlayRef.current)
|
|
364
|
+
ro.observe(overlayRef.current);
|
|
365
|
+
else
|
|
366
|
+
ro.observe(document.body);
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
// Ignore if RO observation fails in some test environments
|
|
370
|
+
}
|
|
371
|
+
// Window resize
|
|
372
|
+
window.addEventListener("resize", schedule);
|
|
373
|
+
// If streaming, run a continuous rAF loop to keep up with frequent layout updates
|
|
374
|
+
let streamingRaf = null;
|
|
375
|
+
const startStreamingLoop = () => {
|
|
376
|
+
if (streamingRaf != null)
|
|
377
|
+
return;
|
|
378
|
+
const loop = () => {
|
|
379
|
+
updateAllLines();
|
|
380
|
+
streamingRaf = requestAnimationFrame(loop);
|
|
381
|
+
};
|
|
382
|
+
streamingRaf = requestAnimationFrame(loop);
|
|
383
|
+
};
|
|
384
|
+
const stopStreamingLoop = () => {
|
|
385
|
+
if (streamingRaf != null) {
|
|
386
|
+
cancelAnimationFrame(streamingRaf);
|
|
387
|
+
streamingRaf = null;
|
|
388
|
+
}
|
|
389
|
+
};
|
|
390
|
+
if (isStreaming)
|
|
391
|
+
startStreamingLoop();
|
|
392
|
+
return () => {
|
|
393
|
+
mountedRef.current = false;
|
|
394
|
+
if (rafRef.current != null)
|
|
395
|
+
cancelAnimationFrame(rafRef.current);
|
|
396
|
+
ro.disconnect();
|
|
397
|
+
window.removeEventListener("resize", schedule);
|
|
398
|
+
stopStreamingLoop();
|
|
399
|
+
};
|
|
400
|
+
}, [updateAllLines, isStreaming]);
|
|
401
|
+
// Don't render anything if thought bubbles are disabled
|
|
402
|
+
// This check is placed after all hooks so hook ordering stays consistent across renders.
|
|
403
|
+
if (!showThoughtBubbles)
|
|
404
|
+
return null;
|
|
405
|
+
return (_jsxs(OverlayContainer, { children: [_jsx("svg", { style: {
|
|
406
|
+
position: "fixed",
|
|
407
|
+
left: 0,
|
|
408
|
+
top: 0,
|
|
409
|
+
width: "100vw",
|
|
410
|
+
height: "100vh",
|
|
411
|
+
pointerEvents: "none",
|
|
412
|
+
zIndex: 9998,
|
|
413
|
+
opacity: 1,
|
|
414
|
+
}, children: renderableBubbles.map((edge, index) => {
|
|
415
|
+
// Per-bubble staggered animation delay in milliseconds (for line animations)
|
|
416
|
+
const animationDelay = index * LAYOUT_BUBBLES_ANIMATION_DELAY_MS;
|
|
417
|
+
const bubbleState = bubbleStates.get(edge.id) || { isVisible: true, isExiting: false, enteredAt: 0 };
|
|
418
|
+
if (!bubbleState.isVisible)
|
|
419
|
+
return null;
|
|
420
|
+
// Only render lines after the bubble's entrance animation delay has elapsed.
|
|
421
|
+
const elapsed = Date.now() - (bubbleState.enteredAt || 0);
|
|
422
|
+
const shouldShowLines = elapsed >= animationDelay;
|
|
423
|
+
if (!shouldShowLines)
|
|
424
|
+
return null;
|
|
425
|
+
// Calculate fresh coordinates for this line (may return multiple targets)
|
|
426
|
+
const coordsArray = calculateLineCoordinates(edge, index);
|
|
427
|
+
if (!coordsArray || coordsArray.length === 0)
|
|
428
|
+
return null;
|
|
429
|
+
return (_jsx("g", { children: coordsArray.map((coords) => {
|
|
430
|
+
const lineKey = `${edge.id}-${coords.targetAgent}`;
|
|
431
|
+
return (_jsx("line", { ref: (el) => {
|
|
432
|
+
if (el) {
|
|
433
|
+
lineRefs.current.set(lineKey, el);
|
|
434
|
+
}
|
|
435
|
+
else {
|
|
436
|
+
lineRefs.current.delete(lineKey);
|
|
437
|
+
}
|
|
438
|
+
}, x1: coords.x1, y1: coords.y1, x2: coords.x2, y2: coords.y2, stroke: "var(--thought-bubble-line-color)", strokeWidth: "3", strokeDasharray: "3,3", style: {
|
|
439
|
+
opacity: bubbleState.isExiting ? 0 : CONNECTING_LINE_OPACITY,
|
|
440
|
+
transition: (() => {
|
|
441
|
+
const duration = bubbleState.isExiting ? 400 : 600;
|
|
442
|
+
const delay = bubbleState.isExiting ? 0 : animationDelay;
|
|
443
|
+
return `opacity ${duration}ms cubic-bezier(0.2, 0, 0.2, 1) ${delay}ms`;
|
|
444
|
+
})(),
|
|
445
|
+
} }, `line-${lineKey}`));
|
|
446
|
+
}) }, `line-group-${edge.id}`));
|
|
447
|
+
}) }), renderableBubbles.map((edge, index) => {
|
|
448
|
+
const text = edge.data?.text;
|
|
449
|
+
if (!text)
|
|
450
|
+
return null;
|
|
451
|
+
// Per-bubble staggered animation delay in milliseconds
|
|
452
|
+
const animationDelay = index * LAYOUT_BUBBLES_ANIMATION_DELAY_MS;
|
|
453
|
+
const isHovered = hoveredBubbleId === edge.id;
|
|
454
|
+
const isTruncated = truncatedBubbles.has(edge.id);
|
|
455
|
+
const bubbleState = bubbleStates.get(edge.id) || { isVisible: true, isExiting: false };
|
|
456
|
+
return (_jsx(Fragment, { children: _jsx(ThoughtBubble, { "data-bubble-id": edge.id, isHovered: isHovered, isTruncated: isTruncated, animationDelay: animationDelay, bubbleIndex: index, isVisible: bubbleState.isVisible, isExiting: bubbleState.isExiting, onMouseEnter: () => handleHoverChange(edge.id), onMouseLeave: () => handleHoverChange(null), children: _jsx(TruncatedText, { isHovered: isHovered, isTruncated: isTruncated, ref: (el) => {
|
|
457
|
+
// Store/remove this text node in `textRefs` for truncation checks.
|
|
458
|
+
if (el) {
|
|
459
|
+
textRefs.current.set(edge.id, el);
|
|
460
|
+
}
|
|
461
|
+
else {
|
|
462
|
+
textRefs.current.delete(edge.id);
|
|
463
|
+
}
|
|
464
|
+
}, children: text }) }) }, edge.id));
|
|
465
|
+
})] }));
|
|
466
|
+
};
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const DEFAULT_FRONTMAN_X_POS = 150;
|
|
2
|
+
export declare const DEFAULT_FRONTMAN_Y_POS = 450;
|
|
3
|
+
export declare const BASE_RADIUS = 100;
|
|
4
|
+
export declare const LEVEL_SPACING = 150;
|
|
5
|
+
export declare const BACKGROUND_COLORS: string[];
|
|
6
|
+
export declare const BACKGROUND_COLORS_DARK_IDX = 6;
|
|
7
|
+
export declare const HEATMAP_COLORS: string[];
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 Cognizant Technology Solutions Corp, www.cognizant.com.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
export const DEFAULT_FRONTMAN_X_POS = 150;
|
|
17
|
+
export const DEFAULT_FRONTMAN_Y_POS = 450;
|
|
18
|
+
// Minimum distance from center
|
|
19
|
+
export const BASE_RADIUS = 100;
|
|
20
|
+
// Distance between depth levels
|
|
21
|
+
export const LEVEL_SPACING = 150;
|
|
22
|
+
// Palette for progressive coloring of nodes based on depth
|
|
23
|
+
export const BACKGROUND_COLORS = [
|
|
24
|
+
"#f7fbff",
|
|
25
|
+
"#deebf7",
|
|
26
|
+
"#c6dbef",
|
|
27
|
+
"#9ecae1",
|
|
28
|
+
"#6baed6",
|
|
29
|
+
"#4292c6",
|
|
30
|
+
"#2171b5",
|
|
31
|
+
"#08519c",
|
|
32
|
+
"#08306b",
|
|
33
|
+
"#041c45",
|
|
34
|
+
];
|
|
35
|
+
// Zero-based index of the one where colors start to get dark. Used for displaying contrasting text colors.
|
|
36
|
+
export const BACKGROUND_COLORS_DARK_IDX = 6; // Somewhat subjective
|
|
37
|
+
// Palette for heatmap coloring of nodes
|
|
38
|
+
// For now, use same palette as background colors
|
|
39
|
+
export const HEATMAP_COLORS = BACKGROUND_COLORS;
|
package/dist/const.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export declare const LOGO: string;
|
|
2
|
+
export declare const NEURO_SAN_UI_VERSION: string;
|
|
3
|
+
export declare const CONTACT_US_CONFIRMATION_DIALOG_TITLE = "Contact Us";
|
|
4
|
+
export declare const CONTACT_US_CONFIRMATION_DIALOG_TEXT: string;
|
|
5
|
+
/**
|
|
6
|
+
* The default user image to use when the user does not have a profile picture.
|
|
7
|
+
*/
|
|
8
|
+
export declare const DEFAULT_USER_IMAGE = "https://www.gravatar.com/avatar/?d=mp";
|
|
9
|
+
export declare const authenticationEnabled: () => boolean;
|
|
10
|
+
export declare const DEFAULT_NEURO_SAN_SERVER_URL = "https://neuro-san-dev.decisionai.ml";
|
package/dist/const.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/*
|
|
2
|
+
Copyright 2025 Cognizant Technology Solutions Corp, www.cognizant.com.
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
*/
|
|
16
|
+
// Name to use for application
|
|
17
|
+
export const LOGO = "Neuro® AI";
|
|
18
|
+
export const NEURO_SAN_UI_VERSION = process.env["NEXT_PUBLIC_NEURO_SAN_UI_VERSION"] ?? "Unknown Version";
|
|
19
|
+
export const CONTACT_US_CONFIRMATION_DIALOG_TITLE = "Contact Us";
|
|
20
|
+
export const CONTACT_US_CONFIRMATION_DIALOG_TEXT = "Would you like to send the Cognizant Neuro AI support team an email? " +
|
|
21
|
+
"You will need to have an email client installed on your device in order " +
|
|
22
|
+
"to continue. If you don't have an email client, you can still contact us at " +
|
|
23
|
+
"NeuroAiSupport@cognizant.com using a web based email client.";
|
|
24
|
+
/**
|
|
25
|
+
* The default user image to use when the user does not have a profile picture.
|
|
26
|
+
*/
|
|
27
|
+
export const DEFAULT_USER_IMAGE = "https://www.gravatar.com/avatar/?d=mp";
|
|
28
|
+
export const authenticationEnabled = () => process.env["NEXT_PUBLIC_ENABLE_AUTHENTICATION"] !== "false";
|
|
29
|
+
// Default "dev URL" for NeuroSan server, to allow for "zero config" execution.
|
|
30
|
+
export const DEFAULT_NEURO_SAN_SERVER_URL = "https://neuro-san-dev.decisionai.ml";
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Controller module for interacting with the Agent LLM API.
|
|
3
|
+
*/
|
|
4
|
+
import { ChatContext, ChatResponse, ConnectivityResponse, FunctionResponse } from "../../generated/neuro-san/NeuroSanClient.js";
|
|
5
|
+
export interface TestConnectionResult {
|
|
6
|
+
readonly success: boolean;
|
|
7
|
+
readonly status?: string;
|
|
8
|
+
readonly version?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Test connection for a neuro-san server.
|
|
12
|
+
* @param url The neuro-san server URL.
|
|
13
|
+
* @returns A boolean indicating whether the connection was successful.
|
|
14
|
+
*/
|
|
15
|
+
export declare function testConnection(url: string): Promise<TestConnectionResult>;
|
|
16
|
+
/**
|
|
17
|
+
* Get the list of available agent networks from the concierge service.
|
|
18
|
+
* @param url The neuro-san server URL
|
|
19
|
+
* @returns A promise that resolves to an array of agent network names.
|
|
20
|
+
*/
|
|
21
|
+
export declare function getAgentNetworks(url: string): Promise<string[]>;
|
|
22
|
+
/**
|
|
23
|
+
* Send a chat query to the Agent LLM API. This opens a session with the agent network..
|
|
24
|
+
* @param url The neuro-san server URL
|
|
25
|
+
* @param signal The AbortSignal to use for the request. Used to cancel the request on user demand
|
|
26
|
+
* @param userInput The user input to send to the agent.
|
|
27
|
+
* In practice this "input" will actually be the output from one of the previous agents such as the data generator
|
|
28
|
+
* or scoping agent.
|
|
29
|
+
* @param targetAgent The target agent to send the request to. See CombinedAgentType for the list of available agents.
|
|
30
|
+
* @param callback The callback function to be called when a chunk of data is received from the server.
|
|
31
|
+
* @param chatContext "Opaque" conversation context for maintaining conversation state with the server. Neuro-san
|
|
32
|
+
* agents do not use ChatHistory directly, but rather, ChatContext, which is a collection of ChatHistory objects.
|
|
33
|
+
* @param slyData Data items that should not be send to the LLM. Generated by the server.
|
|
34
|
+
* @param userId Current user ID in the session.
|
|
35
|
+
* @returns The response from the agent network.
|
|
36
|
+
*/
|
|
37
|
+
export declare function sendChatQuery(url: string, signal: AbortSignal, userInput: string, targetAgent: string, callback: (chunk: string) => void, chatContext: ChatContext, slyData: Record<string, unknown>, userId: string): Promise<ChatResponse>;
|
|
38
|
+
/**
|
|
39
|
+
* Gets information on the agent and tool connections within a network
|
|
40
|
+
* @param url The neuro-san server URL
|
|
41
|
+
* @param network The network to get connectivity information for
|
|
42
|
+
* @param userId Current user ID in the session.
|
|
43
|
+
* @returns The connectivity info as a <code>ConnectivityResponse</code> object
|
|
44
|
+
* @throws Various exceptions if anything goes wrong such as network issues or invalid agent type.
|
|
45
|
+
* Caller is responsible for try-catch.
|
|
46
|
+
*/
|
|
47
|
+
export declare function getConnectivity(url: string, network: string, userId: string): Promise<ConnectivityResponse>;
|
|
48
|
+
/**
|
|
49
|
+
* Get the function of a specified agent meaning its brief description
|
|
50
|
+
* @param url The neuro-san server URL
|
|
51
|
+
* @param agent The agent to get the function for
|
|
52
|
+
* @param userId Current user ID in the session.
|
|
53
|
+
* @returns The function info as a <code>FunctionResponse</code> object
|
|
54
|
+
* @throws Various exceptions if anything goes wrong such as network issues or invalid agent type.
|
|
55
|
+
*/
|
|
56
|
+
export declare function getAgentFunction(url: string, agent: string, userId: string): Promise<FunctionResponse>;
|