Merge remote-tracking branch 'origin/form_io' into python_custom_node_component
This commit is contained in:
commit
6416c43ff2
37 changed files with 669 additions and 423 deletions
|
|
@ -25,6 +25,7 @@ import { nodeColors } from "../../../../utils";
|
|||
import ShadTooltip from "../../../../components/ShadTooltipComponent";
|
||||
import { PopUpContext } from "../../../../contexts/popUpContext";
|
||||
import ToggleShadComponent from "../../../../components/toggleShadComponent";
|
||||
import * as _ from "lodash";
|
||||
|
||||
export default function ParameterComponent({
|
||||
left,
|
||||
|
|
@ -36,6 +37,7 @@ export default function ParameterComponent({
|
|||
type,
|
||||
name = "",
|
||||
required = false,
|
||||
optionalHandle = null,
|
||||
}: ParameterComponentType) {
|
||||
const ref = useRef(null);
|
||||
const refHtml = useRef(null);
|
||||
|
|
@ -73,6 +75,7 @@ export default function ParameterComponent({
|
|||
return {
|
||||
...prev,
|
||||
[tabId]: {
|
||||
...prev[tabId],
|
||||
isPending: true,
|
||||
formKeysData: prev[tabId].formKeysData,
|
||||
},
|
||||
|
|
@ -85,7 +88,7 @@ export default function ParameterComponent({
|
|||
|
||||
refHtml.current = groupedObj.map((item, i) => (
|
||||
<span
|
||||
key={getRandomKeyByssmm()}
|
||||
key={getRandomKeyByssmm() + item.family + i}
|
||||
className={classNames(
|
||||
i > 0 ? "items-center flex mt-3" : "items-center flex"
|
||||
)}
|
||||
|
|
@ -132,13 +135,14 @@ export default function ParameterComponent({
|
|||
<span className="text-red-600">{required ? " *" : ""}</span>
|
||||
</div>
|
||||
{left &&
|
||||
(type === "str" ||
|
||||
((type === "str" ||
|
||||
type === "bool" ||
|
||||
type === "float" ||
|
||||
type === "code" ||
|
||||
type === "prompt" ||
|
||||
type === "file" ||
|
||||
type === "int") ? (
|
||||
type === "int") && !optionalHandle
|
||||
) ? (
|
||||
<></>
|
||||
) : (
|
||||
<ShadTooltip
|
||||
|
|
|
|||
|
|
@ -102,7 +102,7 @@ export default function GenericNode({
|
|||
color: nodeColors[types[data.type]] ?? nodeColors.unknown,
|
||||
}}
|
||||
/>
|
||||
<div className="ml-2 truncate">
|
||||
<div className="ml-2 truncate flex-1">
|
||||
<ShadTooltip
|
||||
delayDuration={1500}
|
||||
content={data.node.display_name}
|
||||
|
|
@ -204,6 +204,7 @@ export default function GenericNode({
|
|||
data={data}
|
||||
color={
|
||||
nodeColors[types[data.node.template[t].type]] ??
|
||||
nodeColors[data.node.template[t].type] ??
|
||||
nodeColors.unknown
|
||||
}
|
||||
title={
|
||||
|
|
@ -214,11 +215,12 @@ export default function GenericNode({
|
|||
: toTitleCase(t)
|
||||
}
|
||||
name={t}
|
||||
tooltipTitle={data.node.template[t].type}
|
||||
tooltipTitle={data.node.template[t].input_types?.join("\n") ?? data.node.template[t].type}
|
||||
required={data.node.template[t].required}
|
||||
id={data.node.template[t].type + "|" + t + "|" + data.id}
|
||||
id={(data.node.template[t].input_types?.join(";") ?? data.node.template[t].type) + "|" + t + "|" + data.id}
|
||||
left={true}
|
||||
type={data.node.template[t].type}
|
||||
optionalHandle={data.node.template[t].input_types}
|
||||
/>
|
||||
) : (
|
||||
<></>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { useEffect, useRef, useState } from "react";
|
||||
import { useContext, useEffect, useRef, useState } from "react";
|
||||
import { useNodes } from "reactflow";
|
||||
import { ChatType } from "../../types/chat";
|
||||
import ChatTrigger from "./chatTrigger";
|
||||
|
|
@ -7,11 +7,13 @@ import BuildTrigger from "./buildTrigger";
|
|||
import { getBuildStatus } from "../../controllers/API";
|
||||
import { NodeType } from "../../types/flow";
|
||||
import FormModal from "../../modals/formModal";
|
||||
import { TabsContext } from "../../contexts/tabsContext";
|
||||
import * as _ from "lodash";
|
||||
|
||||
export default function Chat({ flow }: ChatType) {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [openForm, setOpenForm] = useState(false);
|
||||
const [isBuilt, setIsBuilt] = useState(false);
|
||||
const {tabsState} = useContext(TabsContext);
|
||||
|
||||
useEffect(() => {
|
||||
const handleKeyDown = (event: KeyboardEvent) => {
|
||||
|
|
@ -44,21 +46,19 @@ export default function Chat({ flow }: ChatType) {
|
|||
const prevNodesRef = useRef<any[] | undefined>();
|
||||
const nodes = useNodes();
|
||||
useEffect(() => {
|
||||
|
||||
const prevNodes = prevNodesRef.current;
|
||||
const currentNodes = nodes.map(
|
||||
(node: NodeType) => node.data.node.template
|
||||
(node: NodeType) => _.cloneDeep(node.data.node.template)
|
||||
);
|
||||
|
||||
if (
|
||||
prevNodes &&
|
||||
JSON.stringify(prevNodes) !== JSON.stringify(currentNodes)
|
||||
tabsState && tabsState[flow.id] && tabsState[flow.id].isPending
|
||||
&& JSON.stringify(prevNodes) !== JSON.stringify(currentNodes)
|
||||
) {
|
||||
setIsBuilt(false);
|
||||
}
|
||||
|
||||
prevNodesRef.current = currentNodes;
|
||||
}, [nodes]);
|
||||
}, [tabsState]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
|
|||
|
|
@ -5,6 +5,9 @@ import GenericModal from "../../modals/genericModal";
|
|||
import { TypeModal } from "../../utils";
|
||||
import { INPUT_STYLE } from "../../constants";
|
||||
import { ExternalLink } from "lucide-react";
|
||||
import { postValidatePrompt } from "../../controllers/API";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import * as _ from "lodash";
|
||||
|
||||
export default function PromptAreaComponent({
|
||||
setNodeClass,
|
||||
|
|
@ -14,8 +17,10 @@ export default function PromptAreaComponent({
|
|||
disabled,
|
||||
editNode = false,
|
||||
}: TextAreaComponentType) {
|
||||
const [myValue, setMyValue] = useState(value);
|
||||
const [myValue, setMyValue] = useState("");
|
||||
const { openPopUp } = useContext(PopUpContext);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setMyValue("");
|
||||
|
|
@ -24,8 +29,19 @@ export default function PromptAreaComponent({
|
|||
}, [disabled, onChange]);
|
||||
|
||||
useEffect(() => {
|
||||
setMyValue(value);
|
||||
}, [value]);
|
||||
if (value !== "" && myValue !== value && reactFlowInstance) { // only executed once
|
||||
setMyValue(value);
|
||||
postValidatePrompt(value, nodeClass)
|
||||
.then((apiReturn) => {
|
||||
if (apiReturn.data) {
|
||||
setNodeClass(apiReturn.data.frontend_node);
|
||||
// need to update reactFlowInstance to re-render the nodes.
|
||||
reactFlowInstance.setEdges(_.cloneDeep(reactFlowInstance.getEdges()));
|
||||
}
|
||||
})
|
||||
.catch((error) => {});
|
||||
}
|
||||
}, [reactFlowInstance]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -8,11 +8,6 @@ export default function ToggleShadComponent({
|
|||
disabled,
|
||||
size,
|
||||
}: ToggleComponentType) {
|
||||
useEffect(() => {
|
||||
if (disabled) {
|
||||
setEnabled(false);
|
||||
}
|
||||
}, [disabled, setEnabled]);
|
||||
let scaleX, scaleY;
|
||||
switch (size) {
|
||||
case "small":
|
||||
|
|
@ -32,11 +27,12 @@ export default function ToggleShadComponent({
|
|||
scaleY = 1;
|
||||
}
|
||||
return (
|
||||
<div className={disabled ? "pointer-events-none cursor-not-allowed" : ""}>
|
||||
<div className={disabled ? "pointer-events-none cursor-not-allowed " : ""}>
|
||||
<Switch
|
||||
style={{
|
||||
transform: `scaleX(${scaleX}) scaleY(${scaleY})`,
|
||||
}}
|
||||
disabled={disabled}
|
||||
className="data-[state=unchecked]:bg-slate-500"
|
||||
checked={enabled}
|
||||
onCheckedChange={(x: boolean) => {
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ export const DESCRIPTIONS: string[] = [
|
|||
"Your Hub for Text Generation.",
|
||||
"Promptly Ingenious!",
|
||||
"Building Linguistic Labyrinths.",
|
||||
"LangFlow: Create, Chain, Communicate.",
|
||||
"Create, Chain, Communicate.",
|
||||
"Connect the Dots, Craft Language.",
|
||||
"Interactive Language Weaving.",
|
||||
"Generate, Innovate, Communicate.",
|
||||
|
|
@ -183,6 +183,46 @@ export const DESCRIPTIONS: string[] = [
|
|||
"Nurture NLP Nodes Here.",
|
||||
"Conversational Cartography Unlocked.",
|
||||
"Design, Develop, Dialogize.",
|
||||
"Unleashing Linguistic Creativity.",
|
||||
"Graph Your Way to Great Conversations.",
|
||||
"The Power of Language at Your Fingertips.",
|
||||
"Sculpting Language with Precision.",
|
||||
"Where Language Meets Logic.",
|
||||
"Building Intelligent Interactions.",
|
||||
"Your Passport to Linguistic Landscapes.",
|
||||
"Create, Curate, Communicate with LangFlow.",
|
||||
"Flow into the Future of Language.",
|
||||
"Mapping Meaningful Conversations.",
|
||||
"Unravel the Art of Articulation.",
|
||||
"Language Engineering Excellence.",
|
||||
"Navigate the Networks of Conversation.",
|
||||
"Crafting Conversations, One Node at a Time.",
|
||||
"The Pinnacle of Prompt Generation.",
|
||||
"Language Models, Mapped and Mastered.",
|
||||
"Powerful Prompts, Perfectly Positioned.",
|
||||
"Innovation in Interaction with LangFlow.",
|
||||
"Your Toolkit for Text Generation.",
|
||||
"Unfolding Linguistic Possibilities.",
|
||||
"Building Powerful Solutions with Language Models.",
|
||||
"Uncover Business Opportunities with NLP.",
|
||||
"Harness the Power of Conversational AI.",
|
||||
"Transform Your Business with Smart Dialogues.",
|
||||
"Craft Meaningful Interactions, Generate Value.",
|
||||
"Unleashing Business Potential through Language Engineering.",
|
||||
"Empowering Enterprises with Intelligent Interactions.",
|
||||
"Driving Innovation in Business Communication.",
|
||||
"Catalyzing Business Growth through Conversational AI.",
|
||||
"Text Generation Meets Business Transformation.",
|
||||
"Navigate the Linguistic Landscape, Discover Opportunities.",
|
||||
"Create Powerful Connections, Boost Business Value.",
|
||||
"Empowering Communication, Enabling Opportunities.",
|
||||
"Advanced NLP for Groundbreaking Business Solutions.",
|
||||
"Innovation in Interaction, Revolution in Revenue.",
|
||||
"Maximize Impact with Intelligent Conversations.",
|
||||
"Beyond Text Generation - Unleashing Business Opportunities.",
|
||||
"Unlock the Power of AI in Your Business Conversations.",
|
||||
"Crafting Dialogues that Drive Business Success.",
|
||||
"Engineered for Excellence, Built for Business.",
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -256,8 +296,49 @@ export const ADJECTIVES: string[] = [
|
|||
"thirsty",
|
||||
"tiny",
|
||||
"trusting",
|
||||
"bubbly",
|
||||
"charming",
|
||||
"cheerful",
|
||||
"comical",
|
||||
"dazzling",
|
||||
"delighted",
|
||||
"dynamic",
|
||||
"effervescent",
|
||||
"enthusiastic",
|
||||
"exuberant",
|
||||
"fluffy",
|
||||
"friendly",
|
||||
"funky",
|
||||
"giddy",
|
||||
"giggly",
|
||||
"gleeful",
|
||||
"goofy",
|
||||
"graceful",
|
||||
"grinning",
|
||||
"hilarious",
|
||||
"inquisitive",
|
||||
"joyous",
|
||||
"jubilant",
|
||||
"lively",
|
||||
"mirthful",
|
||||
"mischievous",
|
||||
"optimistic",
|
||||
"peppy",
|
||||
"perky",
|
||||
"playful",
|
||||
"quirky",
|
||||
"radiant",
|
||||
"sassy",
|
||||
"silly",
|
||||
"spirited",
|
||||
"sprightly",
|
||||
"twinkly",
|
||||
"upbeat",
|
||||
"vibrant",
|
||||
"witty",
|
||||
"zany",
|
||||
"zealous",
|
||||
];
|
||||
|
||||
/**
|
||||
* Nouns for the name of the flow
|
||||
* @constant
|
||||
|
|
@ -406,6 +487,38 @@ export const NOUNS: string[] = [
|
|||
"wright",
|
||||
"yalow",
|
||||
"yonath",
|
||||
"coulomb",
|
||||
"degrasse",
|
||||
"dewey",
|
||||
"edison",
|
||||
"eratosthenes",
|
||||
"faraday",
|
||||
"galton",
|
||||
"gauss",
|
||||
"herschel",
|
||||
"hubble",
|
||||
"joule",
|
||||
"kaku",
|
||||
"kepler",
|
||||
"khayyam",
|
||||
"lavoisier",
|
||||
"maxwell",
|
||||
"mendel",
|
||||
"mendeleev",
|
||||
"ohm",
|
||||
"pascal",
|
||||
"planck",
|
||||
"riemann",
|
||||
"schrodinger",
|
||||
"sagan",
|
||||
"tesla",
|
||||
"tyson",
|
||||
"volta",
|
||||
"watt",
|
||||
"weber",
|
||||
"wien",
|
||||
"zoBell",
|
||||
"zuse",
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -189,26 +189,28 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
}
|
||||
|
||||
function processFlowEdges(flow) {
|
||||
if(!flow.data || !flow.data.edges) return;
|
||||
if (!flow.data || !flow.data.edges) return;
|
||||
flow.data.edges.forEach((edge) => {
|
||||
edge.className = "";
|
||||
edge.style = { stroke: "#555555" };
|
||||
});
|
||||
}
|
||||
function updateDisplay_name(node:NodeType,template:APIClassType) {
|
||||
node.data.node.display_name = template["display_name"]?template["display_name"]:node.data.type;
|
||||
function updateDisplay_name(node: NodeType, template: APIClassType) {
|
||||
node.data.node.display_name = template["display_name"]
|
||||
? template["display_name"]
|
||||
: node.data.type;
|
||||
}
|
||||
|
||||
function processFlowNodes(flow) {
|
||||
if(!flow.data || !flow.data.nodes) return;
|
||||
flow.data.nodes.forEach((node:NodeType) => {
|
||||
if (!flow.data || !flow.data.nodes) return;
|
||||
flow.data.nodes.forEach((node: NodeType) => {
|
||||
const template = templates[node.data.type];
|
||||
if (!template) {
|
||||
setErrorData({ title: `Unknown node type: ${node.data.type}` });
|
||||
return;
|
||||
}
|
||||
if (Object.keys(template["template"]).length > 0) {
|
||||
updateDisplay_name(node,template);
|
||||
updateDisplay_name(node, template);
|
||||
updateNodeBaseClasses(node, template);
|
||||
updateNodeEdges(flow, node, template);
|
||||
updateNodeDescription(node, template);
|
||||
|
|
@ -217,11 +219,15 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}
|
||||
|
||||
function updateNodeBaseClasses(node:NodeType,template:APIClassType) {
|
||||
function updateNodeBaseClasses(node: NodeType, template: APIClassType) {
|
||||
node.data.node.base_classes = template["base_classes"];
|
||||
}
|
||||
|
||||
function updateNodeEdges(flow:FlowType, node:NodeType,template:APIClassType) {
|
||||
function updateNodeEdges(
|
||||
flow: FlowType,
|
||||
node: NodeType,
|
||||
template: APIClassType
|
||||
) {
|
||||
flow.data.edges.forEach((edge) => {
|
||||
if (edge.source === node.id) {
|
||||
edge.sourceHandle = edge.sourceHandle
|
||||
|
|
@ -233,11 +239,11 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
});
|
||||
}
|
||||
|
||||
function updateNodeDescription(node:NodeType,template:APIClassType) {
|
||||
function updateNodeDescription(node: NodeType, template: APIClassType) {
|
||||
node.data.node.description = template["description"];
|
||||
}
|
||||
|
||||
function updateNodeTemplate(node:NodeType,template:APIClassType) {
|
||||
function updateNodeTemplate(node: NodeType, template: APIClassType) {
|
||||
node.data.node.template = updateTemplate(
|
||||
template["template"] as unknown as APITemplateType,
|
||||
node.data.node.template as APITemplateType
|
||||
|
|
@ -302,28 +308,39 @@ export function TabsProvider({ children }: { children: ReactNode }) {
|
|||
* If the file type is application/json, the file is read and parsed into a JSON object.
|
||||
* The resulting JSON object is passed to the addFlow function.
|
||||
*/
|
||||
function uploadFlow(newProject?: boolean) {
|
||||
// create a file input
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = ".json";
|
||||
// add a change event listener to the file input
|
||||
input.onchange = (e: Event) => {
|
||||
// check if the file type is application/json
|
||||
if ((e.target as HTMLInputElement).files[0].type === "application/json") {
|
||||
// get the file from the file input
|
||||
const file = (e.target as HTMLInputElement).files[0];
|
||||
// read the file as text
|
||||
file.text().then((text) => {
|
||||
// parse the text into a JSON object
|
||||
let flow: FlowType = JSON.parse(text);
|
||||
function uploadFlow(newProject?: boolean, file?: File) {
|
||||
if (file) {
|
||||
file.text().then((text) => {
|
||||
// parse the text into a JSON object
|
||||
let flow: FlowType = JSON.parse(text);
|
||||
|
||||
addFlow(flow, newProject);
|
||||
});
|
||||
}
|
||||
};
|
||||
// trigger the file input click event to open the file dialog
|
||||
input.click();
|
||||
addFlow(flow, newProject);
|
||||
});
|
||||
} else {
|
||||
// create a file input
|
||||
const input = document.createElement("input");
|
||||
input.type = "file";
|
||||
input.accept = ".json";
|
||||
// add a change event listener to the file input
|
||||
input.onchange = (e: Event) => {
|
||||
// check if the file type is application/json
|
||||
if (
|
||||
(e.target as HTMLInputElement).files[0].type === "application/json"
|
||||
) {
|
||||
// get the file from the file input
|
||||
const currentfile = (e.target as HTMLInputElement).files[0];
|
||||
// read the file as text
|
||||
currentfile.text().then((text) => {
|
||||
// parse the text into a JSON object
|
||||
let flow: FlowType = JSON.parse(text);
|
||||
|
||||
addFlow(flow, newProject);
|
||||
});
|
||||
}
|
||||
};
|
||||
// trigger the file input click event to open the file dialog
|
||||
input.click();
|
||||
}
|
||||
}
|
||||
|
||||
function uploadFlows() {
|
||||
|
|
|
|||
|
|
@ -53,7 +53,7 @@ export default function ChatInput({
|
|||
lockChat
|
||||
? " bg-input text-black dark:bg-gray-700 dark:text-gray-300"
|
||||
: " bg-white-200 text-black dark:bg-gray-900 dark:text-gray-300",
|
||||
"p-4 form-input block w-full custom-scroll rounded-md border-gray-300 dark:border-gray-600 pr-12 sm:text-sm" +
|
||||
"p-4 form-input block w-full custom-scroll rounded-md border-gray-300 dark:border-gray-600 pr-16 sm:text-sm" +
|
||||
INPUT_STYLE
|
||||
)}
|
||||
placeholder={"Send a message..."}
|
||||
|
|
@ -62,7 +62,7 @@ export default function ChatInput({
|
|||
<button className={classNames("p-2 px-1 transition-all duration-300 rounded-md",chatValue == "" ? "text-primary" : " bg-indigo-600 text-background")} disabled={lockChat} onClick={() => sendMessage()}>
|
||||
{lockChat ? (
|
||||
<Lock
|
||||
className="h-5 w-5 ml-1 mr-11 animate-pulse"
|
||||
className="h-5 w-5 ml-1 mr-1 animate-pulse"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
) : (
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ export const CodeBlock: FC<Props> = memo(({ language, value }) => {
|
|||
</div>
|
||||
|
||||
<SyntaxHighlighter
|
||||
className=" w-[570px]"
|
||||
className=" w-[47vw]"
|
||||
language={language}
|
||||
style={oneDark}
|
||||
customStyle={{ margin: 0 }}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,7 @@ export default function ChatMessage({
|
|||
<ReactMarkdown
|
||||
remarkPlugins={[remarkGfm, remarkMath]}
|
||||
rehypePlugins={[rehypeMathjax]}
|
||||
className="markdown prose dark:prose-invert text-primary"
|
||||
className="markdown prose dark:prose-invert text-primary max-w-full"
|
||||
components={{
|
||||
code({ node, inline, className, children, ...props }) {
|
||||
if (children.length) {
|
||||
|
|
|
|||
|
|
@ -4,7 +4,13 @@ import { alertContext } from "../../contexts/alertContext";
|
|||
import { classNames, validateNodes } from "../../utils";
|
||||
import { typesContext } from "../../contexts/typesContext";
|
||||
import ChatMessage from "./chatMessage";
|
||||
import { TerminalSquare, MessageSquare, Variable, Eraser } from "lucide-react";
|
||||
import {
|
||||
TerminalSquare,
|
||||
MessageSquare,
|
||||
Variable,
|
||||
Eraser,
|
||||
MessageSquarePlus,
|
||||
} from "lucide-react";
|
||||
import { sendAllProps } from "../../types/api";
|
||||
import { ChatMessageType } from "../../types/chat";
|
||||
import ChatInput from "./chatInput";
|
||||
|
|
@ -30,6 +36,14 @@ import {
|
|||
import { Textarea } from "../../components/ui/textarea";
|
||||
import { Badge } from "../../components/ui/badge";
|
||||
import ToggleShadComponent from "../../components/toggleShadComponent";
|
||||
import Dropdown from "../../components/dropdownComponent";
|
||||
import {
|
||||
DropdownMenu,
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
} from "../../components/ui/dropdown-menu";
|
||||
import { Button } from "../../components/ui/button";
|
||||
|
||||
export default function FormModal({
|
||||
flow,
|
||||
|
|
@ -40,18 +54,27 @@ export default function FormModal({
|
|||
setOpen: Function;
|
||||
flow: FlowType;
|
||||
}) {
|
||||
const [chatValue, setChatValue] = useState("");
|
||||
const [keysValue, setKeysValue] = useState([]);
|
||||
const { tabsState, setTabsState } = useContext(TabsContext);
|
||||
const [chatValue, setChatValue] = useState(
|
||||
tabsState[flow.id].formKeysData.input_keys[
|
||||
Object.keys(tabsState[flow.id].formKeysData.input_keys).find(
|
||||
(k) => !tabsState[flow.id].formKeysData.handle_keys.some((j) => j === k)
|
||||
)
|
||||
]
|
||||
);
|
||||
const [chatHistory, setChatHistory] = useState<ChatMessageType[]>([]);
|
||||
const { reactFlowInstance } = useContext(typesContext);
|
||||
const { setErrorData, setNoticeData } = useContext(alertContext);
|
||||
const { tabsState } = useContext(TabsContext);
|
||||
const ws = useRef<WebSocket | null>(null);
|
||||
const [lockChat, setLockChat] = useState(false);
|
||||
const isOpen = useRef(open);
|
||||
const messagesRef = useRef(null);
|
||||
const id = useRef(flow.id);
|
||||
const [chatKey, setChatKey] = useState(0);
|
||||
const [chatKey, setChatKey] = useState(
|
||||
Object.keys(tabsState[flow.id].formKeysData.input_keys).find(
|
||||
(k) => !tabsState[flow.id].formKeysData.handle_keys.some((j) => j === k)
|
||||
)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (messagesRef.current) {
|
||||
|
|
@ -64,12 +87,6 @@ export default function FormModal({
|
|||
}, [open]);
|
||||
useEffect(() => {
|
||||
id.current = flow.id;
|
||||
|
||||
if (tabsState[flow.id].formKeysData) {
|
||||
setKeysValue(
|
||||
Array(tabsState[flow.id].formKeysData.input_keys.length).fill("")
|
||||
);
|
||||
}
|
||||
}, [flow.id, tabsState[flow.id], tabsState[flow.id].formKeysData]);
|
||||
|
||||
var isStream = false;
|
||||
|
|
@ -166,13 +183,13 @@ export default function FormModal({
|
|||
chatItem.files
|
||||
? {
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.is_bot ? chatItem.message : formatMessage(chatItem.message),
|
||||
message: formatMessage(chatItem.message),
|
||||
thought: chatItem.intermediate_steps,
|
||||
files: chatItem.files,
|
||||
}
|
||||
: {
|
||||
isSend: !chatItem.is_bot,
|
||||
message: chatItem.is_bot ? chatItem.message : formatMessage(chatItem.message),
|
||||
message: formatMessage(chatItem.message),
|
||||
thought: chatItem.intermediate_steps,
|
||||
}
|
||||
);
|
||||
|
|
@ -305,6 +322,7 @@ export default function FormModal({
|
|||
}, [open]);
|
||||
function formatMessage(inputs: any): string {
|
||||
if (inputs) {
|
||||
if (typeof inputs == "string") return inputs;
|
||||
// inputs is a object with the keys and values being input_keys and keysValue
|
||||
// so the formated message is a string with the keys and values separated by ": "
|
||||
let message = "";
|
||||
|
|
@ -325,13 +343,7 @@ export default function FormModal({
|
|||
let nodeValidationErrors = validateNodes(reactFlowInstance);
|
||||
if (nodeValidationErrors.length === 0) {
|
||||
setLockChat(true);
|
||||
// Message variable makes a object with the keys being the names from tabsState[flow.id].formKeysData.input_keys and the values being the keysValue of the correspondent index
|
||||
let keys = tabsState[flow.id].formKeysData.input_keys; // array of keys
|
||||
let values = keysValue.map((k, i) => (i == chatKey ? chatValue : k)); // array of values
|
||||
let inputs = keys.reduce((object, key, index) => {
|
||||
object[key] = values[index];
|
||||
return object;
|
||||
}, {});
|
||||
let inputs = tabsState[id.current].formKeysData.input_keys;
|
||||
setChatValue("");
|
||||
const message = formatMessage(inputs);
|
||||
addChatHistory(message, true);
|
||||
|
|
@ -342,6 +354,11 @@ export default function FormModal({
|
|||
name: flow.name,
|
||||
description: flow.description,
|
||||
});
|
||||
setTabsState((old) => {
|
||||
let newTabsState = _.cloneDeep(old);
|
||||
newTabsState[id.current].formKeysData.input_keys[chatKey] = "";
|
||||
return newTabsState;
|
||||
});
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "Oops! Looks like you missed some required information:",
|
||||
|
|
@ -365,13 +382,12 @@ export default function FormModal({
|
|||
setOpen(x);
|
||||
}
|
||||
|
||||
function handleOnCheckedChange(checked: boolean, index: number) {
|
||||
function handleOnCheckedChange(checked: boolean, i: string) {
|
||||
if (checked === true) {
|
||||
setChatKey(index);
|
||||
setChatKey(i);
|
||||
setChatValue(tabsState[flow.id].formKeysData.input_keys[i]);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(chatHistory)
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={setModalOpen}>
|
||||
<DialogTrigger className="hidden"></DialogTrigger>
|
||||
|
|
@ -388,47 +404,90 @@ export default function FormModal({
|
|||
<DialogDescription>{CHAT_FORM_DIALOG_SUBTITLE}</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="flex h-[80vh] w-full mt-2">
|
||||
<div className="w-1/4 h-full flex flex-col justify-start mr-6">
|
||||
<div className="flex py-2">
|
||||
<Variable className="w-6 h-6 pe-1 text-gray-700 stroke-2 dark:text-slate-200"></Variable>
|
||||
<span className="text-md font-semibold text-gray-800 dark:text-white">
|
||||
Input Variables
|
||||
</span>
|
||||
<div className="flex h-[80vh] w-full mt-2 ">
|
||||
<div className="w-2/5 h-full overflow-auto scrollbar-hide flex flex-col justify-start mr-6">
|
||||
<div className="flex justify-between items-center">
|
||||
<div className="flex py-2 items-center">
|
||||
<Variable className="w-4 h-4 mr-1 text-primary"></Variable>
|
||||
<span className="text-sm font-semibold text-primary">
|
||||
Input Variables
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex mr-2.5 py-2 items-center">
|
||||
<span className="text-sm font-semibold text-primary">
|
||||
Chat Input
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<Accordion type="single" collapsible className="w-full">
|
||||
{tabsState[id.current].formKeysData.input_keys.map((i, k) => (
|
||||
<AccordionItem key={k} value={i}>
|
||||
<AccordionTrigger><Badge variant="gray" size="md">{i}</Badge></AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="p-1 flex flex-col gap-2">
|
||||
<div className="flex items-center space-x-2">
|
||||
<Label htmlFor="airplane-mode" className="-mt-1">From Chat</Label>
|
||||
<ToggleShadComponent
|
||||
enabled={chatKey === k}
|
||||
setEnabled={(value) =>
|
||||
handleOnCheckedChange(value, k)
|
||||
}
|
||||
size="small"
|
||||
disabled={false}
|
||||
/>
|
||||
</div>
|
||||
<Textarea
|
||||
value={keysValue[k]}
|
||||
onChange={(e) =>
|
||||
setKeysValue((old) => [...old.slice(0, k), e.target.value, ...old.slice(k + 1)])
|
||||
}
|
||||
disabled={chatKey === k}
|
||||
placeholder="Enter text..."
|
||||
></Textarea>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
))}
|
||||
<Accordion type="multiple" className="w-full">
|
||||
{Object.keys(tabsState[id.current].formKeysData.input_keys).map(
|
||||
(i, k) => (
|
||||
<div className="flex items-start gap-3" key={k}>
|
||||
<AccordionItem className="w-full" key={k} value={i}>
|
||||
<AccordionTrigger className="flex gap-2">
|
||||
<div className="flex items-center w-full justify-between">
|
||||
<Badge variant="gray" size="md">
|
||||
{i}
|
||||
</Badge>
|
||||
|
||||
<div
|
||||
className="-mb-1"
|
||||
onClick={(event) => {
|
||||
event.stopPropagation();
|
||||
}}
|
||||
>
|
||||
<ToggleShadComponent
|
||||
enabled={chatKey === i}
|
||||
setEnabled={(value) =>
|
||||
handleOnCheckedChange(value, i)
|
||||
}
|
||||
size="small"
|
||||
disabled={tabsState[
|
||||
id.current
|
||||
].formKeysData.handle_keys.some((t) => t === i)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</AccordionTrigger>
|
||||
<AccordionContent>
|
||||
<div className="p-1 flex flex-col gap-2">
|
||||
{tabsState[
|
||||
id.current
|
||||
].formKeysData.handle_keys.some((t) => t === i) && (
|
||||
<div className="font-normal text-muted-foreground ">
|
||||
Source: Component
|
||||
</div>
|
||||
)}
|
||||
<Textarea
|
||||
value={
|
||||
tabsState[id.current].formKeysData.input_keys[i]
|
||||
}
|
||||
onChange={(e) => {
|
||||
setTabsState((old) => {
|
||||
let newTabsState = _.cloneDeep(old);
|
||||
newTabsState[
|
||||
id.current
|
||||
].formKeysData.input_keys[i] = e.target.value;
|
||||
return newTabsState;
|
||||
});
|
||||
}}
|
||||
disabled={chatKey === i}
|
||||
placeholder="Enter text..."
|
||||
></Textarea>
|
||||
</div>
|
||||
</AccordionContent>
|
||||
</AccordionItem>
|
||||
</div>
|
||||
)
|
||||
)}
|
||||
{tabsState[id.current].formKeysData.memory_keys.map((i, k) => (
|
||||
<AccordionItem key={k} value={i}>
|
||||
<div className="flex flex-1 items-center justify-between py-4 font-normal transition-all group text-muted-foreground text-sm">
|
||||
<div className="group-hover:underline"><Badge size="md" variant="gray">{i}</Badge></div>
|
||||
<div className="group-hover:underline">
|
||||
<Badge size="md" variant="gray">
|
||||
{i}
|
||||
</Badge>
|
||||
</div>
|
||||
Used as Memory Key
|
||||
</div>
|
||||
</AccordionItem>
|
||||
|
|
@ -437,14 +496,19 @@ export default function FormModal({
|
|||
</div>
|
||||
<div className="w-full">
|
||||
<div className="flex flex-col rounded-md border bg-muted w-full h-full relative">
|
||||
<div className="absolute right-3 top-3 z-50">
|
||||
<button disabled={lockChat} onClick={() => clearChat()}>
|
||||
<Eraser
|
||||
className={classNames("h-5 w-5", lockChat ? "text-primary animate-pulse" : "text-primary hover:text-gray-600")}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div className="absolute right-3 top-3 z-50">
|
||||
<button disabled={lockChat} onClick={() => clearChat()}>
|
||||
<Eraser
|
||||
className={classNames(
|
||||
"h-5 w-5",
|
||||
lockChat
|
||||
? "text-primary animate-pulse"
|
||||
: "text-primary hover:text-gray-600"
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
<div
|
||||
ref={messagesRef}
|
||||
className="w-full h-full flex-col flex items-center overflow-scroll scrollbar-hide"
|
||||
|
|
@ -486,7 +550,16 @@ export default function FormModal({
|
|||
chatValue={chatValue}
|
||||
lockChat={lockChat}
|
||||
sendMessage={sendMessage}
|
||||
setChatValue={setChatValue}
|
||||
setChatValue={(value) => {
|
||||
setChatValue(value);
|
||||
setTabsState((old) => {
|
||||
let newTabsState = _.cloneDeep(old);
|
||||
newTabsState[id.current].formKeysData.input_keys[
|
||||
chatKey
|
||||
] = chatValue;
|
||||
return newTabsState;
|
||||
});
|
||||
}}
|
||||
inputRef={ref}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,153 +0,0 @@
|
|||
import { Dialog, Transition } from "@headlessui/react";
|
||||
import { XMarkIcon, DocumentTextIcon } from "@heroicons/react/24/outline";
|
||||
import { Fragment, useContext, useRef, useState } from "react";
|
||||
import { PopUpContext } from "../../contexts/popUpContext";
|
||||
import { darkContext } from "../../contexts/darkContext";
|
||||
import { postValidatePrompt } from "../../controllers/API";
|
||||
import { alertContext } from "../../contexts/alertContext";
|
||||
export default function PromptAreaModal({
|
||||
value,
|
||||
setValue,
|
||||
}: {
|
||||
setValue: (value: string) => void;
|
||||
value: string;
|
||||
}) {
|
||||
const [open, setOpen] = useState(true);
|
||||
const [myValue, setMyValue] = useState(value);
|
||||
const { dark } = useContext(darkContext);
|
||||
const { setErrorData, setSuccessData } = useContext(alertContext);
|
||||
const { closePopUp } = useContext(PopUpContext);
|
||||
const ref = useRef();
|
||||
function setModalOpen(x: boolean) {
|
||||
setOpen(x);
|
||||
if (x === false) {
|
||||
setTimeout(() => {
|
||||
closePopUp();
|
||||
}, 300);
|
||||
}
|
||||
}
|
||||
return (
|
||||
<Transition.Root show={open} appear={true} as={Fragment}>
|
||||
<Dialog
|
||||
as="div"
|
||||
className="relative z-10"
|
||||
onClose={setModalOpen}
|
||||
initialFocus={ref}
|
||||
>
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0"
|
||||
enterTo="opacity-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100"
|
||||
leaveTo="opacity-0"
|
||||
>
|
||||
<div className="fixed inset-0 bg-ring dark:bg-gray-600 dark:bg-opacity-75 bg-opacity-75 transition-opacity" />
|
||||
</Transition.Child>
|
||||
|
||||
<div className="fixed inset-0 z-10 overflow-y-auto">
|
||||
<div className="flex h-full items-end justify-center p-4 text-center sm:items-center sm:p-0">
|
||||
<Transition.Child
|
||||
as={Fragment}
|
||||
enter="ease-out duration-300"
|
||||
enterFrom="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
enterTo="opacity-100 translate-y-0 sm:scale-100"
|
||||
leave="ease-in duration-200"
|
||||
leaveFrom="opacity-100 translate-y-0 sm:scale-100"
|
||||
leaveTo="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
<Dialog.Panel className="relative flex flex-col justify-between transform h-[600px] overflow-hidden rounded-lg bg-white dark:bg-gray-800 text-left shadow-xl transition-all sm:my-8 w-[700px]">
|
||||
<div className=" z-50 absolute top-0 right-0 hidden pt-4 pr-4 sm:block">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md text-gray-400 hover:text-gray-500"
|
||||
onClick={() => {
|
||||
setModalOpen(false);
|
||||
}}
|
||||
>
|
||||
<span className="sr-only">Close</span>
|
||||
<XMarkIcon className="h-6 w-6" aria-hidden="true" />
|
||||
</button>
|
||||
</div>
|
||||
<div className="h-full w-full flex flex-col justify-center items-center">
|
||||
<div className="flex w-full pb-4 z-10 justify-center shadow-sm">
|
||||
<div className="mx-auto mt-4 flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-blue-100 dark:bg-gray-900 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<DocumentTextIcon
|
||||
className="h-6 w-6 text-blue-600"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-4 text-center sm:ml-4 sm:text-left">
|
||||
<Dialog.Title
|
||||
as="h3"
|
||||
className="text-lg font-medium dark:text-white leading-10 text-gray-900"
|
||||
>
|
||||
Edit Prompt
|
||||
</Dialog.Title>
|
||||
</div>
|
||||
</div>
|
||||
<div className="h-full w-full bg-accent overflow-auto dark:bg-gray-900 p-4 gap-4 flex flex-row justify-center items-center">
|
||||
<div className="flex h-full w-full">
|
||||
<div className="overflow-hidden px-4 py-5 sm:p-6 w-full h-full rounded-lg bg-white dark:bg-gray-800 shadow">
|
||||
<textarea
|
||||
ref={ref}
|
||||
className="form-input h-full w-full rounded-lg border-gray-300 dark:border-gray-700 dark:bg-gray-900 dark:text-white"
|
||||
value={myValue}
|
||||
onChange={(e) => {
|
||||
setMyValue(e.target.value);
|
||||
setValue(e.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="bg-gray-200 dark:bg-gray-900 w-full pb-3 flex flex-row-reverse px-4">
|
||||
<button
|
||||
type="button"
|
||||
className="inline-flex w-full justify-center rounded-md border border-transparent bg-indigo-600 px-4 py-2 text-base font-medium text-white shadow-sm hover:bg-ring focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:ring-offset-1 sm:ml-3 sm:w-auto sm:text-sm"
|
||||
onClick={() => {
|
||||
postValidatePrompt(myValue)
|
||||
.then((apiReturn) => {
|
||||
if (apiReturn.data) {
|
||||
let inputVariables =
|
||||
apiReturn.data.input_variables;
|
||||
if (inputVariables.length === 0) {
|
||||
setErrorData({
|
||||
title:
|
||||
"The template you are attempting to use does not contain any variables for data entry.",
|
||||
});
|
||||
} else {
|
||||
setSuccessData({
|
||||
title: "Prompt is ready",
|
||||
});
|
||||
setModalOpen(false);
|
||||
setValue(myValue);
|
||||
}
|
||||
} else {
|
||||
setErrorData({
|
||||
title: "Something went wrong, please try again",
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
return setErrorData({
|
||||
title:
|
||||
"There is something wrong with this prompt, please review it",
|
||||
list: [error.response.data.detail],
|
||||
});
|
||||
});
|
||||
}}
|
||||
>
|
||||
Check & Save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog.Panel>
|
||||
</Transition.Child>
|
||||
</div>
|
||||
</div>
|
||||
</Dialog>
|
||||
</Transition.Root>
|
||||
);
|
||||
}
|
||||
|
|
@ -37,6 +37,7 @@ const nodeTypes = {
|
|||
export default function Page({ flow }: { flow: FlowType }) {
|
||||
let {
|
||||
updateFlow,
|
||||
uploadFlow,
|
||||
disableCopyPaste,
|
||||
addFlow,
|
||||
getNodeId,
|
||||
|
|
@ -151,6 +152,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
return {
|
||||
...prev,
|
||||
[tabId]: {
|
||||
...prev[tabId],
|
||||
isPending: true,
|
||||
},
|
||||
};
|
||||
|
|
@ -166,6 +168,7 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
return {
|
||||
...prev,
|
||||
[tabId]: {
|
||||
...prev[tabId],
|
||||
isPending: true,
|
||||
},
|
||||
};
|
||||
|
|
@ -217,62 +220,72 @@ export default function Page({ flow }: { flow: FlowType }) {
|
|||
|
||||
const onDragOver = useCallback((event: React.DragEvent) => {
|
||||
event.preventDefault();
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
if (event.dataTransfer.types.some((t) => t === "nodedata")) {
|
||||
event.dataTransfer.dropEffect = "move";
|
||||
} else {
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
}
|
||||
}, []);
|
||||
|
||||
const onDrop = useCallback(
|
||||
(event: React.DragEvent) => {
|
||||
event.preventDefault();
|
||||
takeSnapshot();
|
||||
if (event.dataTransfer.types.some((t) => t === "nodedata")) {
|
||||
takeSnapshot();
|
||||
|
||||
// Get the current bounds of the ReactFlow wrapper element
|
||||
const reactflowBounds = reactFlowWrapper.current.getBoundingClientRect();
|
||||
// Get the current bounds of the ReactFlow wrapper element
|
||||
const reactflowBounds =
|
||||
reactFlowWrapper.current.getBoundingClientRect();
|
||||
|
||||
// Extract the data from the drag event and parse it as a JSON object
|
||||
let data: { type: string; node?: APIClassType } = JSON.parse(
|
||||
event.dataTransfer.getData("json")
|
||||
);
|
||||
// Extract the data from the drag event and parse it as a JSON object
|
||||
let data: { type: string; node?: APIClassType } = JSON.parse(
|
||||
event.dataTransfer.getData("nodedata")
|
||||
);
|
||||
|
||||
// If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node
|
||||
// Calculate the position where the node should be created
|
||||
const position = reactFlowInstance.project({
|
||||
x: event.clientX - reactflowBounds.left,
|
||||
y: event.clientY - reactflowBounds.top,
|
||||
});
|
||||
// If data type is not "chatInput" or if there are no "chatInputNode" nodes present in the ReactFlow instance, create a new node
|
||||
// Calculate the position where the node should be created
|
||||
const position = reactFlowInstance.project({
|
||||
x: event.clientX - reactflowBounds.left,
|
||||
y: event.clientY - reactflowBounds.top,
|
||||
});
|
||||
|
||||
// Generate a unique node ID
|
||||
let { type } = data;
|
||||
let newId = getNodeId(type);
|
||||
let newNode: NodeType;
|
||||
// Generate a unique node ID
|
||||
let { type } = data;
|
||||
let newId = getNodeId(type);
|
||||
let newNode: NodeType;
|
||||
|
||||
if (data.type !== "groupNode") {
|
||||
// Create a new node object
|
||||
newNode = {
|
||||
id: newId,
|
||||
type: "genericNode",
|
||||
position,
|
||||
data: {
|
||||
...data,
|
||||
if (data.type !== "groupNode") {
|
||||
// Create a new node object
|
||||
newNode = {
|
||||
id: newId,
|
||||
value: null,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// Create a new node object
|
||||
newNode = {
|
||||
id: newId,
|
||||
type: "genericNode",
|
||||
position,
|
||||
data: {
|
||||
...data,
|
||||
type: "genericNode",
|
||||
position,
|
||||
data: {
|
||||
...data,
|
||||
id: newId,
|
||||
value: null,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
// Create a new node object
|
||||
newNode = {
|
||||
id: newId,
|
||||
value: null,
|
||||
},
|
||||
};
|
||||
type: "genericNode",
|
||||
position,
|
||||
data: {
|
||||
...data,
|
||||
id: newId,
|
||||
value: null,
|
||||
},
|
||||
};
|
||||
|
||||
// Add the new node to the list of nodes in state
|
||||
// Add the new node to the list of nodes in state
|
||||
}
|
||||
setNodes((nds) => nds.concat(newNode));
|
||||
} else if (event.dataTransfer.types.some((t) => t === "Files")) {
|
||||
takeSnapshot();
|
||||
uploadFlow(false, event.dataTransfer.files.item(0));
|
||||
}
|
||||
setNodes((nds) => nds.concat(newNode));
|
||||
},
|
||||
// Specify dependencies for useCallback
|
||||
[getNodeId, reactFlowInstance, setErrorData, setNodes, takeSnapshot]
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ export default function ExtraSidebar() {
|
|||
crt.classList.add("cursor-grabbing");
|
||||
document.body.appendChild(crt);
|
||||
event.dataTransfer.setDragImage(crt, 0, 0);
|
||||
event.dataTransfer.setData("json", JSON.stringify(data));
|
||||
event.dataTransfer.setData("nodedata", JSON.stringify(data));
|
||||
}
|
||||
|
||||
function handleSearchInput(e: string) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ export type APIClassType = {
|
|||
description: string;
|
||||
template: APITemplateType;
|
||||
display_name: string;
|
||||
input_types?: Array<string>;
|
||||
[key: string]: Array<string> | string | APITemplateType;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ export type ParameterComponentType = {
|
|||
name?: string;
|
||||
tooltipTitle: string;
|
||||
dataContext?: typesContextType;
|
||||
optionalHandle?: Array<String>;
|
||||
};
|
||||
export type InputListComponentType = {
|
||||
value: string[];
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ export type TabsContextType = {
|
|||
downloadFlow: (flow: FlowType) => void;
|
||||
downloadFlows: () => void;
|
||||
uploadFlows: () => void;
|
||||
uploadFlow: (newFlow?: boolean) => void;
|
||||
uploadFlow: (newFlow?: boolean, file?: File) => void;
|
||||
hardReset: () => void;
|
||||
//disable CopyPaste
|
||||
disableCopyPaste: boolean;
|
||||
|
|
@ -33,6 +33,6 @@ export type TabsContextType = {
|
|||
export type TabsState = {
|
||||
[key: string]: {
|
||||
isPending: boolean;
|
||||
formKeysData: {input_keys?: Array<string>, memory_keys?: Array<string>};
|
||||
formKeysData: {input_keys?: Object, memory_keys?: Array<string>, handle_keys?: Array<string>};
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@ import { twMerge } from "tailwind-merge";
|
|||
import { ADJECTIVES, DESCRIPTIONS, NOUNS } from "./constants";
|
||||
import { ComponentType, SVGProps } from "react";
|
||||
import {
|
||||
Compass,
|
||||
Cpu,
|
||||
Fingerprint,
|
||||
Gift,
|
||||
|
|
@ -139,6 +140,8 @@ export const nodeColors: { [char: string]: string } = {
|
|||
toolkits: "#DB2C2C",
|
||||
wrappers: "#E6277A",
|
||||
utilities: "#31A3CC",
|
||||
output_parsers: "#E6A627",
|
||||
str: "#049524",
|
||||
unknown: "#9CA3AF",
|
||||
};
|
||||
|
||||
|
|
@ -158,6 +161,7 @@ export const nodeNames: { [char: string]: string } = {
|
|||
wrappers: "Wrappers",
|
||||
textsplitters: "Text Splitters",
|
||||
utilities: "Utilities",
|
||||
output_parsers: "Output Parsers",
|
||||
unknown: "Unknown",
|
||||
};
|
||||
|
||||
|
|
@ -363,6 +367,9 @@ export const nodeIconsLucide: {
|
|||
utilities: Wand2 as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
output_parsers: Compass as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
unknown: HelpCircle as React.ForwardRefExoticComponent<
|
||||
ComponentType<SVGProps<SVGSVGElement>>
|
||||
>,
|
||||
|
|
@ -625,11 +632,11 @@ export function isValidConnection(
|
|||
reactFlowInstance: ReactFlowInstance
|
||||
) {
|
||||
if (
|
||||
sourceHandle.split("|")[0] === targetHandle.split("|")[0] ||
|
||||
targetHandle.split("|")[0].split(";").some((n) => n === sourceHandle.split("|")[0]) ||
|
||||
sourceHandle
|
||||
.split("|")
|
||||
.slice(2)
|
||||
.some((t) => t === targetHandle.split("|")[0]) ||
|
||||
.some((t) => targetHandle.split("|")[0].split(";").some((n) => n === t)) ||
|
||||
targetHandle.split("|")[0] === "str"
|
||||
) {
|
||||
let targetNode = reactFlowInstance.getNode(target).data.node;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue