merge fix

This commit is contained in:
cristhianzl 2024-03-29 17:52:09 -03:00
commit 3a612c874c
89 changed files with 1296 additions and 1059 deletions

View file

@ -55,7 +55,7 @@
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.11",
"react-icons": "^4.10.1",
"react-icons": "^5.0.1",
"react-laag": "^2.0.5",
"react-markdown": "^8.0.7",
"react-router-dom": "^6.15.0",
@ -9363,9 +9363,9 @@
}
},
"node_modules/react-icons": {
"version": "4.12.0",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.12.0.tgz",
"integrity": "sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==",
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.0.1.tgz",
"integrity": "sha512-WqLZJ4bLzlhmsvme6iFdgO8gfZP17rfjYEJ2m9RsZjZ+cc4k1hTzknEz63YS1MeT50kVzoa1Nz36f4BEx+Wigw==",
"peerDependencies": {
"react": "*"
}

View file

@ -50,7 +50,7 @@
"react-cookie": "^4.1.1",
"react-dom": "^18.2.0",
"react-error-boundary": "^4.0.11",
"react-icons": "^4.10.1",
"react-icons": "^5.0.1",
"react-laag": "^2.0.5",
"react-markdown": "^8.0.7",
"react-router-dom": "^6.15.0",

View file

@ -211,10 +211,12 @@ export default function GenericNode({
}
}, [validationStatus, validationStatus?.params]);
// const showNode = data.showNode ?? true;
const [showNode, setShowNode] = useState(data.showNode ?? true);
useEffect(() => {
setShowNode(data.showNode ?? true);
}, [data.showNode]);
const nameEditable = true;
const emojiRegex = /\p{Emoji}/u;
@ -438,7 +440,7 @@ export default function GenericNode({
event.preventDefault();
}}
data-testid={"title-" + data.node?.display_name}
className="generic-node-tooltip-div text-primary"
className="generic-node-tooltip-div cursor-text text-primary"
>
{data.node?.display_name}
</div>

View file

@ -13,6 +13,8 @@ export default function DropdownButton({
firstButtonName,
onFirstBtnClick,
options,
plusButton = false,
dropdownOptions = true,
}: dropdownButtonPropsType): JSX.Element {
const [showOptions, setShowOptions] = useState<boolean>(false);
@ -23,28 +25,33 @@ export default function DropdownButton({
<Button
id="new-project-btn"
variant="primary"
className="relative pr-10"
className={"relative" + dropdownOptions ? "" : " pr-10"}
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
onFirstBtnClick();
}}
>
{plusButton && (
<IconComponent name="Plus" className="main-page-nav-button" />
)}
{firstButtonName}
<div
className="absolute right-2 items-center text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{!showOptions ? (
<IconComponent name="ChevronDown" />
) : (
<IconComponent name="ChevronUp" />
)}
</div>
{dropdownOptions && (
<div
className="absolute right-2 items-center text-muted-foreground"
onClick={(event) => {
event.stopPropagation();
event.preventDefault();
setShowOptions(!showOptions);
}}
>
{!showOptions ? (
<IconComponent name="ChevronDown" />
) : (
<IconComponent name="ChevronUp" />
)}
</div>
)}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent

View file

