llmasaservice-ui
Version:
Prebuilt UI components for LLMAsAService.io
2,743 lines • 105 kB
JavaScript
var __defProp = Object.defineProperty;
var __defProps = Object.defineProperties;
var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
var __getOwnPropSymbols = Object.getOwnPropertySymbols;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __propIsEnum = Object.prototype.propertyIsEnumerable;
var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
var __spreadValues = (a, b) => {
for (var prop in b || (b = {}))
if (__hasOwnProp.call(b, prop))
__defNormalProp(a, prop, b[prop]);
if (__getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(b)) {
if (__propIsEnum.call(b, prop))
__defNormalProp(a, prop, b[prop]);
}
return a;
};
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
var __objRest = (source, exclude) => {
var target = {};
for (var prop in source)
if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
target[prop] = source[prop];
if (source != null && __getOwnPropSymbols)
for (var prop of __getOwnPropSymbols(source)) {
if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
target[prop] = source[prop];
}
return target;
};
var __async = (__this, __arguments, generator) => {
return new Promise((resolve, reject) => {
var fulfilled = (value) => {
try {
step(generator.next(value));
} catch (e) {
reject(e);
}
};
var rejected = (value) => {
try {
step(generator.throw(value));
} catch (e) {
reject(e);
}
};
var step = (x) => x.done ? resolve(x.value) : Promise.resolve(x.value).then(fulfilled, rejected);
step((generator = generator.apply(__this, __arguments)).next());
});
};
// src/ChatPanel.tsx
import { useLLM } from "llmasaservice-client";
import React3, {
useCallback,
useEffect as useEffect2,
useMemo,
useRef,
useState as useState2
} from "react";
import ReactMarkdown from "react-markdown";
import ReactDOMServer from "react-dom/server";
import remarkGfm from "remark-gfm";
import rehypeRaw from "rehype-raw";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import materialDark from "react-syntax-highlighter/dist/esm/styles/prism/material-dark.js";
import materialLight from "react-syntax-highlighter/dist/esm/styles/prism/material-light.js";
// src/EmailModal.tsx
import React, { useState } from "react";
var EmailModal = ({ isOpen, onClose, onSend, defaultEmail }) => {
const [email, setEmail] = useState("");
const [emailFrom, setEmailFrom] = useState(defaultEmail || "");
const handleSend = () => {
onSend(email, emailFrom);
onClose();
};
if (!isOpen) return null;
return /* @__PURE__ */ React.createElement("div", { className: "modal-overlay" }, /* @__PURE__ */ React.createElement("div", { className: "modal-content" }, /* @__PURE__ */ React.createElement("p", { className: "modal-text" }, "Email Addresses", /* @__PURE__ */ React.createElement("br", null), " (If multiple, comma separate them)"), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement(
"input",
{
type: "email",
width: "100%",
value: email,
onChange: (e) => setEmail(e.target.value),
placeholder: "To email address"
}
)), /* @__PURE__ */ React.createElement("p", null, /* @__PURE__ */ React.createElement(
"input",
{
type: "email",
width: "100%",
value: emailFrom,
onChange: (e) => setEmailFrom(e.target.value),
placeholder: "From email address (optional)"
}
)), /* @__PURE__ */ React.createElement("div", { className: "modal-buttons" }, /* @__PURE__ */ React.createElement("button", { onClick: onClose }, "Cancel"), /* @__PURE__ */ React.createElement("button", { onClick: handleSend }, "Send"))));
};
var EmailModal_default = EmailModal;
// src/ToolInfoModal.tsx
import React2 from "react";
var ToolInfoModal = ({
isOpen,
onClose,
data
}) => {
if (!isOpen || !data) return null;
return /* @__PURE__ */ React2.createElement("div", { className: "modal-overlay", onClick: onClose }, /* @__PURE__ */ React2.createElement(
"div",
{
className: "modal-content tool-info-modal-content",
onClick: (e) => e.stopPropagation()
},
/* @__PURE__ */ React2.createElement("div", { className: "tool-info-container" }, /* @__PURE__ */ React2.createElement("div", { className: "tool-info-section" }, /* @__PURE__ */ React2.createElement("b", null, "Tool Calls"), /* @__PURE__ */ React2.createElement(
"textarea",
{
className: "tool-info-json",
readOnly: true,
value: JSON.stringify(data.calls, null, 2)
}
)), /* @__PURE__ */ React2.createElement("div", { className: "tool-info-section" }, /* @__PURE__ */ React2.createElement("b", null, "Tool Responses"), /* @__PURE__ */ React2.createElement(
"textarea",
{
className: "tool-info-json",
readOnly: true,
value: JSON.stringify(data.responses, null, 2)
}
))),
/* @__PURE__ */ React2.createElement("div", { className: "modal-buttons" }, /* @__PURE__ */ React2.createElement("button", { onClick: onClose }, "Close"))
));
};
var ToolInfoModal_default = ToolInfoModal;
// src/ChatPanel.tsx
var ChatPanel = ({
project_id,
initialPrompt = "",
title = "",
placeholder = "Type a message",
hideInitialPrompt = true,
customer = {},
messages = [],
data = [],
thumbsUpClick,
thumbsDownClick,
theme = "light",
cssUrl = "",
markdownClass = null,
width = "300px",
height = "100vh",
url = null,
scrollToEnd = false,
initialMessage = "",
prismStyle = theme === "light" ? materialLight : materialDark,
service = null,
historyChangedCallback = null,
responseCompleteCallback = null,
promptTemplate = "",
actions,
showSaveButton = true,
showEmailButton = true,
showNewConversationButton = true,
followOnQuestions = [],
clearFollowOnQuestionsNextPrompt = false,
followOnPrompt = "",
showPoweredBy = true,
agent = null,
conversation = null,
showCallToAction = false,
callToActionButtonText = "Submit",
callToActionEmailAddress = "",
callToActionEmailSubject = "Agent CTA submitted",
initialHistory = {},
hideRagContextInPrompt = true,
createConversationOnFirstChat = true,
customerEmailCaptureMode = "HIDE",
customerEmailCapturePlaceholder = "Please enter your email...",
mcpServers
}) => {
var _a;
const isEmailAddress = (email) => {
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
return emailRegex.test(email);
};
const [nextPrompt, setNextPrompt] = useState2("");
const [lastController, setLastController] = useState2(new AbortController());
const [lastMessages, setLastMessages] = useState2([]);
const [history, setHistory] = useState2(
initialHistory
);
const [isLoading, setIsLoading] = useState2(false);
const [lastPrompt, setLastPrompt] = useState2(null);
const [lastKey, setLastKey] = useState2(null);
const [hasScroll, setHasScroll] = useState2(false);
const bottomRef = useRef(null);
const bottomPanelRef = useRef(null);
const textareaRef = useRef(null);
const [isAtBottom, setIsAtBottom] = useState2(true);
const [isEmailModalOpen, setIsEmailModalOpen] = useState2(false);
const [isToolInfoModalOpen, setIsToolInfoModalOpen] = useState2(false);
const [toolInfoData, setToolInfoData] = useState2(null);
const [currentConversation, setCurrentConversation] = useState2(
conversation
);
const [emailInput, setEmailInput] = useState2(
(_a = customer == null ? void 0 : customer.customer_user_email) != null ? _a : ""
);
const [emailInputSet, setEmailInputSet] = useState2(
isEmailAddress(emailInput)
);
const [emailValid, setEmailValid] = useState2(true);
const [showEmailPanel, setShowEmailPanel] = useState2(
customerEmailCaptureMode !== "HIDE"
);
const [callToActionSent, setCallToActionSent] = useState2(false);
const [CTAClickedButNoEmail, setCTAClickedButNoEmail] = useState2(false);
const [emailSent, setEmailSent] = useState2(false);
const [emailClickedButNoEmail, setEmailClickedButNoEmail] = useState2(false);
const [currentCustomer, setCurrentCustomer] = useState2(
customer
);
const [justReset, setJustReset] = useState2(false);
const [newConversationConfirm, setNewConversationConfirm] = useState2(false);
const [allActions, setAllActions] = useState2([]);
const [pendingToolRequests, setPendingToolRequests] = useState2([]);
const [followOnQuestionsState, setFollowOnQuestionsState] = useState2(followOnQuestions);
const [sessionApprovedTools, setSessionApprovedTools] = useState2(
[]
);
const [alwaysApprovedTools, setAlwaysApprovedTools] = useState2([]);
const [thinkingBlocks, setThinkingBlocks] = useState2([]);
const [currentThinkingIndex, setCurrentThinkingIndex] = useState2(0);
const [pendingButtonAttachments, setPendingButtonAttachments] = useState2([]);
const buttonActionRegistry = useRef(/* @__PURE__ */ new Map());
useEffect2(() => {
const stored = localStorage.getItem("alwaysApprovedTools");
if (stored) setAlwaysApprovedTools(JSON.parse(stored));
}, []);
useEffect2(() => {
localStorage.setItem(
"alwaysApprovedTools",
JSON.stringify(alwaysApprovedTools)
);
}, [alwaysApprovedTools]);
useEffect2(() => {
if (followOnQuestions !== followOnQuestionsState) {
setFollowOnQuestionsState(followOnQuestions);
}
}, [followOnQuestions]);
useEffect2(() => {
return () => {
buttonActionRegistry.current.clear();
};
}, []);
useEffect2(() => {
const cleanupInterval = setInterval(() => {
const registryKeys = Array.from(buttonActionRegistry.current.keys());
const orphanedKeys = [];
registryKeys.forEach((buttonId) => {
const button = document.getElementById(buttonId);
if (!button) {
orphanedKeys.push(buttonId);
buttonActionRegistry.current.delete(buttonId);
}
});
if (orphanedKeys.length > 0) {
}
}, 1e4);
return () => clearInterval(cleanupInterval);
}, []);
const [iframeUrl, setIframeUrl] = useState2(null);
const responseAreaRef = useRef(null);
const THINKING_PATTERNS = useMemo(
() => ({
reasoning: /<reasoning>([\s\S]*?)<\/reasoning>/gi,
searching: /<searching>([\s\S]*?)<\/searching>/gi
}),
[]
);
const reasoningRegex = useMemo(
() => new RegExp(THINKING_PATTERNS.reasoning.source, "gi"),
[THINKING_PATTERNS.reasoning.source]
);
const searchingRegex = useMemo(
() => new RegExp(THINKING_PATTERNS.searching.source, "gi"),
[THINKING_PATTERNS.searching.source]
);
const cleanContentForDisplay = useCallback((content) => {
let cleaned = content.replace(/\*\*(.*?)\*\*/g, "$1").replace(/\*(.*?)\*/g, "$1").replace(/\n+/g, " ").replace(/\s+/g, " ").trim();
if (cleaned.length > 100) {
cleaned = cleaned.substring(0, 100) + "...";
}
return cleaned || "Thinking";
}, []);
const processThinkingTags = useCallback(
(text) => {
var _a2, _b;
if (!text) {
return {
cleanedText: "",
thinkingBlocks: [],
lastThinkingContent: "Thinking"
};
}
const processedText = text.replace(/\u200B/g, "");
const allMatches = [];
reasoningRegex.lastIndex = 0;
searchingRegex.lastIndex = 0;
let reasoningMatch;
while ((reasoningMatch = reasoningRegex.exec(processedText)) !== null) {
const content = (_a2 = reasoningMatch[1]) == null ? void 0 : _a2.trim();
if (content) {
allMatches.push({
content,
index: reasoningMatch.index,
type: "reasoning"
});
}
}
let searchingMatch;
while ((searchingMatch = searchingRegex.exec(processedText)) !== null) {
const content = (_b = searchingMatch[1]) == null ? void 0 : _b.trim();
if (content) {
allMatches.push({
content,
index: searchingMatch.index,
type: "searching"
});
}
}
const thinkingBlocks2 = allMatches.sort((a, b) => a.index - b.index);
let cleanedText = processedText.replace(THINKING_PATTERNS.reasoning, "").replace(THINKING_PATTERNS.searching, "").trim();
let lastThinkingContent = "Thinking";
if (thinkingBlocks2.length > 0) {
const lastBlock = thinkingBlocks2[thinkingBlocks2.length - 1];
if (lastBlock == null ? void 0 : lastBlock.content) {
lastThinkingContent = cleanContentForDisplay(lastBlock.content);
}
}
return {
cleanedText,
thinkingBlocks: thinkingBlocks2,
lastThinkingContent
};
},
[
THINKING_PATTERNS.reasoning,
THINKING_PATTERNS.searching,
reasoningRegex,
searchingRegex,
cleanContentForDisplay
]
);
const renderThinkingBlocks = useCallback(() => {
if (thinkingBlocks.length === 0) return null;
const currentBlock = thinkingBlocks[currentThinkingIndex];
if (!currentBlock) return null;
const icon = currentBlock.type === "reasoning" ? "\u{1F914}" : "\u{1F50D}";
const baseTitle = currentBlock.type === "reasoning" ? "Reasoning" : "Searching";
const extractTitleAndContent = (text) => {
const trimmedText = text.trim();
const titleMatch = trimmedText.match(/^\*\*\[(.*?)\]\*\*/);
if (titleMatch) {
const extractedTitle = titleMatch[1];
const remainingContent = trimmedText.replace(/^\*\*\[.*?\]\*\*\s*\n?/, "").replace(/\*\*(.*?)\*\*/g, "$1").trim();
return {
displayTitle: `${baseTitle}: ${extractedTitle}`,
content: remainingContent
};
}
return {
displayTitle: baseTitle,
content: trimmedText.replace(/\*\*(.*?)\*\*/g, "$1")
};
};
const { displayTitle, content } = extractTitleAndContent(
currentBlock.content
);
return /* @__PURE__ */ React3.createElement("div", { className: "thinking-block-container" }, /* @__PURE__ */ React3.createElement("div", { className: `thinking-section ${currentBlock.type}-section` }, /* @__PURE__ */ React3.createElement("div", { className: "thinking-header" }, icon, " ", displayTitle, thinkingBlocks.length > 1 && /* @__PURE__ */ React3.createElement("div", { className: "thinking-navigation" }, /* @__PURE__ */ React3.createElement(
"button",
{
onClick: () => setCurrentThinkingIndex(
Math.max(0, currentThinkingIndex - 1)
),
disabled: currentThinkingIndex === 0,
className: "thinking-nav-btn"
},
"\u2190"
), /* @__PURE__ */ React3.createElement("span", { className: "thinking-counter" }, currentThinkingIndex + 1, " / ", thinkingBlocks.length), /* @__PURE__ */ React3.createElement(
"button",
{
onClick: () => setCurrentThinkingIndex(
Math.min(
thinkingBlocks.length - 1,
currentThinkingIndex + 1
)
),
disabled: currentThinkingIndex === thinkingBlocks.length - 1,
className: "thinking-nav-btn"
},
"\u2192"
))), /* @__PURE__ */ React3.createElement("div", { className: "thinking-content" }, content)));
}, [thinkingBlocks, currentThinkingIndex]);
const getBrowserInfo = () => {
try {
return {
currentTimeUTC: (/* @__PURE__ */ new Date()).toISOString(),
userTimezone: typeof Intl !== "undefined" && Intl.DateTimeFormat().resolvedOptions().timeZone || "unknown",
userLanguage: typeof navigator !== "undefined" && (navigator.language || navigator.language) || "unknown"
};
} catch (e) {
console.warn("Error getting browser info:", e);
return {
currentTimeUTC: (/* @__PURE__ */ new Date()).toISOString(),
userTimezone: "unknown",
userLanguage: "unknown"
};
}
};
const browserInfo = useMemo(() => getBrowserInfo(), []);
const dataWithExtras = () => {
var _a2, _b, _c, _d, _e;
return [
...data,
{ key: "--customer_id", data: (_a2 = currentCustomer == null ? void 0 : currentCustomer.customer_id) != null ? _a2 : "" },
{
key: "--customer_name",
data: (_b = currentCustomer == null ? void 0 : currentCustomer.customer_name) != null ? _b : ""
},
{
key: "--customer_user_id",
data: (_c = currentCustomer == null ? void 0 : currentCustomer.customer_user_id) != null ? _c : ""
},
{
key: "--customer_user_name",
data: (_d = currentCustomer == null ? void 0 : currentCustomer.customer_user_name) != null ? _d : ""
},
{
key: "--customer_user_email",
data: (_e = currentCustomer == null ? void 0 : currentCustomer.customer_user_email) != null ? _e : ""
},
{ key: "--email", data: emailInput != null ? emailInput : "" },
{ key: "--emailValid", data: emailValid ? "true" : "false" },
{
key: "--emailInputSet",
data: emailInputSet ? "true" : "false"
},
{
key: "--emailPanelShowing",
data: showEmailPanel ? "true" : "false"
},
{
key: "--callToActionSent",
data: callToActionSent ? "true" : "false"
},
{
key: "--CTAClickedButNoEmail",
data: CTAClickedButNoEmail ? "true" : "false"
},
{ key: "--emailSent", data: emailSent ? "true" : "false" },
{
key: "--emailClickedButNoEmail",
data: emailClickedButNoEmail ? "true" : "false"
},
{
key: "--currentTimeUTC",
data: browserInfo == null ? void 0 : browserInfo.currentTimeUTC
},
{ key: "--userTimezone", data: browserInfo == null ? void 0 : browserInfo.userTimezone },
{ key: "--userLanguage", data: browserInfo == null ? void 0 : browserInfo.userLanguage }
];
};
let publicAPIUrl = "https://api.llmasaservice.io";
if (window.location.hostname === "localhost" || window.location.hostname === "dev.llmasaservice.io") {
publicAPIUrl = "https://8ftw8droff.execute-api.us-east-1.amazonaws.com/dev";
}
const [toolList, setToolList] = useState2([]);
const [toolsLoading, setToolsLoading] = useState2(false);
const [toolsFetchError, setToolsFetchError] = useState2(false);
useEffect2(() => {
console.log("MCP servers", mcpServers);
const fetchAndSetTools = () => __async(void 0, null, function* () {
if (!mcpServers || mcpServers.length === 0) {
setToolList([]);
setToolsLoading(false);
setToolsFetchError(false);
return;
}
setToolsLoading(true);
setToolsFetchError(false);
try {
const fetchPromises = (mcpServers != null ? mcpServers : []).map((m) => __async(void 0, null, function* () {
const urlToFetch = `${publicAPIUrl}/tools/${encodeURIComponent(
m.url
)}`;
try {
const response2 = yield fetch(urlToFetch);
if (!response2.ok) {
console.error(
`Error fetching tools from ${m.url}: ${response2.status} ${response2.statusText}`
);
const errorBody = yield response2.text();
console.error(`Error body: ${errorBody}`);
throw new Error(
`HTTP ${response2.status}: ${response2.statusText}`
);
}
const toolsFromServer = yield response2.json();
if (Array.isArray(toolsFromServer)) {
return toolsFromServer.map((tool) => __spreadProps(__spreadValues({}, tool), {
url: m.url,
accessToken: m.accessToken || "",
headers: {}
}));
} else {
return [];
}
} catch (fetchError) {
console.error(
`Network or parsing error fetching tools from ${m.url}:`,
fetchError
);
throw fetchError;
}
}));
const results = yield Promise.all(fetchPromises);
const allTools = results.flat();
console.log("Merged tools from all servers:", allTools);
setToolList(allTools);
setToolsFetchError(false);
} catch (error) {
console.error(
"An error occurred while processing tool fetches:",
error
);
setToolList([]);
setToolsFetchError(true);
} finally {
setToolsLoading(false);
}
});
fetchAndSetTools();
}, [mcpServers, publicAPIUrl]);
const { send, response, idle, stop, lastCallId, setResponse } = useLLM({
project_id,
customer: currentCustomer,
url,
agent,
tools: toolList.map((item) => {
return {
name: item.name,
description: item.description,
parameters: item.parameters
};
})
});
useEffect2(() => {
setShowEmailPanel(customerEmailCaptureMode !== "HIDE");
if (customerEmailCaptureMode === "REQUIRED") {
if (!isEmailAddress(emailInput)) {
setEmailValid(false);
}
}
}, [customerEmailCaptureMode]);
useEffect2(() => {
if (lastCallId && lastCallId !== "" && idle && response) {
allActions.filter((a) => a.type === "response").forEach((action) => {
if (action.type === "response" && action.pattern) {
const regex = new RegExp(action.pattern, "gi");
const matches = regex.exec(response);
console.log(
"action match",
matches,
action.pattern,
action.callback
);
if (matches && action.callback) {
action.callback(matches[0], matches.slice(1));
}
}
});
}
if (responseCompleteCallback) {
if (lastCallId && lastCallId !== "" && idle)
responseCompleteCallback(lastCallId, lastPrompt != null ? lastPrompt : "", response);
}
}, [idle]);
useEffect2(() => {
if (Object.keys(initialHistory).length === 0) return;
setHistory(initialHistory);
}, [initialHistory]);
useEffect2(() => {
if (!conversation || conversation === "") return;
setCurrentConversation(conversation);
setHistory(initialHistory);
}, [conversation]);
useEffect2(() => {
const existingLinks = document.querySelectorAll(
'link[data-source="llmasaservice-ui"]'
);
existingLinks.forEach((link) => {
var _a2;
return (_a2 = link.parentNode) == null ? void 0 : _a2.removeChild(link);
});
const existingStyles = document.querySelectorAll(
'style[data-source="llmasaservice-ui"]'
);
existingStyles.forEach((style) => {
var _a2;
return (_a2 = style.parentNode) == null ? void 0 : _a2.removeChild(style);
});
if (cssUrl) {
if (cssUrl.startsWith("http://") || cssUrl.startsWith("https://")) {
const link = document.createElement("link");
link.href = cssUrl;
link.rel = "stylesheet";
link.setAttribute("data-source", "llmasaservice-ui");
document.head.appendChild(link);
console.log("Added CSS link", link);
} else {
const style = document.createElement("style");
style.textContent = cssUrl;
style.setAttribute("data-source", "llmasaservice-ui");
document.head.appendChild(style);
console.log("Added inline CSS");
}
}
return () => {
const links = document.querySelectorAll(
'link[data-source="llmasaservice-ui"]'
);
links.forEach((link) => {
var _a2;
return (_a2 = link.parentNode) == null ? void 0 : _a2.removeChild(link);
});
const styles = document.querySelectorAll(
'style[data-source="llmasaservice-ui"]'
);
styles.forEach((style) => {
var _a2;
return (_a2 = style.parentNode) == null ? void 0 : _a2.removeChild(style);
});
};
}, [cssUrl]);
const extractValue = (match, groups = [], extraArgs = []) => {
var _a2;
if ((!extraArgs || extraArgs.length === 0) && (!groups || groups.length === 0)) {
return match;
}
if ((!extraArgs || extraArgs.length === 0) && groups && groups.length > 0) {
return groups[0];
}
if (extraArgs && extraArgs.length > 0) {
const template = (_a2 = extraArgs[0]) != null ? _a2 : "";
return template.replace(/\$(\d+)/g, (_, index) => {
const i = parseInt(index, 10);
return groups[i] !== void 0 ? groups[i] : "";
});
}
return "";
};
const openUrlActionCallback = useCallback(
(match, groups, ...extraArgs) => {
const url2 = extractValue(match, groups, extraArgs);
if ((url2 == null ? void 0 : url2.startsWith("http")) || (url2 == null ? void 0 : url2.startsWith("mailto"))) {
window.open(url2, "_blank");
}
},
[]
);
const copyToClipboardCallback = useCallback(
(match, groups, ...extraArgs) => {
const val = extractValue(match, groups, extraArgs);
navigator.clipboard.writeText(val);
},
[]
);
const showAlertCallback = useCallback(
(match, groups, ...extraArgs) => {
alert(extractValue(match, groups, extraArgs));
},
[]
);
const sendFollowOnPromptCallback = useCallback(
(match, groups, ...extraArgs) => {
const val = extractValue(match, groups, extraArgs);
if (val && val !== followOnPrompt) {
continueChat(val);
}
},
[followOnPrompt]
);
const setFollowUpQuestionsCallback = useCallback(
(match, groups, ...extraArgs) => {
const val = extractValue(match, groups, extraArgs).split("|");
setFollowOnQuestionsState(val);
},
[followOnQuestions]
);
const openIframeCallback = useCallback(
(match, groups, ...extraArgs) => {
const url2 = extractValue(match, groups, extraArgs);
if (url2 == null ? void 0 : url2.startsWith("http")) {
setIframeUrl(url2);
}
},
[]
);
const anthropic_toolAction = {
pattern: '\\{"type":"tool_use","id":"([^"]+)","name":"([^"]+)","input":(\\{[\\s\\S]+?\\}),"service":"([^"]+)"\\}',
type: "markdown",
markdown: "<br />*Tool use requested: $2*",
actionType: "tool"
};
const openAI_toolAction = {
pattern: '\\{"id":"([^"]+)","type":"function","function":\\{"name":"([^"]+)","arguments":"((?:\\\\.|[^"\\\\])*)"\\},"service":"([^"]+)"\\}',
type: "markdown",
markdown: "<br />*Tool use requested: $2*",
actionType: "tool"
};
const google_toolAction = {
pattern: '^\\{\\s*"(functionCall)"\\s*:\\s*\\{\\s*"name"\\s*:\\s*"([^"]+)"\\s*,\\s*"args"\\s*:\\s*(\\{[\\s\\S]+?\\})\\s*\\}(?:\\s*,\\s*"thoughtSignature"\\s*:\\s*"[^"]*")?\\s*,\\s*"service"\\s*:\\s*"([^"]+)"\\s*\\}$',
type: "markdown",
markdown: "<br />*Tool use requested: $2*",
actionType: "tool"
};
const callbackMapping = useMemo(
() => ({
openUrlActionCallback,
copyToClipboardCallback,
showAlertCallback,
sendFollowOnPromptCallback,
setFollowUpQuestionsCallback,
openIframeCallback
}),
[
openUrlActionCallback,
copyToClipboardCallback,
showAlertCallback,
sendFollowOnPromptCallback,
setFollowUpQuestionsCallback,
openIframeCallback
]
);
const parseCallbackString = (callbackStr) => {
const regex = /^(\w+)(?:\((.+)\))?$/;
const match = callbackStr.match(regex);
if (match) {
const name = match[1];
const args = match[2] ? match[2].split(",").map((arg) => arg.trim()) : [];
return { name, args };
}
return null;
};
const getActionsArraySafely = (actionsString) => {
let actions2 = [];
if (actionsString && actionsString !== "") {
try {
actions2 = JSON.parse(actionsString);
if (!Array.isArray(actions2)) {
throw new Error("Parsed actions is not an array");
}
actions2 = actions2.map((action) => {
if (typeof action.callback === "string") {
const parsed = parseCallbackString(action.callback);
if (parsed && parsed.name && callbackMapping[parsed.name]) {
const mappedCallback = callbackMapping[parsed.name];
if (mappedCallback) {
return __spreadProps(__spreadValues({}, action), {
callback: (match, groups) => mappedCallback(match, groups, ...parsed.args)
});
}
} else {
console.warn("Invalid or missing callback in action:", action);
return null;
}
} else {
return action;
}
}).filter(Boolean);
} catch (error) {
console.error("Error parsing actions string:", error);
actions2 = [];
}
}
return actions2;
};
useEffect2(() => {
const actionsString = typeof actions === "string" ? actions : JSON.stringify(actions);
setAllActions([
...getActionsArraySafely(actionsString),
anthropic_toolAction,
openAI_toolAction,
google_toolAction
]);
}, [actions]);
const pendingToolRequestsRef = useRef(pendingToolRequests);
useEffect2(() => {
pendingToolRequestsRef.current = pendingToolRequests;
}, [pendingToolRequests]);
const processGivenToolRequests = (requests) => __async(void 0, null, function* () {
if (!requests || requests.length === 0)
requests = pendingToolRequestsRef.current;
if (requests.length === 0) return;
console.log("processGivenToolRequests", requests);
setIsLoading(true);
const toolsToProcess = [...requests];
setPendingToolRequests([]);
try {
const newMessages = [
{
role: "user",
content: [
{
type: "text",
text: lastKey
}
]
}
];
const toolCallsMessage = {
role: "assistant",
content: [],
tool_calls: []
};
const toolCallsPromises = toolsToProcess.map((req) => __async(void 0, null, function* () {
if (!req) return null;
try {
return {
req,
parsedToolCall: JSON.parse(req.match)
};
} catch (e) {
console.error("Failed to parse tool call:", e);
return null;
}
}));
const parsedToolCalls = yield Promise.all(toolCallsPromises);
parsedToolCalls.forEach((item) => {
if (item && item.parsedToolCall) {
toolCallsMessage.tool_calls.push(item.parsedToolCall);
}
});
newMessages.push(toolCallsMessage);
const finalToolCalls = toolCallsMessage.tool_calls;
const toolResponsePromises = parsedToolCalls.map((item) => __async(void 0, null, function* () {
var _a2;
if (!item || !item.req) return null;
const req = item.req;
console.log(`Processing tool ${req.toolName}`);
const mcpTool = toolList.find((tool) => tool.name === req.toolName);
if (!mcpTool) {
console.error(`Tool ${req.toolName} not found in tool list`);
return null;
}
try {
let args;
try {
args = JSON.parse(req.groups[2]);
} catch (e) {
try {
args = JSON.parse(req.groups[2].replace(/\\"/g, '"'));
} catch (err) {
console.error("Failed to parse tool arguments:", err);
return null;
}
}
const body = {
tool: req.groups[1],
args
};
const result = yield fetch(
`${publicAPIUrl}/tools/${encodeURIComponent(mcpTool.url)}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
"x-mcp-access-token": mcpTool.accessToken && mcpTool.accessToken !== "" ? mcpTool.accessToken : "",
"x-project-id": project_id
},
body: JSON.stringify(body)
}
);
if (!result.ok) {
console.error(
`Error calling tool ${req.toolName}: ${result.status} ${result.statusText}`
);
const errorBody = yield result.text();
console.error(`Error body: ${errorBody}`);
return null;
}
let resultData;
try {
resultData = yield result.json();
} catch (jsonError) {
console.error(
`Error parsing JSON response for tool ${req.toolName}:`,
jsonError
);
try {
const textBody = yield result.text();
console.error("Response body (text):", textBody);
} catch (textError) {
console.error(
"Failed to read response body as text either:",
textError
);
}
return null;
}
if (resultData && resultData.content && resultData.content.length > 0) {
const textResult = (_a2 = resultData.content[0]) == null ? void 0 : _a2.text;
return {
role: "tool",
content: [
{
type: "text",
text: textResult
}
],
tool_call_id: req.groups[0]
};
} else {
console.error(`No content returned from tool ${req.toolName}`);
return null;
}
} catch (error) {
console.error(`Error processing tool ${req.toolName}:`, error);
return null;
}
}));
const toolResponses = yield Promise.all(toolResponsePromises);
const finalToolResponses = toolResponses.filter(Boolean);
if (lastKey) {
setHistory((prev) => {
const existingEntry = prev[lastKey] || {};
return __spreadProps(__spreadValues({}, prev), {
[lastKey]: __spreadProps(__spreadValues({}, existingEntry), {
toolCalls: [
...existingEntry.toolCalls || [],
...finalToolCalls
],
toolResponses: [
...existingEntry.toolResponses || [],
...finalToolResponses
]
})
});
});
}
finalToolResponses.forEach((response2) => {
if (response2) {
newMessages.push(response2);
}
});
send(
"",
newMessages,
[
...dataWithExtras(),
{
key: "--messages",
data: newMessages.length.toString()
}
],
true,
true,
service,
currentConversation,
lastController
);
} catch (error) {
console.error("Error in processing all tools:", error);
setIsLoading(false);
}
});
useEffect2(() => {
if (response && response.length > 0) {
setIsLoading(false);
const toolRequests = [];
if (allActions && allActions.length > 0) {
allActions.filter((a) => a.actionType === "tool").forEach((action) => {
var _a2;
const regex = new RegExp(action.pattern, "gmi");
let match;
while ((match = regex.exec(response)) !== null) {
toolRequests.push({
match: match[0],
groups: Array.from(match).slice(1),
toolName: (_a2 = match[2]) != null ? _a2 : "tool"
// Tool name should always in the 2nd capture group
});
}
});
}
if (toolRequests.length > 0) {
console.log("toolRequests", toolRequests);
setPendingToolRequests(toolRequests);
} else {
setPendingToolRequests([]);
}
let responseWithoutTools = response;
if (allActions && allActions.length > 0) {
allActions.filter((a) => a.actionType === "tool").forEach((action) => {
const regex = new RegExp(action.pattern, "gmi");
responseWithoutTools = responseWithoutTools.replace(regex, "");
});
}
const { cleanedText, thinkingBlocks: newThinkingBlocks } = processThinkingTags(responseWithoutTools);
setThinkingBlocks(newThinkingBlocks);
setCurrentThinkingIndex(Math.max(0, newThinkingBlocks.length - 1));
let newResponse = cleanedText;
if (allActions && allActions.length > 0) {
allActions.filter((a) => a.type !== "response" && a.actionType !== "tool").forEach((action, index) => {
const regex = new RegExp(action.pattern, "gmi");
newResponse = newResponse.replace(regex, (match, ...groups) => {
var _a2, _b;
console.log("action match", match, groups);
const matchIndex = groups[groups.length - 2];
const buttonId = `button-${messages.length}-${index}-${matchIndex}`;
let html = match;
if (action.type === "button" || action.type === "callback") {
html = `<br /><button id="${buttonId}" ${action.style ? 'class="' + action.style + '"' : ""}>
${(_a2 = action.markdown) != null ? _a2 : match}
</button>`;
} else if (action.type === "markdown" || action.type === "html") {
html = (_b = action.markdown) != null ? _b : "";
}
html = html.replace(new RegExp("\\$match", "gmi"), match);
groups.forEach((group, index2) => {
html = html.replace(
new RegExp(`\\$${index2 + 1}`, "gmi"),
group
);
});
const buttonContext = {
buttonId,
action,
match,
groups
};
setPendingButtonAttachments((prev) => [...prev, buttonContext]);
return html;
});
});
}
setHistory((prevHistory) => {
const existingEntry = prevHistory[lastKey != null ? lastKey : ""] || {
content: "",
callId: ""
};
const updatedHistory = __spreadProps(__spreadValues({}, prevHistory), {
[lastKey != null ? lastKey : ""]: __spreadProps(__spreadValues({}, existingEntry), {
// This preserves toolCalls and toolResponses
content: newResponse,
// Store cleaned response without thinking tags or tool JSON
callId: lastCallId
})
});
return updatedHistory;
});
}
}, [
response,
allActions,
lastKey,
lastCallId,
messages.length,
lastPrompt,
lastMessages,
initialPrompt,
processThinkingTags
]);
const attachButtonHandlers = useCallback(
(attachments, retryCount = 0) => {
if (attachments.length === 0) return;
let attachedCount = 0;
let notFoundCount = 0;
let alreadyAttachedCount = 0;
const stillPending = [];
const allButtonsInDOM = document.querySelectorAll("button");
const buttonIdsInDOM = Array.from(allButtonsInDOM).map((btn) => btn.id).filter((id) => id);
attachments.forEach(({ buttonId, action, match, groups }) => {
const button = document.getElementById(buttonId);
if (button) {
if (!button.onclick) {
button.onclick = () => {
if (action.callback) {
action.callback(match, groups);
}
if (action.clickCode) {
try {
const func = new Function("match", action.clickCode);
func(match);
if (typeof interactionClicked === "function") {
interactionClicked(lastCallId, "action");
}
} catch (error) {
console.error("Error executing clickCode:", error);
}
}
};
attachedCount++;
} else {
alreadyAttachedCount++;
}
} else {
notFoundCount++;
stillPending.push({ buttonId, action, match, groups });
buttonActionRegistry.current.set(buttonId, { action, match, groups });
}
});
if (stillPending.length > 0 && retryCount < 5) {
setTimeout(() => {
attachButtonHandlers(stillPending, retryCount + 1);
}, 200);
} else if (stillPending.length > 0) {
console.warn(
"Failed to attach all buttons after max retries:",
stillPending.map((p) => p.buttonId)
);
}
},
[lastCallId]
);
useEffect2(() => {
if (pendingButtonAttachments.length > 0) {
requestAnimationFrame(() => {
setTimeout(() => {
attachButtonHandlers([...pendingButtonAttachments]);
setPendingButtonAttachments([]);
}, 100);
});
}
}, [pendingButtonAttachments, attachButtonHandlers]);
useEffect2(() => {
if (typeof window === "undefined") return;
const observer = new MutationObserver((mutations) => {
let newButtonsFound = false;
mutations.forEach((mutation) => {
if (mutation.type === "childList") {
mutation.addedNodes.forEach((node) => {
var _a2;
if (node.nodeType === Node.ELEMENT_NODE) {
const element = node;
const actionButtons = ((_a2 = element.id) == null ? void 0 : _a2.startsWith("button-")) ? [element] : Array.from(element.querySelectorAll('button[id^="button-"]'));
if (actionButtons.length > 0) {
newButtonsFound = true;
actionButtons.forEach((button) => {
if (!button.onclick) {
console.log(
"Found unattached button via MutationObserver:",
button.id
);
}
});
}
}
});
}
});
});
const responseArea = responseAreaRef.current;
if (responseArea) {
observer.observe(responseArea, {
childList: true,
subtree: true
});
}
return () => observer.disconnect();
}, []);
useEffect2(() => {
if (typeof window === "undefined") return;
const handleDelegatedClick = (event) => {
const target = event.target;
if (target.tagName === "BUTTON" && target.id && target.id.startsWith("button-")) {
const buttonData = buttonActionRegistry.current.get(target.id);
if (buttonData) {
const { action, match, groups } = buttonData;
const button = target;
if (!button.onclick) {
event.preventDefault();
event.stopPropagation();
if (action.callback) {
action.callback(match, groups);
}
if (action.clickCode) {
try {
const func = new Function("match", action.clickCode);
func(match);
if (typeof interactionClicked === "function") {
interactionClicked(lastCallId, "action");
}
} catch (error) {
console.error(
"Error executing clickCode via delegation:",
error
);
}
}
buttonActionRegistry.current.delete(target.id);
}
}
}
};
document.addEventListener("click", handleDelegatedClick, false);
return () => {
document.removeEventListener("click", handleDelegatedClick, false);
};
}, [lastCallId]);
useEffect2(() => {
if (typeof window !== "undefined") {
window.debugChatPanelButtons = () => {
const allButtons = document.querySelectorAll('button[id^="button-"]');
const allButtonsAny = document.querySelectorAll("button");
const buttonInfo = Array.from(allButtons).map((button) => {
var _a2, _b;
return {
id: button.id,
hasOnclick: !!button.onclick,
textContent: (_a2 = button.textContent) == null ? void 0 : _a2.substring(0, 50),
visible: button.offsetParent !== null,
inDOM: document.contains(button),
parentElement: (_b = button.parentElement) == null ? void 0 : _b.tagName,
outerHTML: button.outerHTML.substring(0, 200),
inRegistry: buttonActionRegistry.current.has(button.id)
};
});
return buttonInfo;
};
}
}, [pendingButtonAttachments, history]);
function hasVerticalScrollbar(element) {
return element.scrollHeight > element.clientHeight;
}
const autoResizeTextarea = useCallback(() => {
if (textareaRef.current) {
const textarea = textareaRef.current;
textarea.style.height = "auto";
const minHeight = 40;
const maxHeight = 120;
const newHeight = Math.min(
Math.max(textarea.scrollHeight, minHeight),
maxHeight
);
textarea.style.height = `${newHeight}px`;
}
}, []);
useEffect2(() => {
autoResizeTextarea();
}, [nextPrompt, autoResizeTextarea]);
useEffect2(() => {
if (initialPrompt && initialPrompt !== "") {
if (initialPrompt !== lastPrompt) {
setIsLoading(true);
setThinkingBlocks([]);
setCurrentThinkingIndex(0);
ensureConversation().then((convId) => {
if (lastController) stop(lastController);
const controller = new AbortController();
send(
initialPrompt,
messages,
[
...dataWithExtras(),
{
key: "--messages",
data: messages.length.toString()
}
],
true,
true,
service,
convId,
controller
);
setLastPrompt(initialPrompt);
setLastMessages(messages);
setLastKey(initialPrompt);
setLastController(controller);
setHistory({});
buttonActionRegistry.current.clear();
});
}
}
}, [initialPrompt]);
useEffect2(() => {
var _a2;
if (scrollToEnd) {
if (window.top !== window.self) {
const responseArea = responseAreaRef.current;
responseArea.scrollTo({
top: responseArea.scrollHeight,
behavior: "smooth"
});
} else {
(_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth", block: "end" });
}
} else {
const responseArea = responseAreaRef.current;
if (responseArea) {
setHasScroll(hasVerticalScrollbar(responseArea));
const handleScroll = () => {
const isScrolledToBottom = responseArea.scrollHeight - responseArea.scrollTop === responseArea.clientHeight;
setIsAtBottom(isScrolledToBottom);
};
handleScroll();
responseArea.addEventListener("scroll", handleScroll);
return () => responseArea.removeEventListener("scroll", handleScroll);
}
}
}, [response, history]);
useEffect2(() => {
if (historyChangedCallback) {
historyChangedCallback(history);
}
}, [history, historyChangedCallback]);
useEffect2(() => {
console.log("followOnPromptChanged detected");
if (followOnPrompt && followOnPrompt !== "") {
continueChat(followOnPrompt);
}
}, [followOnPrompt]);
const setCookie = (name, value, days = 30) => {
const date = /* @__PURE__ */ new Date();
date.setTime(date.getTime() + days * 24 * 60 * 60 * 1e3);
const expires = `; expires=${date.toUTCString()}`;
const sameSite = "; SameSite=Lax";
const secure = window.location.protocol === "https:" ? "; Secure" : "";
document.cookie = `${name}=${value}${expires}; path=/${sameSite}${secure}`;
};
const getCookie = (name) => {
var _a2;
const value = `; ${document.cookie}`;
const parts = value.split(`; ${name}=`);
if (parts.length === 2) {
return (_a2 = parts.pop()) == null ? void 0 : _a2.split(";").shift();
}
return void 0;
};
const ensureConversation = () => {
var _a2, _b;
if ((!currentConversation || currentConversation === "") && createConversationOnFirstChat) {
const browserInfo2 = getBrowserInfo();
return fetch(`${publicAPIUrl}/conversations`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
project_id: project_id != null ? project_id : "",
agentId: agent,
customerId: (_a2 = currentCustomer == null ? void 0 : currentCustomer.customer_id) != null ? _a2 : null,
customerEmail: (_b = currentCustomer == null ? void 0 : currentCustomer.customer_user_email) != null ? _b : null,
timezone: browserInfo2 == null ? void 0 : browserInfo2.userTimezone,
language: browserInfo2 == null ? void 0 : browserInfo2.userLanguage
})
}).then((res) => __async(void 0, null, function* () {
if (!res.ok) {
const errorText = yield res.text();
throw new Error(
`HTTP error! status: ${res.status}, message: ${errorText}`
);
}
return res.json();
})).then((newConvo) => {
if (newConvo == null ? void 0 : newConvo.id) {
console.log("new conversation created", newConvo.id);
setCurrentConversation(newConvo.id);
return newConvo.id;
}
return "";
}).catch((error) => {
console.error("Error creating new conversation", error);
return "";
});
}
return Promise.resolve(currentConversation);
};
useEffect2(() => {
var _a2, _b, _c, _d, _e;
const isEmpty = (value) => {
return !value || value.trim() === "" || value.trim() === "undefined";
};
let updatedValues = __spreadValues({}, currentCustomer);
let needsUpdate = false;
if (!isEmpty(currentCustomer == null ? void 0 : currentCustomer.customer_user_email) && isEmailAddress((_a2 = currentCustomer.customer_user_email) != null ? _a2 : "")) {
setCookie(
"llmasaservice-panel-customer-user-email",
(_b = currentCustomer == null ? void 0 : currentCustomer.customer_user_email) != null ? _b : ""
);
if (emailInput !== currentCustomer.customer_user_email) {
setEmailInput((_c = currentCustomer.customer_user_email) != null ? _c : "");
setEmailInputSet(true);
setEmailValid(true);
}
}
if (isEmpty(currentCustomer == null ? void 0 : currentCustomer.customer_user_email)) {
const cookieEmail = getCookie("llmasaservice-panel-customer-user-email");
if (!isEmpty(cookieEmail) && isEmailAddress(cookieEmail != null ? cookieEmail : "")) {
updatedValues.customer_user_email = cookieEmail;
needsUpdate = true;
}
}
if (isEmpty(currentCustomer == null ? void 0 : currentCustomer.customer_id) && !isEmpty(updatedValues.customer_user_email) && isEmailAddress((_d = updatedValues.customer_user_email) != null ? _d : "")) {
updatedValues.customer_id = (_e = updatedValues.customer_user_email) != null ? _e : "";
needsUpdate = true;
}
if (needsUpdate && (updatedValues.customer_id !== currentCustomer.customer_id || updatedValues.customer_user_email !== currentCustomer.customer_user_email)) {
ensureConversation().then((convId) => {
if (convId && convId !== "") {
console.log(
"updating conversation with customer id and email",
convId,
updatedValues
);
fetch(`${publicAPIUrl}/conversations/${convId}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
project_id: project_id != null ? project_id : "",
customerId: updatedValues.customer_id,
customerEmail: updatedValues.customer_user_email
})
}).catch(
(error) => console.error("Failed to update conversation:", error)
);
}
});
setCurrentCustomer(updatedValues);
}
}, [currentCustomer, project_id, agent, publicAPIUrl, emailInput]);
const continueChat = (suggestion) => {
console.log("continueChat called");
setThinkingBlocks([]);
setCurrentThinkingIndex(0);
if (emailInput && isEmailAddress(emailInput) && !emailInputSet) {
const newId = (currentCustomer == null ? void 0 : currentCustomer.customer_id) && currentCustomer.customer_id !== "" && currentCustomer.customer_id !== (currentCustomer == null ? void 0 : currentCustomer.customer_user_email) ? currentCustomer.customer_id : emailInput;
setEmailInputSet(true);
setEmailValid(true);
setCurrentCustomer({
customer_id: newId,
customer_user_email: emailInput
});
}
ensureConversation().then((convId) => {
var _a2, _b, _c;
console.log("current customer", currentCustomer);
if (!idle) {
stop(lastController);
setHistory((prevHistory) => {
return __spreadProps(__spreadValues({}, prevHistory), {
[lastKey != null ? lastKey : ""]: {
content: processThinkingTags(response).cleanedText + "\n\n(response cancelled)",
callId: lastCallId
}
});
});
return;
}
if (clearFollowOnQuestionsNextPrompt) {
setFollowOnQuestionsState([]);
}
if (suggestion && suggestion !== "" || nextPrompt && nextPrompt !== "") {
setIsLoading(true);
const messagesAndHistory = Object.keys(history).length === 0 ? [] : messages;
Object.entries(history).forEach(([prompt, response2]) => {
var _a3, _b2, _c2;
let promptToSend = prompt;
if (prompt.includes(":")) {
const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
if (isoTimestampRegex.test(prompt)) {
const colonIndex = prompt.indexOf(":", 19);
promptToSend = prompt.substring(colonIndex + 1);
} else if (/^\d+:/.test(prompt)) {
const colonIndex = prompt.indexOf(":");
promptToSend = prompt.substring(colonIndex + 1);
}
}
if (promptTemplate && promptTemplate !== "") {
promptToSend = promptTemplate.replace("{{prompt}}", promptToSend);
for (let i = 0; i < data.length; i++) {
promptToSend = promptToSend.replace(
"{{" + ((_a3 = data[i]) == null ? void 0 : _a3.key) + "}}",
(_c2 = (_b2 = data[i]) == null ? void 0 : _b2.data) != null ? _c2 : ""
);
}
}
messagesAndHistory.push({ role: "user", content: promptToSend });
messagesAndHistory.push({
role: "assistant",
content: response2.content
});
});
let nextPromptToSend = suggestion != null ? suggestion : nextPrompt;
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
let promptKey = `${timestamp}:${nextPromptToSend != null ? nextPromptToSend : ""}`;
setHistory((prevHistory) => {
return __spreadProps(__spreadValues({}, prevHistory), {
[promptKey != null ? promptKey : ""]: { content: "", callId: "" }
});
});
if (initialPrompt && initialPrompt !== "" && Object.keys(history).length === 1 || (!initialPrompt || initialPrompt === "") && Object.keys(history).length === 0) {
if (promptTemplate && promptTemplate !== "") {
nextPromptToSend = promptTemplate.replace(
"{{prompt}}",
nextPromptToSend
);
for (let i = 0; i < data.length; i++) {
nextPromptToSend = nextPromptToSend.replace(
"{{" + ((_a2 = data[i]) == null ? void 0 : _a2.key) + "}}",
(_c = (_b = data[i]) == null ? void 0 : _b.data) != null ? _c : ""
);
}
}
}
console.log("Sending for conversation", convId);
const controller = new AbortController();
send(
nextPromptToSend,
messagesAndHistory,
[
...dataWithExtras(),
{
key: "--messages",
data: messagesAndHistory.length.toString()
}
],
true,
true,
service,
convId,
controller
);
setLastPrompt(nextPromptToSend);
setLastMessages(messagesAndHistory);
setLastKey(promptKey);
setLastController(controller);
setNextPrompt("");
}
});
};
const replaceHistory = (newHistory) => {
setHistory(newHistory);
};
function copyToClipboard(text) {
navigator.clipboard.writeText(text);
interactionClicked(lastCallId, "copy");
}
const scrollToBottom = () => {
var _a2;
if (window.top !== window.self) {
const responseArea = responseAreaRef.current;
responseArea.scrollTo({
top: responseArea.scrollHeight,
behavior: "smooth"
});
} else {
(_a2 = bottomRef.current) == null ? void 0 : _a2.scrollIntoView({ behavior: "smooth" });
}
};
const CodeBlock = (_b) => {
var _c = _b, { node, className, children, style } = _c, props = __objRest(_c, ["node", "className", "children", "style"]);
const match = /language-(\w+)/.exec(className || "");
return match ? /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(
"div",
{
style: {
border: 0,
padding: 0,
height: "16px",
display: "flex",
justifyContent: "space-between",
alignItems: "center"
}
},
/* @__PURE__ */ React3.createElement("span", null, match ? match[1] : "Unknown"),
/* @__PURE__ */ React3.createElement(
"button",
{
onClick: () => copyToClipboard(children),
className: "copy-button"
},
/* @__PURE__ */ React3.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 320 320",
fill: "currentColor",
className: "icon-svg"
},
/* @__PURE__ */ React3.createElement(
"path",
{
d: "M35,270h45v45c0,8.284,6.716,15,15,15h200c8.284,0,15-6.716,15-15V75c0-8.284-6.716-15-15-15h-45V15\r\n c0-8.284-6.716-15-15-15H35c-8.284,0-15,6.716-15,15v240C20,263.284,26.716,270,35,270z M280,300H110V90h170V300z M50,30h170v30H95\r\n c-8.284,0-15,6.716-15,15v165H50V30z"
}
),
/* @__PURE__ */ React3.createElement("path", { d: "M155,120c-8.284,0-15,6.716-15,15s6.716,15,15,15h80c8.284,0,15-6.716,15-15s-6.716-15-15-15H155z" }),
/* @__PURE__ */ React3.createElement("path", { d: "M235,180h-80c-8.284,0-15,6.716-15,15s6.716,15,15,15h80c8.284,0,15-6.716,15-15S243.284,180,235,180z" }),
/* @__PURE__ */ React3.createElement(
"path",
{
d: "M235,240h-80c-8.284,0-15,6.716-15,15c0,8.284,6.716,15,15,15h80c8.284,0,15-6.716,15-15C250,246.716,243.284,240,235,240z\r\n "
}
)
)
)
), /* @__PURE__ */ React3.createElement(
SyntaxHighlighter,
__spreadValues({
style: prismStyle,
PreTag: "div",
language: match[1]
}, props),
String(children).replace(/\n$/, "")
)) : /* @__PURE__ */ React3.createElement("code", __spreadValues({ className: className ? className : "" }, props), children);
};
const CustomLink = (_d) => {
var _e = _d, { href, children } = _e, props = __objRest(_e, ["href", "children"]);
return /* @__PURE__ */ React3.createElement("a", __spreadValues({ href, target: "_blank", rel: "noopener noreferrer" }, props), children);
};
const convertMarkdownToHTML = (markdown) => {
const html = ReactDOMServer.renderToStaticMarkup(
/* @__PURE__ */ React3.createElement(
ReactMarkdown,
{
className: markdownClass,
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeRaw]
},
markdown
)
);
return html;
};
const convertHistoryToHTML = (history2) => {
const stylesheet = `
<style>
.conversation-history {
font-family: Arial, sans-serif;
line-height: 1.5; /* Slightly increase line height for readability */
}
.history-entry {
margin-bottom: 15px;
}
.prompt-container, .response-container {
margin-bottom: 10px; /* Adjusted spacing */
}
.prompt, .response {
display: block; /* Ensure they take up the full row */
margin: 5px 0; /* Add vertical spacing */
padding: 10px; /* Increase padding for better spacing */
border-radius: 5px;
max-width: 80%; /* Keep width constrained */
}
.prompt {
background-color: #efefef;
margin-left: 0; /* Align to the left */
}
.response {
background-color: #f0fcfd;
margin-left: 25px; /* Indent slightly for visual differentiation */
}
</style>
`;
let html = `
<html>
<head>
${stylesheet}
</head>
<body>
<h1>Conversation History (${(/* @__PURE__ */ new Date()).toLocaleString()})</h1>
<div class="conversation-history">
`;
Object.entries(history2).forEach(([prompt, response2], index) => {
if (hideInitialPrompt && index === 0) {
html += `
<div class="history-entry">
<div class="response-container">
<div class="response">${convertMarkdownToHTML(
response2.content
)}</div>
</div>
</div>
`;
} else {
html += `
<div class="history-entry">
<div class="prompt-container">
<div class="prompt">${convertMarkdownToHTML(
formatPromptForDisplay(prompt)
)}</div>
</div>
<div class="response-container">
<div class="response">${convertMarkdownToHTML(response2.content)}</div>
</div>
</div>
`;
}
});
html += `
</div>
</body>
</html>
`;
return html;
};
const saveHTMLToFile = (html, filename) => {
const blob = new Blob([html], { type: "text/html" });
const link = document.createElement("a");
link.href = URL.createObjectURL(blob);
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
};
const handleSendEmail = (to, from) => {
sendConversationsViaEmail(to, `Conversation History from ${title}`, from);
interactionClicked(lastCallId, "email", to);
setEmailSent(true);
};
const handleSuggestionClick = (suggestion) => __async(void 0, null, function* () {
continueChat(suggestion);
yield interactionClicked(lastCallId, "suggestion");
});
const sendConversationsViaEmail = (_0, ..._1) => __async(void 0, [_0, ..._1], function* (to, subject = `Conversation History from ${title}`, from = "") {
fetch(`${publicAPIUrl}/share/email`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
to,
from,
subject,
html: convertHistoryToHTML(history),
project_id: project_id != null ? project_id : "",
customer: currentCustomer,
history,
title
})
});
yield interactionClicked(lastCallId, "email", from);
});
const sendCallToActionEmail = (from) => __async(void 0, null, function* () {
const r = yield fetch(`${publicAPIUrl}/share/email`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
to: callToActionEmailAddress,
from,
subject: `${callToActionEmailSubject} from ${from}`,
html: convertHistoryToHTML(history),
project_id: project_id != null ? project_id : "",
customer: currentCustomer,
history,
title
})
});
yield interactionClicked(lastCallId, "cta", from);
setCallToActionSent(true);
});
const interactionClicked = (callId, action, emailaddress = "", comment = "") => __async(void 0, null, function* () {
console.log(`Interaction clicked: ${action} for callId: ${callId}`);
ensureConversation().then((convId) => {
var _a2, _b;
if (!callId || callId === "") callId = convId;
const email = emailaddress && emailaddress !== "" ? emailaddress : isEmailAddress((_a2 = currentCustomer == null ? void 0 : currentCustomer.customer_user_email) != null ? _a2 : "") ? currentCustomer == null ? void 0 : currentCustomer.customer_user_email : isEmailAddress((_b = currentCustomer == null ? void 0 : currentCustomer.customer_id) != null ? _b : "") ? currentCustomer == null ? void 0 : currentCustomer.customer_id : "";
fetch(`${publicAPIUrl}/feedback/${callId}/${action}`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
project_id: project_id != null ? project_id : "",
conversation_id: convId != null ? convId : "",
email,
comment
})
});
});
});
const formatPromptForDisplay = (prompt) => {
if (!prompt) {
return "";
}
let cleanedPrompt = prompt;
if (prompt.includes(":")) {
const isoTimestampRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z:/;
if (isoTimestampRegex.test(prompt)) {
const colonIndex = prompt.indexOf(":", 19);
cleanedPrompt = prompt.substring(colonIndex + 1);
} else if (/^\d+:/.test(prompt)) {
const colonIndex = prompt.indexOf(":");
cleanedPrompt = prompt.substring(colonIndex + 1);
}
}
if (hideRagContextInPrompt && cleanedPrompt.includes("CONTEXT:")) {
const parts = cleanedPrompt.split("CONTEXT:");
const withoutContext = parts.length > 0 ? parts[0] : "";
if (withoutContext.includes("PROMPT:")) {
const promptParts = withoutContext.split("PROMPT:");
return promptParts.length > 1 ? (promptParts[1] || "").trim() : withoutContext.trim();
}
return withoutContext.trim();
}
return cleanedPrompt;
};
const isDisabledDueToNoEmail = () => {
const valid = isEmailAddress(emailInput);
if (valid) return false;
if (customerEmailCaptureMode === "REQUIRED") return true;
return false;
};
const getUniqueToolNames = (reqs) => Array.from(new Set(reqs.map((r) => r.toolName)));
const handleToolApproval = (toolName, scope) => {
if (scope === "session" || scope === "always") {
setSessionApprovedTools((p) => Array.from(/* @__PURE__ */ new Set([...p, toolName])));
}
if (scope === "always") {
setAlwaysApprovedTools((p) => Array.from(/* @__PURE__ */ new Set([...p, toolName])));
}
const requestsToRun = pendingToolRequests.filter(
(r) => r.toolName === toolName
);
processGivenToolRequests(requestsToRun);
setPendingToolRequests((p) => p.filter((r) => r.toolName !== toolName));
};
useEffect2(() => {
if (pendingToolRequests.length === 0) return;
const toAuto = pendingToolRequests.filter(
(r) => sessionApprovedTools.includes(r.toolName) || alwaysApprovedTools.includes(r.toolName)
);
if (toAuto.length > 0) {
processGivenToolRequests(toAuto);
setPendingToolRequests(
(prev) => prev.filter(
(r) => !sessionApprovedTools.includes(r.toolName) && !alwaysApprovedTools.includes(r.toolName)
)
);
}
}, [pendingToolRequests, sessionApprovedTools, alwaysApprovedTools]);
return /* @__PURE__ */ React3.createElement(React3.Fragment, null, /* @__PURE__ */ React3.createElement(
"div",
{
style: { width, height },
className: "llm-panel" + (theme === "light" ? "" : " dark-theme")
},
title && title !== "" ? /* @__PURE__ */ React3.createElement("div", { className: "title" }, title) : null,
/* @__PURE__ */ React3.createElement("div", { className: "responseArea", ref: responseAreaRef }, initialMessage && initialMessage !== "" ? /* @__PURE__ */ React3.createElement("div", { className: "history-entry" }, /* @__PURE__ */ React3.createElement("div", { className: "response" }, /* @__PURE__ */ React3.createElement(
ReactMarkdown,
{
className: markdownClass,
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeRaw]
},
initialMessage
))) : null, Object.entries(history).map(([prompt, historyEntry], index) => {
var _a2, _b;
const isLastEntry = index === Object.keys(history).length - 1;
const hasToolData = !!((((_a2 = historyEntry == null ? void 0 : historyEntry.toolCalls) == null ? void 0 : _a2.length) || 0) > 0 || (((_b = historyEntry == null ? void 0 : historyEntry.toolResponses) == null ? void 0 : _b.length) || 0) > 0);
return /* @__PURE__ */ React3.createElement("div", { className: "history-entry", key: index }, hideInitialPrompt && index === 0 ? null : /* @__PURE__ */ React3.createElement("div", { className: "prompt" }, formatPromptForDisplay(prompt)), /* @__PURE__ */ React3.createElement("div", { className: "response" }, index === Object.keys(history).length - 1 && (isLoading || !idle) && !justReset ? /* @__PURE__ */ React3.createElement("div", { className: "streaming-response" }, (() => {
const { cleanedText } = processThinkingTags(
response || ""
);
if (thinkingBlocks.length > 0) {
const isOnLastBlock = currentThinkingIndex === thinkingBlocks.length - 1;
const hasMainContent = cleanedText && cleanedText.trim().length > 0;
const shouldShowLoading = isOnLastBlock && !hasMainContent;
return /* @__PURE__ */ React3.createElement("div", null, renderThinkingBlocks(), shouldShowLoading && /* @__PURE__ */ React3.createElement("div", { className: "loading-text" }, "Thinking...\xA0", /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" })));
}
if (!cleanedText || cleanedText.length === 0) {
return /* @__PURE__ */ React3.createElement("div", { className: "loading-text" }, "Thinking...\xA0", /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" }), /* @__PURE__ */ React3.createElement("div", { className: "dot" }));
}
return null;
})(), (() => {
const { cleanedText } = processThinkingTags(
response || ""
);
return cleanedText && cleanedText.length > 0 ? /* @__PURE__ */ React3.createElement(
ReactMarkdown,
{
className: markdownClass,
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeRaw],
components: {
/*a: CustomLink,*/
code: CodeBlock
}
},
cleanedText
) : null;
})()) : /* @__PURE__ */ React3.createElement("div", null, isLastEntry && thinkingBlocks.length > 0 && renderThinkingBlocks(), /* @__PURE__ */ React3.createElement(
ReactMarkdown,
{
className: markdownClass,
remarkPlugins: [remarkGfm],
rehypePlugins: [rehypeRaw],
components: {
/*a: CustomLink,*/
code: CodeBlock
}
},
processThinkingTags(historyEntry.content).cleanedText
)), isLastEntry && pendingToolRequests.length > 0 && /* @__PURE__ */ React3.createElement("div", { className: "approve-tools-panel" }, getUniqueToolNames(pendingToolRequests).map(
(toolName) => {
const tool = toolList.find(
(t) => t.name === toolName
);
return /* @__PURE__ */ React3.createElement("div", { key: toolName, className: "approve-tool-item" }, /* @__PURE__ */ React3.createElement("div", { className: "approve-tools-header" }, "Tool \u201C", toolName, "\u201D requires approval ", /* @__PURE__ */ React3.createElement("br", null)), /* @__PURE__ */ React3.createElement("div", { className: "approve-tools-buttons" }, /* @__PURE__ */ React3.createElement(
"button",
{
className: "approve-tools-button",
onClick: () => handleToolApproval(toolName, "once"),
disabled: isLoading
},
"Approve Once"
), /* @__PURE__ */ React3.createElement(
"button",
{
className: "approve-tools-button",
onClick: () => handleToolApproval(toolName, "session"),
disabled: isLoading
},
"Approve This Chat"
), /* @__PURE__ */ React3.createElement(
"button",
{
className: "approve-tools-button",
onClick: () => handleToolApproval(toolName, "always"),
disabled: isLoading
},
"Approve Always"
), /* @__PURE__ */ React3.createElement("br", null)), (tool == null ? void 0 : tool.description) && /* @__PURE__ */ React3.createElement("div", { className: "approve-tools-description" }, tool.description));
}
)), idle && !isLoading && pendingToolRequests.length === 0 && /* @__PURE__ */ React3.createElement("div", { className: "button-container" }, /* @__PURE__ */ React3.createElement(
"button",
{
className: "copy-button",
onClick: () => {
copyToClipboard(historyEntry.content);
},
disabled: isDisabledDueToNoEmail()
},
/* @__PURE__ */ React3.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 320 320",
fill: "currentColor",
className: "icon-svg"
},
/* @__PURE__ */ React3.createElement(
"path",
{
d: "M35,270h45v45c0,8.284,6.716,15,15,15h200c8.284,0,15-6.716,15-15V75c0-8.284-6.716-15-15-15h-45V15\r\n c0-8.284-6.716-15-15-15H35c-8.284,0-15,6.716-15,15v240C20,263.284,26.716,270,35,270z M280,300H110V90h170V300z M50,30h170v30H95\r\n c-8.284,0-15,6.716-15,15v165H50V30z"
}
),
/* @__PURE__ */ React3.createElement("path", { d: "M155,120c-8.284,0-15,6.716-15,15s6.716,15,15,15h80c8.284,0,15-6.716,15-15s-6.716-15-15-15H155z" }),
/* @__PURE__ */ React3.createElement("path", { d: "M235,180h-80c-8.284,0-15,6.716-15,15s6.716,15,15,15h80c8.284,0,15-6.716,15-15S243.284,180,235,180z" }),
/* @__PURE__ */ React3.createElement(
"path",
{
d: "M235,240h-80c-8.284,0-15,6.716-15,15c0,8.284,6.716,15,15,15h80c8.284,0,15-6.716,15-15C250,246.716,243.284,240,235,240z\r\n "
}
)
)
), /* @__PURE__ */ React3.createElement(
"button",
{
className: "thumbs-button",
onClick: () => {
if (thumbsUpClick) thumbsUpClick(historyEntry.callId);
interactionClicked(historyEntry.callId, "thumbsup");
},
disabled: isDisabledDueToNoEmail()
},
/* @__PURE__ */ React3.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 20 20",
fill: "currentColor",
className: "icon-svg"
},
/* @__PURE__ */ React3.createElement("path", { d: "M20.22 9.55C19.79 9.04 19.17 8.75 18.5 8.75H14.47V6C14.47 4.48 13.24 3.25 11.64 3.25C10.94 3.25 10.31 3.67 10.03 4.32L7.49 10.25H5.62C4.31 10.25 3.25 11.31 3.25 12.62V18.39C3.25 19.69 4.32 20.75 5.62 20.75H17.18C18.27 20.75 19.2 19.97 19.39 18.89L20.71 11.39C20.82 10.73 20.64 10.06 20.21 9.55H20.22ZM5.62 19.25C5.14 19.25 4.75 18.86 4.75 18.39V12.62C4.75 12.14 5.14 11.75 5.62 11.75H7.23V19.25H5.62ZM17.92 18.63C17.86 18.99 17.55 19.25 17.18 19.25H8.74V11.15L11.41 4.9C11.45 4.81 11.54 4.74 11.73 4.74C12.42 4.74 12.97 5.3 12.97 5.99V10.24H18.5C18.73 10.24 18.93 10.33 19.07 10.5C19.21 10.67 19.27 10.89 19.23 11.12L17.91 18.62L17.92 18.63Z" })
)
), /* @__PURE__ */ React3.createElement(
"button",
{
className: "thumbs-button",
onClick: () => {
if (thumbsDownClick)
thumbsDownClick(historyEntry.callId);
interactionClicked(historyEntry.callId, "thumbsdown");
},
disabled: isDisabledDueToNoEmail()
},
/* @__PURE__ */ React3.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 20 20",
fill: "currentColor",
className: "icon-svg"
},
/* @__PURE__ */ React3.createElement("path", { d: "M18.38 3.25H6.81C5.72 3.25 4.79 4.03 4.6 5.11L3.29 12.61C3.18 13.27 3.36 13.94 3.78 14.45C4.21 14.96 4.83 15.25 5.5 15.25H9.53V18C9.53 19.52 10.76 20.75 12.36 20.75C13.06 20.75 13.69 20.33 13.97 19.68L16.51 13.75H18.39C19.7 13.75 20.76 12.69 20.76 11.38V5.61C20.76 4.31 19.7 3.25 18.39 3.25H18.38ZM15.26 12.85L12.59 19.1C12.55 19.19 12.46 19.26 12.27 19.26C11.58 19.26 11.03 18.7 11.03 18.01V13.76H5.5C5.27 13.76 5.07 13.67 4.93 13.5C4.78 13.33 4.73 13.11 4.77 12.88L6.08 5.38C6.14 5.02 6.45001 4.76 6.82 4.76H15.26V12.85ZM19.25 11.38C19.25 11.86 18.86 12.25 18.38 12.25H16.77V4.75H18.38C18.86 4.75 19.25 5.14 19.25 5.61V11.38Z" })
)
), (idle || hasToolData) && /* @__PURE__ */ React3.createElement(
"button",
{
className: "copy-button",
title: "Show Tool Call/Response JSON",
onClick: () => {
var _a3, _b2;
const historyEntry2 = history[prompt];
setToolInfoData({
calls: (_a3 = historyEntry2 == null ? void 0 : historyEntry2.toolCalls) != null ? _a3 : [],
responses: (_b2 = historyEntry2 == null ? void 0 : historyEntry2.toolResponses) != null ? _b2 : []
});
setIsToolInfoModalOpen(true);
}
},
/* @__PURE__ */ React3.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 24 24",
fill: "currentColor",
className: "icon-svg"
},
/* @__PURE__ */ React3.createElement("path", { d: "M9.4 16.6L4.8 12l4.6-4.6L8 6l-6 6 6 6 1.4-1.4zm5.2 0l4.6-4.6-4.6-4.6L16 6l6 6-6 6-1.4-1.4z" })
)
))));
}), /* @__PURE__ */ React3.createElement(
ToolInfoModal_default,
{
isOpen: isToolInfoModalOpen,
onClose: () => setIsToolInfoModalOpen(false),
data: toolInfoData
}
), followOnQuestionsState && followOnQuestionsState.length > 0 && idle && !isLoading && pendingToolRequests.length === 0 && /* @__PURE__ */ React3.createElement("div", { className: "suggestions-container" }, followOnQuestionsState.map((question, index) => /* @__PURE__ */ React3.createElement(
"button",
{
key: index,
className: "suggestion-button",
onClick: () => handleSuggestionClick(question),
disabled: !idle || isDisabledDueToNoEmail()
},
question
))), /* @__PURE__ */ React3.createElement("div", { ref: bottomRef }), hasScroll && !isAtBottom && /* @__PURE__ */ React3.createElement("button", { className: "scroll-button", onClick: scrollToBottom }, "\u2193")),
showEmailPanel && /* @__PURE__ */ React3.createElement(React3.Fragment, null, !emailValid && /* @__PURE__ */ React3.createElement("div", { className: "email-input-message" }, isDisabledDueToNoEmail() ? "Let's get started - please enter your email" : CTAClickedButNoEmail || emailClickedButNoEmail ? "Sure, we just need an email address to contact you" : "Email address is invalid"), /* @__PURE__ */ React3.createElement("div", { className: "email-input-container" }, /* @__PURE__ */ React3.createElement(
"input",
{
type: "email",
name: "email",
id: "email",
className: emailValid ? emailInputSet ? "email-input-set" : "email-input" : "email-input-invalid",
placeholder: customerEmailCapturePlaceholder,
value: emailInput,
onChange: (e) => {
const newEmail = e.target.value;
setEmailInput(newEmail);
if (!emailInputSet) {
if (customerEmailCaptureMode === "REQUIRED" && newEmail !== "") {
setEmailValid(isEmailAddress(newEmail));
} else {
setEmailValid(true);
}
}
},
onBlur: () => {
if (emailInput && isEmailAddress(emailInput) && !emailInputSet) {
const newId = (currentCustomer == null ? void 0 : currentCustomer.customer_id) && currentCustomer.customer_id !== "" && currentCustomer.customer_id !== (currentCustomer == null ? void 0 : currentCustomer.customer_user_email) ? currentCustomer.customer_id : emailInput;
setEmailInputSet(true);
setEmailValid(true);
setCurrentCustomer({
customer_id: newId,
customer_user_email: emailInput
});
if (CTAClickedButNoEmail) {
sendCallToActionEmail(emailInput);
setCTAClickedButNoEmail(false);
}
if (emailClickedButNoEmail) {
handleSendEmail(emailInput, emailInput);
setEmailClickedButNoEmail(false);
}
} else if (customerEmailCaptureMode === "REQUIRED" && emailInput !== "") {
setEmailValid(isEmailAddress(emailInput));
}
},
disabled: false
}
), emailInputSet && /* @__PURE__ */ React3.createElement(
"button",
{
className: "email-input-button",
onClick: () => {
setEmailInputSet(false);
setEmailValid(true);
},
title: "Edit email"
},
"\u270E"
))),
/* @__PURE__ */ React3.createElement("div", { className: "button-container-actions" }, showNewConversationButton && /* @__PURE__ */ React3.createElement(
"button",
{
className: `save-button new-conversation-button ${newConversationConfirm ? "confirm-state" : ""}`,
onClick: () => {
if (!newConversationConfirm) {
setNewConversationConfirm(true);
setTimeout(() => {
setNewConversationConfirm(false);
}, 3e3);
} else {
setNewConversationConfirm(false);
if (!idle) {
stop(lastController);
}
setResponse("");
setHistory({});
setLastMessages([]);
setNextPrompt("");
setLastPrompt(null);
setLastKey(null);
setIsLoading(false);
setCurrentConversation(null);
setCallToActionSent(false);
setEmailSent(false);
setCTAClickedButNoEmail(false);
setEmailClickedButNoEmail(false);
setFollowOnQuestionsState(followOnQuestions);
setSessionApprovedTools([]);
setThinkingBlocks([]);
setCurrentThinkingIndex(0);
setPendingButtonAttachments([]);
setPendingToolRequests([]);
setIsEmailModalOpen(false);
setIsToolInfoModalOpen(false);
setToolInfoData(null);
setJustReset(true);
setLastController(new AbortController());
buttonActionRegistry.current.clear();
setTimeout(() => {
setJustReset(false);
const responseArea = responseAreaRef.current;
if (responseArea) {
responseArea.scrollTo({
top: 0,
behavior: "smooth"
});
}
}, 100);
}
},
title: newConversationConfirm ? "Click again to confirm reset" : "Start a new conversation"
},
newConversationConfirm ? "Click to Confirm" : "New Conversation"
), showSaveButton && /* @__PURE__ */ React3.createElement(
"button",
{
className: "save-button",
onClick: () => {
saveHTMLToFile(
convertHistoryToHTML(history),
`conversation-${(/* @__PURE__ */ new Date()).toISOString()}.html`
);
interactionClicked(lastCallId, "save");
},
disabled: isDisabledDueToNoEmail()
},
"Save Conversation"
), showEmailButton && /* @__PURE__ */ React3.createElement(
"button",
{
className: "save-button",
onClick: () => {
if (isEmailAddress(emailInput)) {
setEmailInputSet(true);
setEmailValid(true);
handleSendEmail(emailInput, emailInput);
setEmailClickedButNoEmail(false);
} else {
setShowEmailPanel(true);
setEmailValid(false);
setEmailClickedButNoEmail(true);
}
},
disabled: isDisabledDueToNoEmail()
},
"Email Conversation" + (emailSent ? " \u2713" : "")
), showCallToAction && /* @__PURE__ */ React3.createElement(
"button",
{
className: "save-button",
onClick: () => {
if (isEmailAddress(emailInput)) {
setEmailInputSet(true);
setEmailValid(true);
sendCallToActionEmail(emailInput);
setCTAClickedButNoEmail(false);
} else {
setShowEmailPanel(true);
setEmailValid(false);
setCTAClickedButNoEmail(true);
}
},
disabled: isDisabledDueToNoEmail()
},
callToActionButtonText + (callToActionSent ? " \u2713" : "")
)),
/* @__PURE__ */ React3.createElement(
EmailModal_default,
{
isOpen: isEmailModalOpen,
defaultEmail: emailInput != null ? emailInput : "",
onClose: () => setIsEmailModalOpen(false),
onSend: handleSendEmail
}
),
/* @__PURE__ */ React3.createElement("div", { className: "input-container" }, /* @__PURE__ */ React3.createElement(
"textarea",
{
ref: textareaRef,
className: "chat-input",
placeholder,
value: nextPrompt,
onChange: (e) => {
setNextPrompt(e.target.value);
setTimeout(autoResizeTextarea, 0);
},
onKeyDown: (e) => {
if (e.key === "Enter") {
if (e.shiftKey) {
return;
} else {
if (nextPrompt.trim() !== "") {
e.preventDefault();
continueChat();
}
}
}
},
disabled: isDisabledDueToNoEmail()
}
), /* @__PURE__ */ React3.createElement(
"button",
{
className: "send-button",
onClick: () => continueChat(),
disabled: isDisabledDueToNoEmail()
},
idle ? /* @__PURE__ */ React3.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 24 24",
strokeWidth: "1",
stroke: "currentColor",
fill: "none",
strokeLinecap: "round",
strokeLinejoin: "round",
className: "icon-svg-large"
},
/* @__PURE__ */ React3.createElement("path", { stroke: "none", d: "M0 0h24v24H0z", fill: "none" }),
/* @__PURE__ */ React3.createElement("path", { d: "M10 14l11 -11" }),
/* @__PURE__ */ React3.createElement("path", { d: "M21 3l-6.5 18a.55 .55 0 0 1 -1 0l-3.5 -7l-7 -3.5a.55 .55 0 0 1 0 -1l18 -6.5" })
) : /* @__PURE__ */ React3.createElement(
"svg",
{
xmlns: "http://www.w3.org/2000/svg",
viewBox: "0 0 24 24",
strokeWidth: "1",
stroke: "currentColor",
fill: "currentColor",
className: "icon-svg-large"
},
/* @__PURE__ */ React3.createElement("path", { d: "M8 8h16v16H8z" })
)
)),
showPoweredBy && /* @__PURE__ */ React3.createElement(
"div",
{
className: `footer-container ${mcpServers && mcpServers.length > 0 ? "with-tools" : "no-tools"}`
},
mcpServers && mcpServers.length > 0 && /* @__PURE__ */ React3.createElement("div", { className: "footer-left" }, /* @__PURE__ */ React3.createElement("div", { className: "tool-status" }, /* @__PURE__ */ React3.createElement(
"span",
{
className: `tool-status-dot ${toolsLoading ? "loading" : toolsFetchError ? "error" : "ready"}`,
title: !toolsLoading && !toolsFetchError && toolList.length > 0 ? toolList.map(
(tool) => `${tool.name}: ${tool.description || "No description"}`
).join("\n") : ""
}
), /* @__PURE__ */ React3.createElement(
"span",
{
className: "tool-status-text",
title: !toolsLoading && !toolsFetchError && toolList.length > 0 ? toolList.map(
(tool) => `${tool.name}: ${tool.description || "No description"}`
).join("\n") : ""
},
toolsLoading ? "tools loading..." : toolsFetchError ? "tool fetch failed" : toolList.length > 0 ? `${toolList.length} tools ready` : "no tools found"
))),
/* @__PURE__ */ React3.createElement(
"div",
{
className: `footer-right ${mcpServers && mcpServers.length > 0 ? "" : "footer-center"}`
},
/* @__PURE__ */ React3.createElement("div", { className: "powered-by" }, /* @__PURE__ */ React3.createElement(
"svg",
{
width: "16",
height: "16",
viewBox: "0 0 72 72",
fill: "none",
xmlns: "http://www.w3.org/2000/svg"
},
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "14.0868",
cy: "59.2146",
rx: "7.8261",
ry: "7.7854",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "24.9013",
cy: "43.0776",
rx: "6.11858",
ry: "6.08676",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "45.391",
cy: "43.0776",
rx: "6.11858",
ry: "6.08676",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "65.8813",
cy: "43.0776",
rx: "6.11858",
ry: "6.08676",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "13.9444",
cy: "30.4795",
rx: "4.83795",
ry: "4.81279",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "34.7193",
cy: "30.4795",
rx: "4.83795",
ry: "4.81279",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "55.4942",
cy: "30.4795",
rx: "4.83795",
ry: "4.81279",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "3.27273",
cy: "20.4293",
rx: "3.27273",
ry: "3.25571",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "24.9011",
cy: "20.4293",
rx: "3.27273",
ry: "3.25571",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "45.3914",
cy: "20.4293",
rx: "3.27273",
ry: "3.25571",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "12.2373",
cy: "13.4931",
rx: "1.70751",
ry: "1.69863",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "33.0122",
cy: "13.4931",
rx: "1.70751",
ry: "1.69863",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "53.5019",
cy: "13.4931",
rx: "1.70751",
ry: "1.69863",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "19.3517",
cy: "6.13242",
rx: "1.13834",
ry: "1.13242",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "40.1266",
cy: "6.13242",
rx: "1.13834",
ry: "1.13242",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "60.901",
cy: "6.13242",
rx: "1.13834",
ry: "1.13242",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "34.8617",
cy: "59.2146",
rx: "7.8261",
ry: "7.7854",
fill: "#2487D8"
}
),
/* @__PURE__ */ React3.createElement(
"ellipse",
{
cx: "55.6366",
cy: "59.2146",
rx: "7.8261",
ry: "7.7854",
fill: "#ED7D31"
}
)
), " ", "\xA0\xA0powered by\xA0", /* @__PURE__ */ React3.createElement(
"a",
{
href: "https://llmasaservice.io",
target: "_blank",
rel: "noopener noreferrer"
},
"llmasaservice.io"
))
)
)
), /* @__PURE__ */ React3.createElement("div", { ref: bottomPanelRef }), iframeUrl && /* @__PURE__ */ React3.createElement(
"div",
{
style: {
position: "fixed",
top: 0,
left: 0,
width: "100vw",
height: "100vh",
backgroundColor: "rgba(0, 0, 0, 0.7)",
display: "flex",
justifyContent: "center",
alignItems: "center",
zIndex: 9999
}
},
/* @__PURE__ */ React3.createElement("div", { style: { position: "relative", width: "95vw", height: "95vh" } }, /* @__PURE__ */ React3.createElement(
"button",
{
onClick: () => setIframeUrl(null),
style: {
position: "absolute",
top: 10,
right: 10,
zIndex: 1e4,
background: "#fff",
border: "none",
padding: "5px 5px",
cursor: "pointer"
}
},
"Close"
), /* @__PURE__ */ React3.createElement(
"iframe",
{
src: iframeUrl,
title: "Dynamic Iframe",
style: { width: "100%", height: "100%", border: "none" }
}
))
));
};
var ChatPanel_default = ChatPanel;
// src/AgentPanel.tsx
import React4, { useEffect as useEffect3, useState as useState3 } from "react";
import materialDark2 from "react-syntax-highlighter/dist/esm/styles/prism/material-dark.js";
import materialLight2 from "react-syntax-highlighter/dist/esm/styles/prism/material-light.js";
var AgentPanel = ({
//project_id,
//initialPrompt = "",
//title = "Chat",
//placeholder = "Type a message",
//hideInitialPrompt = true,
customer = {},
messages = [],
data = [],
thumbsUpClick,
thumbsDownClick,
theme,
markdownClass = null,
width,
height,
url = "https://chat.llmasaservice.io",
//url = "https://chat.llmasaservice.io/dev",
//scrollToEnd = false,
//initialMessage = "",
prismStyle = null,
service = null,
historyChangedCallback = void 0,
responseCompleteCallback = void 0,
//promptTemplate = "",
actions = [],
//showSaveButton = true,
//showEmailButton = true,
followOnQuestions = [],
clearFollowOnQuestionsNextPrompt = false,
followOnPrompt = "",
showPoweredBy = true,
agent,
conversation = null,
//showCallToAction = false,
//callToActionButtonText = "Submit",
//callToActionEmailAddress = "",
//callToActionEmailSubject = "Agent CTA submitted",
//ragQueryLimit = 10,
//ragRankLimit = 5,
initialHistory = {},
hideRagContextInPrompt = true
}) => {
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p, _q, _r, _s, _t, _u;
const searchParams = new URLSearchParams(location.search);
const customer_id = searchParams.get("customer_id") || null;
const customer_email = searchParams.get("customer_email") || null;
const customer_name = searchParams.get("customer_name") || null;
const customer_user_id = searchParams.get("customer_user_id") || null;
const [agentData, setAgentData] = useState3(null);
const [mcpData, setMCPData] = useState3(null);
const createCustomerObject = () => {
const customerData = {};
const finalCustomerId = (customer == null ? void 0 : customer.customer_id) || customer_id;
if (finalCustomerId) {
customerData.customer_id = finalCustomerId;
}
const finalCustomerEmail = (customer == null ? void 0 : customer.customer_user_email) || customer_email;
if (finalCustomerEmail) {
customerData.customer_user_email = finalCustomerEmail;
}
const finalCustomerUserName = (customer == null ? void 0 : customer.customer_user_name) || customer_name;
if (finalCustomerUserName) {
customerData.customer_user_name = finalCustomerUserName;
}
const finalCustomerName = (customer == null ? void 0 : customer.customer_name) || customer_name;
if (finalCustomerName) {
customerData.customer_name = finalCustomerName;
}
const finalCustomerUserId = (customer == null ? void 0 : customer.customer_user_id) || customer_user_id;
if (finalCustomerUserId) {
customerData.customer_user_id = finalCustomerUserId;
}
return Object.keys(customerData).length > 0 ? customerData : void 0;
};
useEffect3(() => {
const fetchAgentData = () => __async(void 0, null, function* () {
try {
const fetchUrl = url.endsWith("dev") ? `https://8ftw8droff.execute-api.us-east-1.amazonaws.com/dev/agents/${agent}` : `https://api.llmasaservice.io/agents/${agent}`;
const response = yield fetch(fetchUrl, {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const data2 = yield response.json();
if (data2 && data2.length > 0) {
setAgentData(data2[0]);
const response2 = yield fetch(fetchUrl + "/mcp", {
method: "GET",
headers: {
"Content-Type": "application/json"
}
});
const mcpData2 = yield response2.json();
if (mcpData2 && mcpData2.length > 0) {
setMCPData(
mcpData2.filter(
(mcp) => mcp.status === "active" && mcp.executionMode !== "SERVER"
)
);
}
}
} catch (error) {
console.error("Error fetching agent data:", error);
}
});
if (agent && agent !== "") {
fetchAgentData();
}
}, [agent, url]);
const getActionsArraySafely = (actionsString) => {
let actions2 = [];
if (actionsString && actionsString !== "") {
try {
actions2 = JSON.parse(actionsString);
if (!Array.isArray(actions2)) {
throw new Error("Parsed actions is not an array");
}
} catch (error) {
actions2 = [];
}
}
return actions2;
};
return /* @__PURE__ */ React4.createElement(React4.Fragment, null, agentData && /* @__PURE__ */ React4.createElement(
ChatPanel_default,
__spreadProps(__spreadValues({
project_id: agentData == null ? void 0 : agentData.projectId,
service: service && service !== "" ? service : (agentData == null ? void 0 : agentData.groupId) || null,
url,
title: (_a = agentData == null ? void 0 : agentData.displayTitle) != null ? _a : "",
theme: theme ? theme : (agentData == null ? void 0 : agentData.displayTheme) === "light" ? "light" : "dark",
markdownClass: markdownClass != null ? markdownClass : void 0,
cssUrl: (_b = agentData == null ? void 0 : agentData.cssUrl) != null ? _b : "",
height: height ? height : (_c = agentData == null ? void 0 : agentData.displayHeight) != null ? _c : "100vh",
width: width ? width : (_d = agentData == null ? void 0 : agentData.displayWidth) != null ? _d : "100%",
promptTemplate: (_e = agentData == null ? void 0 : agentData.displayPromptTemplate) != null ? _e : "{{prompt}}",
initialMessage: (agentData == null ? void 0 : agentData.displayStartMessageOrPrompt) === "message" ? (_f = agentData == null ? void 0 : agentData.displayInitialMessageOrPrompt) != null ? _f : "" : void 0,
initialPrompt: (agentData == null ? void 0 : agentData.displayStartMessageOrPrompt) === "prompt" ? (_g = agentData == null ? void 0 : agentData.displayInitialMessageOrPrompt) != null ? _g : "" : void 0,
followOnQuestions: followOnQuestions && followOnQuestions.length > 0 ? followOnQuestions : (_i = (_h = agentData == null ? void 0 : agentData.displayFollowOnPrompts) == null ? void 0 : _h.split("|")) != null ? _i : [],
clearFollowOnQuestionsNextPrompt,
historyChangedCallback,
responseCompleteCallback,
prismStyle: prismStyle != null ? prismStyle : theme ? theme === "light" ? materialLight2 : materialDark2 : (agentData == null ? void 0 : agentData.displayTheme) === "light" ? materialLight2 : materialDark2,
actions: [
...actions,
...getActionsArraySafely(agentData == null ? void 0 : agentData.displayActions)
],
followOnPrompt,
agent,
placeholder: (_j = agentData == null ? void 0 : agentData.displayPlaceholder) != null ? _j : "Type a message",
hideInitialPrompt: (_k = agentData == null ? void 0 : agentData.displayHideInitialPrompt) != null ? _k : true,
data: [...data, { key: "data", data: agentData == null ? void 0 : agentData.data }],
showEmailButton: (_l = agentData == null ? void 0 : agentData.displayShowEmailButton) != null ? _l : true,
showSaveButton: (_m = agentData == null ? void 0 : agentData.displayShowSaveButton) != null ? _m : true,
showCallToAction: (_n = agentData == null ? void 0 : agentData.displayShowCallToAction) != null ? _n : false,
callToActionButtonText: (_o = agentData == null ? void 0 : agentData.displayCallToActionButtonText) != null ? _o : "Submit",
callToActionEmailAddress: (_p = agentData == null ? void 0 : agentData.displayCallToActionEmailAddress) != null ? _p : "",
callToActionEmailSubject: (_q = agentData == null ? void 0 : agentData.displayCallToActionEmailSubject) != null ? _q : "Agent CTA Submitted"
}, createCustomerObject() && { customer: createCustomerObject() }), {
scrollToEnd: (_r = agentData == null ? void 0 : agentData.displayScrollToEnd) != null ? _r : false,
showPoweredBy,
messages,
conversation,
initialHistory,
hideRagContextInPrompt,
createConversationOnFirstChat: (_s = agentData == null ? void 0 : agentData.createConversationOnFirstChat) != null ? _s : true,
customerEmailCaptureMode: (_t = agentData == null ? void 0 : agentData.customerEmailCaptureMode) != null ? _t : "HIDE",
customerEmailCapturePlaceholder: (_u = agentData == null ? void 0 : agentData.customerEmailCapturePlaceholder) != null ? _u : "Please enter your email...",
mcpServers: mcpData
})
));
};
var AgentPanel_default = AgentPanel;
export {
AgentPanel_default as AgentPanel,
ChatPanel_default as ChatPanel
};