@ -120,7 +120,7 @@ export default function IOView({
{/* TODO ADAPT TO ALL TYPES OF INPUTS AND OUTPUTS */}
<BaseModal.Header description={CHAT_FORM_DIALOG_SUBTITLE}>
<div className="flex items-center">
<span className="pr-2">Chat</span>
<span className="pr-2">Interaction Panel</span>
<IconComponent
name="prompts"
className="h-6 w-6 pl-1 text-foreground"

View file

@ -1,5 +1,6 @@
import { useContext, useEffect } from "react";
import { FaDiscord, FaGithub, FaTwitter } from "react-icons/fa";
import { FaDiscord, FaGithub } from "react-icons/fa";
import { RiTwitterXFill } from "react-icons/ri";
import { Link, useLocation, useNavigate, useParams } from "react-router-dom";
import AlertDropdown from "../../alerts/alertDropDown";
import { USER_PROJECTS_HEADER } from "../../constants/constants";
@ -127,7 +128,7 @@ export default function Header(): JSX.Element {
rel="noreferrer"
className="text-muted-foreground"
>
<FaTwitter className="side-bar-button-size" />
<RiTwitterXFill className="side-bar-button-size" />
</a>
<a
href="https://discord.gg/EqksyE2EX9"

View file

@ -99,12 +99,16 @@ export default function InputComponent({
type="text"
onBlur={onInputLostFocus}
value={
selectedOption !== "" || !onChange ? selectedOption : value
(selectedOption !== "" || !onChange) && setSelectedOption
? selectedOption
: value
}
autoFocus={autoFocus}
disabled={disabled}
onClick={() => {
(selectedOption !== "" || !onChange) && setShowOptions(true);
(selectedOption !== "" || !onChange) &&
setSelectedOption &&
setShowOptions(true);
}}
required={required}
className={classNames(
@ -115,8 +119,12 @@ export default function InputComponent({
? " text-clip password "
: "",
editNode ? " input-edit-node " : "",
password && selectedOption === "" && editNode ? "pr-8" : "",
password && selectedOption === "" && !editNode ? "pr-10" : "",
password && setSelectedOption ? "pr-16" : "",
(!password && setSelectedOption) ||
(password && !setSelectedOption)
? "pr-8"
: "",
className!
)}
placeholder={password && editNode ? "Key" : placeholder}
@ -179,18 +187,32 @@ export default function InputComponent({
setShowOptions(false);
}}
>
<div className="flex w-full items-center justify-between">
<div className="group flex w-full items-center justify-between">
<div className="flex items-center">
<ForwardedIconComponent
name="Check"
<div
className={cn(
"mr-2 h-4 w-4 text-primary",
"relative mr-2 h-4 w-4",
selectedOption === option
? "opacity-100"
: "opacity-0"
)}
aria-hidden="true"
/>
>
<div className="absolute opacity-100 transition-all group-hover:opacity-0">
<ForwardedIconComponent
name="Check"
className="mr-2 h-4 w-4 text-primary"
aria-hidden="true"
/>
</div>
<div className="absolute opacity-0 transition-all group-hover:opacity-100">
<ForwardedIconComponent
name="X"
className="mr-2 h-4 w-4 text-status-red"
aria-hidden="true"
/>
</div>
</div>
{option}
</div>
{optionButton && optionButton(option)}
@ -206,10 +228,12 @@ export default function InputComponent({
<div
className={cn(
"pointer-events-auto absolute inset-y-0 h-full w-full cursor-pointer",
selectedOption !== "" || !onChange ? "" : "hidden"
(selectedOption !== "" || !onChange) && setSelectedOption
? ""
: "hidden"
)}
onClick={
selectedOption !== "" || !onChange
(selectedOption !== "" || !onChange) && setSelectedOption
? (e) => {
setShowOptions((old) => !old);
e.preventDefault();
@ -221,30 +245,32 @@ export default function InputComponent({
</>
)}
<span
className={cn(
password && selectedOption === "" ? "right-8" : "right-0",
"absolute inset-y-0 flex items-center pr-2.5"
)}
>
<button
onClick={() => {
setShowOptions(!showOptions);
}}
{setSelectedOption && (
<span
className={cn(
selectedOption !== ""
? "text-medium-indigo"
: "text-muted-foreground",
"hover:text-accent-foreground"
password && selectedOption === "" ? "right-8" : "right-0",
"absolute inset-y-0 flex items-center pr-2.5"
)}
>
<ForwardedIconComponent
name={optionsIcon}
className={"h-4 w-4"}
aria-hidden="true"
/>
</button>
</span>
<button
onClick={() => {
setShowOptions(!showOptions);
}}
className={cn(
selectedOption !== ""
? "text-medium-indigo"
: "text-muted-foreground",
"hover:text-accent-foreground"
)}
>
<ForwardedIconComponent
name={optionsIcon}
className={"h-4 w-4"}
aria-hidden="true"
/>
</button>
</span>
)}
{password && selectedOption === "" && (
<button

View file

@ -107,7 +107,7 @@ export const CODE_DIALOG_SUBTITLE =
* @constant
*/
export const CHAT_FORM_DIALOG_SUBTITLE =
"Set up the input variables defined in prompt templates. Interact with agents and chains.";
"Adjust or overwrite input values, inspect outputs or interact through the chat console.";
/**
* The base text for subtitle of Edit Node Dialog
@ -732,12 +732,12 @@ export const PRIORITY_SIDEBAR_ORDER = [
"saved_components",
"inputs",
"outputs",
"prompts",
"data",
"prompt",
"models",
"vectorstores",
"vectorsearch",
"helpers",
"experimental",
];
export const NATIVE_CATEGORIES = [
"inputs",

View file

@ -33,7 +33,7 @@ export default function ChatInput({
<div className="relative">
<Textarea
onKeyDown={(event) => {
if (event.key === "Enter" && !lockChat && !event.shiftKey) {
if (event.key === "Enter" && !event.nativeEvent.isComposing && !lockChat && !event.shiftKey) {
sendMessage();
}
}}

View file

@ -5,7 +5,6 @@ import ShadTooltip from "../../../../components/ShadTooltipComponent";
import IconComponent from "../../../../components/genericIconComponent";
import { Input } from "../../../../components/ui/input";
import { Separator } from "../../../../components/ui/separator";
import { PRIORITY_SIDEBAR_ORDER } from "../../../../constants/constants";
import ExportModal from "../../../../modals/exportModal";
import ShareModal from "../../../../modals/shareModal";
import useAlertStore from "../../../../stores/alertStore";
@ -282,35 +281,7 @@ export default function ExtraSidebar(): JSX.Element {
</div>
</div>
)}
{index === PRIORITY_SIDEBAR_ORDER.length - 1 && (
<>
<a
target={"_blank"}
href="https://langflow.store"
className="components-disclosure-arrangement"
>
<div className="flex gap-4">
{/* BUG ON THIS ICON */}
<SparklesIcon
strokeWidth={1.5}
className="w-[22px] text-primary"
/>
<span className="components-disclosure-title">
Discover More
</span>
</div>
<div className="components-disclosure-div">
<div>
<LinkIcon className="h-4 w-4 text-foreground" />
</div>
</div>
</a>
<div className="p-2 px-4 text-sm font-semibold" key={index}>
Legacy Components
</div>
</>
)}
<DisclosureComponent
openDisc={
getFilterEdge.length !== 0 || search.length !== 0
@ -371,6 +342,32 @@ export default function ExtraSidebar(): JSX.Element {
))}
</div>
</DisclosureComponent>
{index === Object.keys(dataFilter).length - 1 && (
<>
<a
target={"_blank"}
href="https://langflow.store"
className="components-disclosure-arrangement"
>
<div className="flex gap-4">
{/* BUG ON THIS ICON */}
<SparklesIcon
strokeWidth={1.5}
className="w-[22px] text-primary"
/>
<span className="components-disclosure-title">
Discover More
</span>
</div>
<div className="components-disclosure-div">
<div>
<LinkIcon className="h-4 w-4 text-foreground" />
</div>
</div>
</a>
</>
)}
</>
) : (
<div key={index}></div>

View file

@ -85,7 +85,9 @@ export default function NodeToolbarComponent({
const [showModalAdvanced, setShowModalAdvanced] = useState(false);
const [showconfirmShare, setShowconfirmShare] = useState(false);
const [showOverrideModal, setShowOverrideModal] = useState(false);
const [flowComponent, setFlowComponent] = useState<FlowType>();
const [flowComponent, setFlowComponent] = useState<FlowType>(
createFlowComponent(cloneDeep(data), version)
);
const openInNewTab = (url) => {
window.open(url, "_blank", "noreferrer");
@ -285,7 +287,13 @@ export default function NodeToolbarComponent({
event.preventDefault();
setShowconfirmShare((state) => !state);
}
if (selected && (event.ctrlKey || event.metaKey) && event.key === "q") {
if (
selected &&
(event.ctrlKey || event.metaKey) &&
event.shiftKey &&
event.key === "Q"
) {
event.preventDefault();
if (isMinimal) {
setShowState((show) => !show);
@ -341,6 +349,10 @@ export default function NodeToolbarComponent({
title: `${data.id} docs is not available at the moment.`,
});
}
if (selected && (event.ctrlKey || event.metaKey) && event.key === "j") {
event.preventDefault();
downloadNode(flowComponent!);
}
}
document.addEventListener("keydown", onKeyDown);
@ -499,15 +511,17 @@ export default function NodeToolbarComponent({
/>
</SelectItem>
)}
{!hasStore && (
{(!hasStore || !hasApiKey || !validApiKey) && (
<SelectItem value={"Download"}>
<div className="flex">
<IconComponent
name="Download"
className="relative top-0.5 mr-2 h-4 w-4"
/>{" "}
Download{" "}
</div>{" "}
<ToolbarSelectItem
value="Download"
shift={false}
isMac={navigator.userAgent.toUpperCase().includes("MAC")}
icon="Download"
styleObj={{ iconClasses: "relative top-0.5 mr-2 h-4 w-4" }}
keyboardKey={"J"}
dataTestId={"Dowload-button-nodeToolbar"}
/>
</SelectItem>
)}
<SelectItem
@ -525,24 +539,14 @@ export default function NodeToolbarComponent({
</SelectItem>
{isMinimal && (
<SelectItem value={"show"}>
<div className="flex">
<IconComponent
name={showNode ? "Minimize2" : "Maximize2"}
className="relative top-0.5 mr-2 h-4 w-4"
/>
{showNode ? "Minimize" : "Expand"}
{navigator.userAgent.toUpperCase().includes("MAC") ? (
<IconComponent
name="Command"
className="absolute right-[1.25rem] top-[0.65em] h-3.5 w-3.5 stroke-2"
></IconComponent>
) : (
<span className="absolute right-[1.30rem] top-[0.40em] stroke-2">
Ctrl +{" "}
</span>
)}
<span className="absolute right-2 top-[0.46em]">Q</span>
</div>
<ToolbarSelectItem
icon={showNode ? "Minimize2" : "Maximize2"}
value={showNode ? "Minimize" : "Expand"}
isMac={navigator.userAgent.toUpperCase().includes("MAC")}
shift={true}
keyboardKey={"Q"}
dataTestId={"minimize-button-nodeToolbar"}
/>
</SelectItem>
)}
{isGroup && (

View file

@ -105,6 +105,8 @@ export default function HomePage(): JSX.Element {
firstButtonName="New Project"
onFirstBtnClick={() => setOpenModal(true)}
options={dropdownOptions}
plusButton={true}
dropdownOptions={false}
/>
</div>
}

View file

@ -4,7 +4,7 @@ import { DarkStoreType } from "../types/zustand/dark";
const startedStars = Number(window.localStorage.getItem("githubStars")) ?? 0;
export const useDarkStore = create<DarkStoreType>((set) => ({
export const useDarkStore = create<DarkStoreType>((set, get) => ({
dark: JSON.parse(window.localStorage.getItem("isDark")!) ?? false,
stars: startedStars,
version: "",
@ -15,14 +15,23 @@ export const useDarkStore = create<DarkStoreType>((set) => ({
});
},
refreshStars: () => {
if (window.localStorage.getItem("githubStars") !== null) {
set(() => ({ stars: startedStars }));
return;
let lastUpdated = window.localStorage.getItem("githubStarsLastUpdated");
let diff = 0;
// check if lastUpdated actually exists
if (lastUpdated !== null) {
diff = Math.abs(new Date().getTime() - new Date(lastUpdated).getTime());
}
getRepoStars("logspace-ai", "langflow").then((res) => {
window.localStorage.setItem("githubStars", res.toString());
set(() => ({ stars: res }));
});
// if lastUpdated is null or the difference is greater than 2 hours
if (lastUpdated === null || diff > 7200000) {
getRepoStars("logspace-ai", "langflow").then((res) => {
window.localStorage.setItem("githubStars", res.toString());
window.localStorage.setItem(
"githubStarsLastUpdated",
new Date().toString()
);
set(() => ({ stars: res, lastUpdated: new Date() }));
});
}
},
}));

View file

@ -689,6 +689,8 @@ export type dropdownButtonPropsType = {
firstButtonName: string;
onFirstBtnClick: () => void;
options: Array<{ name: string; onBtnClick: () => void }>;
plusButton?: boolean;
dropdownOptions?: boolean;
};
export type IOInputProps = {

View file

@ -119,6 +119,8 @@ export async function buildVertices({
try {
onValidateNodes(verticesOrderResponse.verticesToRun);
} catch (e) {
useFlowStore.getState().setIsBuilding(false);
return;
}
}

View file

@ -1170,7 +1170,7 @@ export function downloadNode(NodeFLow: FlowType) {
type: "application/json",
});
element.href = URL.createObjectURL(file);
element.download = `${NodeFLow.name}.json`;
element.download = `${NodeFLow?.name??"node"}.json`;
element.click();
}

View file

@ -236,6 +236,7 @@ export const nodeColors: { [char: string]: string } = {
embeddings: "#42BAA7",
documentloaders: "#7AAE42",
vectorstores: "#AA8742",
vectorsearch: "#AA8742",
textsplitters: "#B47CB5",
toolkits: "#DB2C2C",
wrappers: "#E6277A",
@ -248,6 +249,7 @@ export const nodeColors: { [char: string]: string } = {
retrievers: "#e6b25a",
unknown: "#9CA3AF",
custom_components: "#ab11ab",
Record:"#31a3cc"
};
export const nodeNames: { [char: string]: string } = {
@ -267,6 +269,7 @@ export const nodeNames: { [char: string]: string } = {
embeddings: "Embeddings",
documentloaders: "Loaders",
vectorstores: "Vector Stores",
vectorsearch: "Vector Search",
toolkits: "Toolkits",
wrappers: "Wrappers",
textsplitters: "Text Splitters",
@ -369,6 +372,7 @@ export const nodeIconsLucide: iconsType = {
saved_components: GradientSave,
documentloaders: Paperclip,
vectorstores: Layers,
vectorsearch: Search,
toolkits: Package2,
textsplitters: Scissors,
wrappers: Gift,
@ -387,7 +391,6 @@ export const nodeIconsLucide: iconsType = {
Trash2,
Boxes,
Network,
X,
XCircle,
Info,
CheckCircle2,

View file

@ -0,0 +1,358 @@
{
"id": "3f5e860d-1676-4fba-9d86-06db9f51e43a",
"data": {
"nodes": [
{
"id": "ChatInput-7hPDT",
"type": "genericNode",
"position": {
"x": 687,
"y": 41.9375
},
"data": {
"type": "ChatInput",
"node": {
"template": {
"code": {
"type": "code",
"required": true,
"placeholder": "",
"list": false,
"show": true,
"multiline": true,
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Used to get user input from the chat.\"\n icon = \"ChatInput\"\n\n def build(\n self,\n sender: Optional[str] = \"User\",\n sender_name: Optional[str] = \"User\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
"fileTypes": [],
"file_path": "",
"password": false,
"name": "code",
"advanced": true,
"dynamic": true,
"info": "",
"load_from_db": false,
"title_case": false
},
"input_value": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": true,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "input_value",
"display_name": "Message",
"advanced": false,
"input_types": ["Text"],
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false
},
"return_record": {
"type": "bool",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"value": false,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "return_record",
"display_name": "Return Record",
"advanced": false,
"dynamic": false,
"info": "Return the message as a record containing the sender, sender_name, and session_id.",
"load_from_db": false,
"title_case": false
},
"sender": {
"type": "str",
"required": false,
"placeholder": "",
"list": true,
"show": true,
"multiline": false,
"value": "User",
"fileTypes": [],
"file_path": "",
"password": false,
"options": ["Machine", "User"],
"name": "sender",
"display_name": "Sender Type",
"advanced": false,
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"sender_name": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"value": "User",
"fileTypes": [],
"file_path": "",
"password": false,
"name": "sender_name",
"display_name": "Sender Name",
"advanced": false,
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"session_id": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "session_id",
"display_name": "Session ID",
"advanced": true,
"dynamic": false,
"info": "If provided, the message will be stored in the memory.",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"_type": "CustomComponent"
},
"description": "Used to get user input from the chat.",
"icon": "ChatInput",
"base_classes": ["Text", "object", "str", "Record"],
"display_name": "Chat Input",
"documentation": "",
"custom_fields": {
"sender": null,
"sender_name": null,
"input_value": null,
"session_id": null,
"return_record": null
},
"output_types": ["Text", "Record"],
"field_formatters": {},
"frozen": false,
"field_order": [],
"beta": false
},
"id": "ChatInput-7hPDT"
},
"selected": false,
"width": 384,
"height": 569,
"positionAbsolute": {
"x": 687,
"y": 41.9375
},
"dragging": false
},
{
"id": "ChatOutput-4sm9Z",
"type": "genericNode",
"position": {
"x": 4.939451516507972,
"y": 103.3695231904992
},
"data": {
"type": "ChatOutput",
"node": {
"template": {
"code": {
"type": "code",
"required": true,
"placeholder": "",
"list": false,
"show": true,
"multiline": true,
"value": "from typing import Optional, Union\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.field_typing import Text\nfrom langflow.schema import Record\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Used to send a message to the chat.\"\n icon = \"ChatOutput\"\n\n def build(\n self,\n sender: Optional[str] = \"Machine\",\n sender_name: Optional[str] = \"AI\",\n input_value: Optional[str] = None,\n session_id: Optional[str] = None,\n return_record: Optional[bool] = False,\n ) -> Union[Text, Record]:\n return super().build(\n sender=sender,\n sender_name=sender_name,\n input_value=input_value,\n session_id=session_id,\n return_record=return_record,\n )\n",
"fileTypes": [],
"file_path": "",
"password": false,
"name": "code",
"advanced": true,
"dynamic": true,
"info": "",
"load_from_db": false,
"title_case": false
},
"input_value": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": true,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "input_value",
"display_name": "Message",
"advanced": false,
"input_types": ["Text"],
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false
},
"return_record": {
"type": "bool",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"value": false,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "return_record",
"display_name": "Return Record",
"advanced": false,
"dynamic": false,
"info": "Return the message as a record containing the sender, sender_name, and session_id.",
"load_from_db": false,
"title_case": false
},
"sender": {
"type": "str",
"required": false,
"placeholder": "",
"list": true,
"show": true,
"multiline": false,
"value": "Machine",
"fileTypes": [],
"file_path": "",
"password": false,
"options": ["Machine", "User"],
"name": "sender",
"display_name": "Sender Type",
"advanced": false,
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"sender_name": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"value": "AI",
"fileTypes": [],
"file_path": "",
"password": false,
"name": "sender_name",
"display_name": "Sender Name",
"advanced": false,
"dynamic": false,
"info": "",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"session_id": {
"type": "str",
"required": false,
"placeholder": "",
"list": false,
"show": true,
"multiline": false,
"fileTypes": [],
"file_path": "",
"password": false,
"name": "session_id",
"display_name": "Session ID",
"advanced": true,
"dynamic": false,
"info": "If provided, the message will be stored in the memory.",
"load_from_db": false,
"title_case": false,
"input_types": ["Text"]
},
"_type": "CustomComponent"
},
"description": "Used to send a message to the chat.",
"icon": "ChatOutput",
"base_classes": ["Text", "object", "str", "Record"],
"display_name": "Chat Output",
"documentation": "",
"custom_fields": {
"sender": null,
"sender_name": null,
"input_value": null,
"session_id": null,
"return_record": null
},
"output_types": ["Text", "Record"],
"field_formatters": {},
"frozen": false,
"field_order": [],
"beta": false
},
"id": "ChatOutput-4sm9Z"
},
"selected": true,
"width": 384,
"height": 569,
"positionAbsolute": {
"x": 4.939451516507972,
"y": 103.3695231904992
},
"dragging": false
}
],
"edges": [
{
"source": "ChatOutput-4sm9Z",
"sourceHandle": "{œbaseClassesœ:[œTextœ,œobjectœ,œstrœ,œRecordœ],œdataTypeœ:œChatOutputœ,œidœ:œChatOutput-4sm9Zœ}",
"target": "ChatInput-7hPDT",
"targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatInput-7hPDTœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}",
"data": {
"targetHandle": {
"fieldName": "input_value",
"id": "ChatInput-7hPDT",
"inputTypes": ["Text"],
"type": "str"
},
"sourceHandle": {
"baseClasses": ["Text", "object", "str", "Record"],
"dataType": "ChatOutput",
"id": "ChatOutput-4sm9Z"
}
},
"style": {
"stroke": "#555"
},
"className": "stroke-gray-900 stroke-connection",
"id": "reactflow__edge-ChatOutput-4sm9Z{œbaseClassesœ:[œTextœ,œobjectœ,œstrœ,œRecordœ],œdataTypeœ:œChatOutputœ,œidœ:œChatOutput-4sm9Zœ}-ChatInput-7hPDT{œfieldNameœ:œinput_valueœ,œidœ:œChatInput-7hPDTœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}"
}
],
"viewport": {
"x": 316.78239920440234,
"y": 182.84104875768622,
"zoom": 0.6070974421975234
}
},
"description": "flow to test ChatInput and output",
"name": "ChatITest",
"last_tested_version": "1.0.0a0",
"is_component": false
}

View file

@ -0,0 +1,46 @@
import { test } from "@playwright/test";
import { readFileSync } from "fs";
test.beforeEach(async ({ page }) => {
// await page.waitForTimeout(20000);
// test.setTimeout(120000);
});
test("chat_io_teste", async ({ page }) => {
await page.goto("/");
await page.locator("span").filter({ hasText: "My Collection" }).isVisible();
// Read your file into a buffer.
const jsonContent = readFileSync(
"tests/end-to-end/assets/ChatTest.json",
"utf-8"
);
// Create the DataTransfer and File
const dataTransfer = await page.evaluateHandle((data) => {
const dt = new DataTransfer();
// Convert the buffer to a hex array
const file = new File([data], "ChatTest.json", {
type: "application/json",
});
dt.items.add(file);
return dt;
}, jsonContent);
await page.locator('//*[@id="new-project-btn"]').click();
await page.waitForTimeout(2000);
await page.getByTestId("blank-flow").click();
await page.waitForTimeout(2000);
// Now dispatch
await page.dispatchEvent(
'//*[@id="react-flow-id"]/div[1]/div[1]/div',
"drop",
{
dataTransfer,
}
);
await page.getByLabel("fit view").click();
await page.getByText("Run", { exact: true }).click();
await page.getByPlaceholder("Send a message...").click();
await page.getByPlaceholder("Send a message...").fill("teste");
await page.getByRole("button").nth(1).click();
});

View file

@ -25,86 +25,31 @@ test("CodeAreaModalComponent", async ({ page }) => {
await page.getByTestId("code-button-modal").click();
let value = await page.locator('//*[@id="codeValue"]').inputValue();
if (
value !=
'def python_function(text: str) -> str: """This is a default python function that returns the input text""" return text'
) {
expect(false).toBeTruthy();
}
const code =
'def python_function(text: str) -> str:\n """This is a default python function that returns the input text"""\n return text';
const wCode =
'def python_function(text: str) -> st: """This is a default python function that returns the input text""" return text';
const assertCode =
'def python_function(text: str) -> str: """This is a default python function that returns the input text""" return text';
await page
.locator("#CodeEditor div")
.filter({ hasText: "def python_function(text: str" })
.nth(1)
.click();
await page.locator("textarea").press("Control+a");
await page.locator("textarea").fill(wCode);
await page.locator('//*[@id="checkAndSaveBtn"]').click();
await page.getByTestId("div-generic-node").click();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="showdescription"]').click();
await page.waitForTimeout(1000);
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
await page.getByText("invalid syntax (<unknown>, line 1)").isVisible()
).toBeTruthy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeFalsy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeFalsy();
await page.locator('//*[@id="showdescription"]').click();
expect(
await page.locator('//*[@id="showdescription"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="showname"]').click();
expect(await page.locator('//*[@id="showname"]').isChecked()).toBeTruthy();
await page.locator('//*[@id="showreturn_direct"]').click();
expect(
await page.locator('//*[@id="showreturn_direct"]').isChecked()
).toBeTruthy();
await page.locator('//*[@id="saveChangesBtn"]').click();
const plusButtonLocator = page.locator('//*[@id="code-input-0"]');
const elementCount = await plusButtonLocator.count();
if (elementCount === 0) {
expect(true).toBeTruthy();
await page.getByTestId("more-options-modal").click();
await page.getByTestId("edit-button-modal").click();
await page.locator('//*[@id="saveChangesBtn"]').click();
await page.getByTestId("div-generic-node").click();
await page.getByTestId("code-button-modal").click();
}
await page.locator("textarea").press("Control+a");
await page.locator("textarea").fill(wCode);
await page.locator("textarea").fill(code);
await page.locator('//*[@id="checkAndSaveBtn"]').click();
expect(await page.getByText("Code is ready to run").isVisible()).toBeTruthy();
await page.getByTestId("code-button-modal").click();
expect(await page.locator('//*[@id="codeValue"]').inputValue()).toBe(
assertCode
);
});

View file

@ -0,0 +1,17 @@
import { expect, test } from "@playwright/test";
test("curl_api_generation", async ({ page, context }) => {
await page.goto("/");
await page.locator('//*[@id="new-project-btn"]').click();
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await page.getByRole("heading", { name: "Data Ingestion" }).click();
await page.waitForTimeout(2000);
await page.getByText("API", { exact: true }).click();
await page.getByRole("tab", { name: "cURL" }).click();
await page.getByRole("button", { name: "Copy Code" }).click();
const handle = await page.evaluateHandle(() =>
navigator.clipboard.readText()
);
const clipboardContent = await handle.jsonValue();
expect(clipboardContent.length).toBeGreaterThan(0);
});

View file

@ -0,0 +1,17 @@
import { expect, test } from "@playwright/test";
test("python_api_generation", async ({ page, context }) => {
await page.goto("/");
await page.locator('//*[@id="new-project-btn"]').click();
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await page.getByRole("heading", { name: "Data Ingestion" }).click();
await page.waitForTimeout(2000);
await page.getByText("API", { exact: true }).click();
await page.getByRole("tab", { name: "Python API" }).click();
await page.getByRole("button", { name: "Copy Code" }).click();
const handle = await page.evaluateHandle(() =>
navigator.clipboard.readText()
);
const clipboardContent = await handle.jsonValue();
expect(clipboardContent.length).toBeGreaterThan(0);
});

View file

@ -0,0 +1,38 @@
import { expect, test } from "@playwright/test";
test("curl_api_generation", async ({ page, context }) => {
await page.goto("/");
await page.locator('//*[@id="new-project-btn"]').click();
await context.grantPermissions(["clipboard-read", "clipboard-write"]);
await page.getByRole("heading", { name: "Data Ingestion" }).click();
await page.waitForTimeout(2000);
await page.getByText("API", { exact: true }).click();
await page.getByRole("tab", { name: "cURL" }).click();
await page.getByRole("button", { name: "Copy Code" }).click();
const handle = await page.evaluateHandle(() =>
navigator.clipboard.readText()
);
const clipboardContent = await handle.jsonValue();
const oldValue = clipboardContent;
expect(clipboardContent.length).toBeGreaterThan(0);
await page.getByRole("tab", { name: "Tweaks" }).click();
await page
.getByRole("heading", { name: "URL" })
.locator("div")
.first()
.click();
await page.getByRole("textbox", { name: "Type something..." }).click();
await page
.getByRole("textbox", { name: "Type something..." })
.press("Control+a");
await page.getByRole("textbox", { name: "Type something..." }).fill("teste");
await page.getByRole("tab", { name: "cURL" }).click();
await page.getByRole("button", { name: "Copy Code" }).click();
const handle2 = await page.evaluateHandle(() =>
navigator.clipboard.readText()
);
const clipboardContent2 = await handle2.jsonValue();
const newValue = clipboardContent2;
expect(oldValue).not.toBe(newValue);
expect(clipboardContent2.length).toBeGreaterThan(clipboardContent.length);
});