From 7f371af0e503e7d9d4330b79bba770b986012d08 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 24 May 2024 14:28:14 +0200 Subject: [PATCH 01/52] Added button as footer and a submit form --- .../addNewVariableButton.tsx | 11 ++-- src/frontend/src/modals/baseModal/index.tsx | 57 ++++++++++++++++--- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx index 5da7d1461..0076fa926 100644 --- a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx +++ b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx @@ -70,7 +70,12 @@ export default function AddNewVariableButton({ children }): JSX.Element { }); } return ( - + - - - + ); } diff --git a/src/frontend/src/modals/baseModal/index.tsx b/src/frontend/src/modals/baseModal/index.tsx index 19fc80711..b0a3c36bb 100644 --- a/src/frontend/src/modals/baseModal/index.tsx +++ b/src/frontend/src/modals/baseModal/index.tsx @@ -15,6 +15,7 @@ import { DialogContent as ModalContent, } from "../../components/ui/dialog-with-no-close"; +import { Button } from "../../components/ui/button"; import { modalHeaderType } from "../../types/components"; import { cn } from "../../utils/utils"; @@ -61,8 +62,23 @@ const Header: React.FC<{ children: ReactNode; description: string | null }> = ({ ); }; -const Footer: React.FC<{ children: ReactNode }> = ({ children }) => { - return <>{children}; +const Footer: React.FC<{ + children?: ReactNode; + submit?: { label: string; icon?: ReactNode }; +}> = ({ children, submit }) => { + return submit ? ( +
+ {children ??
} +
+ +
+
+ ) : ( + <>{children && children} + ); }; interface BaseModalProps { children: [ @@ -91,6 +107,7 @@ interface BaseModalProps { disable?: boolean; onChangeOpenModal?: (open?: boolean) => void; type?: "modal" | "dialog"; + onSubmit?: () => void; } function BaseModal({ open, @@ -99,6 +116,7 @@ function BaseModal({ size = "large", onChangeOpenModal, type = "dialog", + onSubmit, }: BaseModalProps) { const headerChild = React.Children.toArray(children).find( (child) => (child as React.ReactElement).type === Header, @@ -212,13 +230,34 @@ function BaseModal({
{headerChild}
-
- {ContentChild} -
- {ContentFooter && ( -
{ContentFooter}
+ {onSubmit ? ( +
{ + event.preventDefault(); + onSubmit(); + }} + className="flex flex-col gap-4" + > +
+ {ContentChild} +
+ {ContentFooter && ( +
{ContentFooter}
+ )} +
+ ) : ( + <> +
+ {ContentChild} +
+ {ContentFooter && ( +
{ContentFooter}
+ )} + )} From bc5cd9e7e223e6ac16154f598d53565ac9832a05 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 24 May 2024 14:34:04 +0200 Subject: [PATCH 02/52] Fixed submitting on storeApiKeyModal --- .../src/modals/storeApiKeyModal/index.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/frontend/src/modals/storeApiKeyModal/index.tsx b/src/frontend/src/modals/storeApiKeyModal/index.tsx index cdd2419a5..67e1de6bb 100644 --- a/src/frontend/src/modals/storeApiKeyModal/index.tsx +++ b/src/frontend/src/modals/storeApiKeyModal/index.tsx @@ -58,7 +58,7 @@ export default function StoreApiKeyModal({ setHasApiKey(false); setValidApiKey(false); setLoadingApiKey(false); - } + }, ); } }; @@ -71,8 +71,8 @@ export default function StoreApiKeyModal({ (hasApiKey && !validApiKey ? INVALID_API_KEY : !hasApiKey - ? NO_API_KEY - : "") + INSERT_API_KEY + ? NO_API_KEY + : "") + INSERT_API_KEY } > API Key @@ -121,6 +121,7 @@ export default function StoreApiKeyModal({ - - - +
From 741731c0e2c55d5622000751ef016c82b1f437c0 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 24 May 2024 14:38:08 +0200 Subject: [PATCH 03/52] Added close button when submit is present --- src/frontend/src/modals/baseModal/index.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/modals/baseModal/index.tsx b/src/frontend/src/modals/baseModal/index.tsx index b0a3c36bb..aefb8c4d1 100644 --- a/src/frontend/src/modals/baseModal/index.tsx +++ b/src/frontend/src/modals/baseModal/index.tsx @@ -15,6 +15,7 @@ import { DialogContent as ModalContent, } from "../../components/ui/dialog-with-no-close"; +import { DialogClose } from "@radix-ui/react-dialog"; import { Button } from "../../components/ui/button"; import { modalHeaderType } from "../../types/components"; import { cn } from "../../utils/utils"; @@ -69,7 +70,12 @@ const Footer: React.FC<{ return submit ? (
{children ??
} -
+
+ + + -
- + ); diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index 82350b57e..4d7453619 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -3,7 +3,6 @@ import AccordionComponent from "../../components/accordionComponent"; import IconComponent from "../../components/genericIconComponent"; import ShadTooltip from "../../components/shadTooltipComponent"; import { Badge } from "../../components/ui/badge"; -import { Button } from "../../components/ui/button"; import { Tabs, TabsContent, @@ -121,6 +120,7 @@ export default function IOModal({ open={open} setOpen={setOpen} disable={disable} + onSubmit={() => sendMessage(1)} > {children} {/* TODO ADAPT TO ALL TYPES OF INPUTS AND OUTPUTS */} @@ -371,13 +371,10 @@ export default function IOModal({
{!haveChat ? ( - -
- -
-
+ ), + }} + /> ) : ( <> )} diff --git a/src/frontend/src/modals/baseModal/index.tsx b/src/frontend/src/modals/baseModal/index.tsx index aefb8c4d1..a899bd071 100644 --- a/src/frontend/src/modals/baseModal/index.tsx +++ b/src/frontend/src/modals/baseModal/index.tsx @@ -16,6 +16,7 @@ import { } from "../../components/ui/dialog-with-no-close"; import { DialogClose } from "@radix-ui/react-dialog"; +import ForwardedIconComponent from "../../components/genericIconComponent"; import { Button } from "../../components/ui/button"; import { modalHeaderType } from "../../types/components"; import { cn } from "../../utils/utils"; @@ -65,7 +66,12 @@ const Header: React.FC<{ children: ReactNode; description: string | null }> = ({ const Footer: React.FC<{ children?: ReactNode; - submit?: { label: string; icon?: ReactNode }; + submit?: { + label: string; + icon?: ReactNode; + loading?: boolean; + disabled?: boolean; + }; }> = ({ children, submit }) => { return submit ? (
@@ -76,9 +82,26 @@ const Footer: React.FC<{ Cancel -
@@ -242,7 +265,7 @@ function BaseModal({ event.preventDefault(); onSubmit(); }} - className="flex flex-col gap-4" + className="flex flex-col gap-6" >
{ setMyData(data); }} + onSubmit={() => { + setNode(data.id, (old) => ({ + ...old, + data: { + ...old.data, + node: myData.node, + }, + })); + setOpen(false); + }} > <> @@ -608,26 +617,7 @@ const EditNodeModal = forwardRef(
- - - + ); }, diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx index 9eca267e8..4951dace6 100644 --- a/src/frontend/src/modals/exportModal/index.tsx +++ b/src/frontend/src/modals/exportModal/index.tsx @@ -1,7 +1,6 @@ import { ReactNode, forwardRef, useEffect, useState } from "react"; import EditFlowSettings from "../../components/editFlowSettingsComponent"; import IconComponent from "../../components/genericIconComponent"; -import { Button } from "../../components/ui/button"; import { Checkbox } from "../../components/ui/checkbox"; import { API_WARNING_NOTICE_ALERT } from "../../constants/alerts_constants"; import { @@ -30,7 +29,43 @@ const ExportModal = forwardRef( const [open, setOpen] = useState(false); return ( - + { + if (checked) { + downloadFlow( + { + id: currentFlow!.id, + data: currentFlow!.data!, + description, + name, + last_tested_version: version, + is_component: false, + }, + name!, + description, + ); + setNoticeData({ + title: API_WARNING_NOTICE_ALERT, + }); + } else + downloadFlow( + removeApiKeys({ + id: currentFlow!.id, + data: currentFlow!.data!, + description, + name, + last_tested_version: version, + is_component: false, + }), + name!, + description, + ); + setOpen(false); + }} + > {props.children} Export @@ -64,47 +99,9 @@ const ExportModal = forwardRef( - - - + ); - } + }, ); export default ExportModal; diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx index 1daeb0fe1..467a43ebc 100644 --- a/src/frontend/src/modals/flowSettingsModal/index.tsx +++ b/src/frontend/src/modals/flowSettingsModal/index.tsx @@ -41,7 +41,12 @@ export default function FlowSettingsModal({ }, [flows]); return ( - + Settings @@ -56,15 +61,12 @@ export default function FlowSettingsModal({ /> - - - + ); } diff --git a/src/frontend/src/modals/shareModal/index.tsx b/src/frontend/src/modals/shareModal/index.tsx index a2c2f88d0..f8f8ea447 100644 --- a/src/frontend/src/modals/shareModal/index.tsx +++ b/src/frontend/src/modals/shareModal/index.tsx @@ -1,4 +1,3 @@ -import { Loader2 } from "lucide-react"; import { ReactNode, useEffect, useMemo, useState } from "react"; import EditFlowSettings from "../../components/editFlowSettingsComponent"; import IconComponent from "../../components/genericIconComponent"; @@ -202,6 +201,18 @@ export default function ShareModal({ size="smaller-h-full" open={(!disabled && open) ?? internalOpen} setOpen={setOpen ?? internalSetOpen} + onSubmit={() => { + const isNameAvailable = !unavaliableNames.some( + (element) => element.name === name, + ); + + if (isNameAvailable) { + handleShareComponent(); + (setOpen || internalSetOpen)(false); + } else { + setOpenConfirmationModal(true); + } + }} > {children ? children : <>} @@ -250,8 +261,15 @@ export default function ShareModal({ - -
+ + <> {!is_component && ( -
+
<>{modalConfirmation} From da55854f67e4539166b1b488e6eeb5740864f2cf Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 24 May 2024 15:03:21 +0200 Subject: [PATCH 05/52] fixed padding on deleteConfirmationModal --- src/frontend/src/modals/deleteConfirmationModal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/modals/deleteConfirmationModal/index.tsx b/src/frontend/src/modals/deleteConfirmationModal/index.tsx index d03a0d707..a0eb1200f 100644 --- a/src/frontend/src/modals/deleteConfirmationModal/index.tsx +++ b/src/frontend/src/modals/deleteConfirmationModal/index.tsx @@ -59,7 +59,7 @@ export default function DeleteConfirmationModal({
From f5462b1f8d139e6b9d495bc4d52a1dd70d76ae2a Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 30 May 2024 19:49:56 -0300 Subject: [PATCH 07/52] Made node description editable by clicking once and changed cursor type --- src/frontend/src/customNodes/genericNode/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/customNodes/genericNode/index.tsx b/src/frontend/src/customNodes/genericNode/index.tsx index ae6fbb7f3..aaac29979 100644 --- a/src/frontend/src/customNodes/genericNode/index.tsx +++ b/src/frontend/src/customNodes/genericNode/index.tsx @@ -704,14 +704,14 @@ export default function GenericNode({ ) : (
{ + onClick={(e) => { setInputDescription(true); takeSnapshot(); }} From cd9429ac9d6743ef45ef5e282176f66e76abde1b Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 30 May 2024 20:07:33 -0300 Subject: [PATCH 08/52] Changed message of terminal as Run Langflow. --- src/backend/base/langflow/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backend/base/langflow/__main__.py b/src/backend/base/langflow/__main__.py index dfb784c90..ee1eb18f2 100644 --- a/src/backend/base/langflow/__main__.py +++ b/src/backend/base/langflow/__main__.py @@ -121,7 +121,7 @@ def run( ), ): """ - Run the Langflow. + Run Langflow. """ configure(log_level=log_level, log_file=log_file) From a119c3777a55037b985574a582287a83b66999fd Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Thu, 30 May 2024 20:15:48 -0300 Subject: [PATCH 09/52] Changed empty component to open New Project modal --- .../components/emptyComponent/index.tsx | 41 +++++++++---------- 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/src/frontend/src/pages/MainPage/components/emptyComponent/index.tsx b/src/frontend/src/pages/MainPage/components/emptyComponent/index.tsx index 0655b6f47..fdcc7291a 100644 --- a/src/frontend/src/pages/MainPage/components/emptyComponent/index.tsx +++ b/src/frontend/src/pages/MainPage/components/emptyComponent/index.tsx @@ -1,5 +1,7 @@ import { useNavigate } from "react-router-dom"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; +import NewFlowModal from "../../../../modals/newFlowModal"; +import { useState } from "react"; type EmptyComponentProps = {}; @@ -7,31 +9,28 @@ const EmptyComponent = ({}: EmptyComponentProps) => { const addFlow = useFlowsManagerStore((state) => state.addFlow); const navigate = useNavigate(); + const [openModal, setOpenModal] = useState(false); + return ( <>
-
-
- Flows and components can be created using Langflow. -
-
- New? - - - . - - 🚀 -
+
+ + This folder is empty. New? + + + + + + 🚀
From 58008015f5f5ee5ccaba2eb4e0c1243124e6eb23 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 31 May 2024 21:07:00 -0300 Subject: [PATCH 10/52] Implemented unselect on escape --- .../FlowPage/components/nodeToolbarComponent/index.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index f547d0713..a4cf2840f 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -141,6 +141,9 @@ export default function NodeToolbarComponent({ break; case "disabled": break; + case "unselect": + unselectAll(); + break; case "ungroup": takeSnapshot(); expandGroupNode( @@ -276,6 +279,10 @@ export default function NodeToolbarComponent({ event.preventDefault(); handleSelectChange("update"); } + if (selected && event.key.toUpperCase() === "ESCAPE") { + event.preventDefault(); + handleSelectChange("unselect"); + } if ( selected && isGroup && From ba779d5ad077f5b5d4f2a53a93c26c9b0336f88e Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 31 May 2024 21:15:44 -0300 Subject: [PATCH 11/52] Fixed save with my api keys coming as default --- src/frontend/src/modals/exportModal/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/modals/exportModal/index.tsx b/src/frontend/src/modals/exportModal/index.tsx index 4951dace6..8ebc3f863 100644 --- a/src/frontend/src/modals/exportModal/index.tsx +++ b/src/frontend/src/modals/exportModal/index.tsx @@ -18,7 +18,7 @@ const ExportModal = forwardRef( (props: { children: ReactNode }, ref): JSX.Element => { const version = useDarkStore((state) => state.version); const setNoticeData = useAlertStore((state) => state.setNoticeData); - const [checked, setChecked] = useState(true); + const [checked, setChecked] = useState(false); const currentFlow = useFlowsManagerStore((state) => state.currentFlow); useEffect(() => { setName(currentFlow!.name); From d2cb5452c95eaf356c30eb965e90ff0bd752efe7 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Fri, 31 May 2024 21:24:12 -0300 Subject: [PATCH 12/52] Fixed scrolling not working in global variables dropdown --- src/frontend/src/alerts/alertDropDown/index.tsx | 8 ++++---- src/frontend/src/alerts/error/index.tsx | 2 +- src/frontend/src/alerts/notice/index.tsx | 2 +- src/frontend/src/alerts/success/index.tsx | 2 +- .../inputComponent/components/popover/index.tsx | 2 +- .../inputComponent/components/popoverObject/index.tsx | 2 +- 6 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/frontend/src/alerts/alertDropDown/index.tsx b/src/frontend/src/alerts/alertDropDown/index.tsx index 3577a5de6..05f42922d 100644 --- a/src/frontend/src/alerts/alertDropDown/index.tsx +++ b/src/frontend/src/alerts/alertDropDown/index.tsx @@ -16,13 +16,13 @@ export default function AlertDropdown({ }: AlertDropdownType): JSX.Element { const notificationList = useAlertStore((state) => state.notificationList); const clearNotificationList = useAlertStore( - (state) => state.clearNotificationList + (state) => state.clearNotificationList, ); const removeFromNotificationList = useAlertStore( - (state) => state.removeFromNotificationList + (state) => state.removeFromNotificationList, ); const setNotificationCenter = useAlertStore( - (state) => state.setNotificationCenter + (state) => state.setNotificationCenter, ); const [open, setOpen] = useState(false); @@ -36,7 +36,7 @@ export default function AlertDropdown({ }} > {children} - +
Notifications
diff --git a/src/frontend/src/alerts/error/index.tsx b/src/frontend/src/alerts/error/index.tsx index ec23c103e..3690590b9 100644 --- a/src/frontend/src/alerts/error/index.tsx +++ b/src/frontend/src/alerts/error/index.tsx @@ -40,7 +40,7 @@ export default function ErrorAlert({ removeAlert(id); }, 500); }} - className="error-build-message nocopy nopan nodelete nodrag noundo" + className="error-build-message nocopy nowheel nopan nodelete nodrag noundo" >
diff --git a/src/frontend/src/alerts/notice/index.tsx b/src/frontend/src/alerts/notice/index.tsx index faaa4db6a..fb29954ea 100644 --- a/src/frontend/src/alerts/notice/index.tsx +++ b/src/frontend/src/alerts/notice/index.tsx @@ -36,7 +36,7 @@ export default function NoticeAlert({ setShow(false); removeAlert(id); }} - className="nocopy nopan nodelete nodrag noundo mt-6 w-96 rounded-md bg-info-background p-4 shadow-xl" + className="nocopy nowheel nopan nodelete nodrag noundo mt-6 w-96 rounded-md bg-info-background p-4 shadow-xl" >
diff --git a/src/frontend/src/alerts/success/index.tsx b/src/frontend/src/alerts/success/index.tsx index ec6abf589..db62c8432 100644 --- a/src/frontend/src/alerts/success/index.tsx +++ b/src/frontend/src/alerts/success/index.tsx @@ -34,7 +34,7 @@ export default function SuccessAlert({ setShow(false); removeAlert(id); }} - className="success-alert nocopy nopan nodelete nodrag noundo" + className="success-alert nocopy nowheel nopan nodelete nodrag noundo" >
diff --git a/src/frontend/src/components/inputComponent/components/popover/index.tsx b/src/frontend/src/components/inputComponent/components/popover/index.tsx index 52f79203f..78fc5bd3d 100644 --- a/src/frontend/src/components/inputComponent/components/popover/index.tsx +++ b/src/frontend/src/components/inputComponent/components/popover/index.tsx @@ -108,7 +108,7 @@ const CustomInputPopover = ({ /> Date: Sun, 2 Jun 2024 23:16:04 -0300 Subject: [PATCH 13/52] Added description column to advanced tab --- .../Basic Prompting (Hello, world!).json | 22 +++--- .../Langflow Blog Writter.json | 34 ++++----- .../Langflow Document QA.json | 28 ++++---- .../Langflow Memory Conversation.json | 36 +++++----- .../Langflow Prompt Chaining.json | 56 +++++++-------- .../VectorStore-RAG-Flows.json | 72 +++++++++---------- .../components/shadTooltipComponent/index.tsx | 16 +++-- src/frontend/src/components/ui/tooltip.tsx | 26 ++++++- .../src/modals/editNodeModal/index.tsx | 23 +++++- src/frontend/src/types/components/index.ts | 1 + 10 files changed, 183 insertions(+), 131 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json index c56a8304c..1dfe86b4a 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json @@ -149,7 +149,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, @@ -304,7 +304,7 @@ }, "temperature": { "type": "float", - "required": true, + "required": false, "placeholder": "", "list": false, "show": true, @@ -716,9 +716,9 @@ "edges": [ { "source": "OpenAIModel-k39HS", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-k39HS\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-k39HSœ}", "target": "ChatOutput-njtka", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-njtka\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-njtkaœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -736,13 +736,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-OpenAIModel-k39HS{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-k39HS\u0153}-ChatOutput-njtka{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-njtka\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-OpenAIModel-k39HS{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-k39HSœ}-ChatOutput-njtka{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-njtkaœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "Prompt-uxBqP", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-uxBqP\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-uxBqPœ}", "target": "OpenAIModel-k39HS", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-k39HS\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-k39HSœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -760,13 +760,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-Prompt-uxBqP{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-uxBqP\u0153}-OpenAIModel-k39HS{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-k39HS\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-Prompt-uxBqP{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-uxBqPœ}-OpenAIModel-k39HS{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-k39HSœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "ChatInput-P3fgL", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Record\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-P3fgL\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œRecordœ,œstrœ,œTextœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-P3fgLœ}", "target": "Prompt-uxBqP", - "targetHandle": "{\u0153fieldName\u0153:\u0153user_input\u0153,\u0153id\u0153:\u0153Prompt-uxBqP\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-uxBqPœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "user_input", @@ -784,7 +784,7 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-ChatInput-P3fgL{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Record\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-P3fgL\u0153}-Prompt-uxBqP{\u0153fieldName\u0153:\u0153user_input\u0153,\u0153id\u0153:\u0153Prompt-uxBqP\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-ChatInput-P3fgL{œbaseClassesœ:[œobjectœ,œRecordœ,œstrœ,œTextœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-P3fgLœ}-Prompt-uxBqP{œfieldNameœ:œuser_inputœ,œidœ:œPrompt-uxBqPœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" } ], "viewport": { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json index d2c8cf951..6df211ba6 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json @@ -453,7 +453,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, @@ -608,7 +608,7 @@ }, "temperature": { "type": "float", - "required": true, + "required": false, "placeholder": "", "list": false, "show": true, @@ -854,9 +854,9 @@ { "source": "URL-HYPkR", "target": "Prompt-Rse03", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153URL\u0153,\u0153id\u0153:\u0153URL-HYPkR\u0153}", - "targetHandle": "{\u0153fieldName\u0153:\u0153reference_2\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", - "id": "reactflow__edge-URL-HYPkR{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153URL\u0153,\u0153id\u0153:\u0153URL-HYPkR\u0153}-Prompt-Rse03{\u0153fieldName\u0153:\u0153reference_2\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "sourceHandle": "{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-HYPkRœ}", + "targetHandle": "{œfieldNameœ:œreference_2œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-URL-HYPkR{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-HYPkRœ}-Prompt-Rse03{œfieldNameœ:œreference_2œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "reference_2", @@ -878,9 +878,9 @@ }, { "source": "OpenAIModel-gi29P", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-gi29P\u0153}", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gi29Pœ}", "target": "ChatOutput-JPlxl", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-JPlxl\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-JPlxlœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -898,13 +898,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-OpenAIModel-gi29P{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-gi29P\u0153}-ChatOutput-JPlxl{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-JPlxl\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-OpenAIModel-gi29P{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-gi29Pœ}-ChatOutput-JPlxl{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-JPlxlœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "URL-2cX90", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153URL\u0153,\u0153id\u0153:\u0153URL-2cX90\u0153}", + "sourceHandle": "{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-2cX90œ}", "target": "Prompt-Rse03", - "targetHandle": "{\u0153fieldName\u0153:\u0153reference_1\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œreference_1œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "reference_1", @@ -922,13 +922,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-URL-2cX90{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153URL\u0153,\u0153id\u0153:\u0153URL-2cX90\u0153}-Prompt-Rse03{\u0153fieldName\u0153:\u0153reference_1\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-URL-2cX90{œbaseClassesœ:[œRecordœ],œdataTypeœ:œURLœ,œidœ:œURL-2cX90œ}-Prompt-Rse03{œfieldNameœ:œreference_1œ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" }, { "source": "TextInput-og8Or", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153TextInput\u0153,\u0153id\u0153:\u0153TextInput-og8Or\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-og8Orœ}", "target": "Prompt-Rse03", - "targetHandle": "{\u0153fieldName\u0153:\u0153instructions\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "instructions", @@ -946,13 +946,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-TextInput-og8Or{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153TextInput\u0153,\u0153id\u0153:\u0153TextInput-og8Or\u0153}-Prompt-Rse03{\u0153fieldName\u0153:\u0153instructions\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-TextInput-og8Or{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-og8Orœ}-Prompt-Rse03{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-Rse03œ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" }, { "source": "Prompt-Rse03", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-Rse03œ}", "target": "OpenAIModel-gi29P", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-gi29P\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gi29Pœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -970,7 +970,7 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-Prompt-Rse03{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-Rse03\u0153}-OpenAIModel-gi29P{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-gi29P\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "id": "reactflow__edge-Prompt-Rse03{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-Rse03œ}-OpenAIModel-gi29P{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-gi29Pœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "selected": false } ], diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json index 32933e0d6..71b1b5c41 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json @@ -598,7 +598,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, @@ -753,7 +753,7 @@ }, "temperature": { "type": "float", - "required": true, + "required": false, "placeholder": "", "list": false, "show": true, @@ -825,9 +825,9 @@ "edges": [ { "source": "ChatInput-MsSJ9", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Record\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-MsSJ9\u0153}", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œRecordœ,œTextœ,œobjectœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-MsSJ9œ}", "target": "Prompt-tHwPf", - "targetHandle": "{\u0153fieldName\u0153:\u0153Question\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œQuestionœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "Question", @@ -845,13 +845,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-ChatInput-MsSJ9{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Record\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-MsSJ9\u0153}-Prompt-tHwPf{\u0153fieldName\u0153:\u0153Question\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-ChatInput-MsSJ9{œbaseClassesœ:[œstrœ,œRecordœ,œTextœ,œobjectœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-MsSJ9œ}-Prompt-tHwPf{œfieldNameœ:œQuestionœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" }, { "source": "File-6TEsD", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153File\u0153,\u0153id\u0153:\u0153File-6TEsD\u0153}", + "sourceHandle": "{œbaseClassesœ:[œRecordœ],œdataTypeœ:œFileœ,œidœ:œFile-6TEsDœ}", "target": "Prompt-tHwPf", - "targetHandle": "{\u0153fieldName\u0153:\u0153Document\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œDocumentœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "Document", @@ -869,13 +869,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-File-6TEsD{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153File\u0153,\u0153id\u0153:\u0153File-6TEsD\u0153}-Prompt-tHwPf{\u0153fieldName\u0153:\u0153Document\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-File-6TEsD{œbaseClassesœ:[œRecordœ],œdataTypeœ:œFileœ,œidœ:œFile-6TEsDœ}-Prompt-tHwPf{œfieldNameœ:œDocumentœ,œidœ:œPrompt-tHwPfœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" }, { "source": "Prompt-tHwPf", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-tHwPfœ}", "target": "OpenAIModel-Bt067", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-Bt067\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Bt067œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -893,13 +893,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-Prompt-tHwPf{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-tHwPf\u0153}-OpenAIModel-Bt067{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-Bt067\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-Prompt-tHwPf{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-tHwPfœ}-OpenAIModel-Bt067{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Bt067œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "OpenAIModel-Bt067", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-Bt067\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Bt067œ}", "target": "ChatOutput-F5Awj", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-F5Awj\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-F5Awjœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -917,7 +917,7 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-OpenAIModel-Bt067{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-Bt067\u0153}-ChatOutput-F5Awj{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-F5Awj\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-OpenAIModel-Bt067{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Bt067œ}-ChatOutput-F5Awj{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-F5Awjœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" } ], "viewport": { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json index 9e51846be..e26ffa21d 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json @@ -1,6 +1,6 @@ { "id": "08d5cccf-d098-4367-b14b-1078429c9ed9", - "icon": "\ud83e\udd16", + "icon": "🤖", "icon_bg_color": "#FFD700", "data": { "nodes": [ @@ -679,7 +679,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, @@ -834,7 +834,7 @@ }, "temperature": { "type": "float", - "required": true, + "required": false, "placeholder": "", "list": false, "show": true, @@ -1003,9 +1003,9 @@ "edges": [ { "source": "MemoryComponent-cdA1J", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153MemoryComponent\u0153,\u0153id\u0153:\u0153MemoryComponent-cdA1J\u0153}", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}", "target": "Prompt-ODkUx", - "targetHandle": "{\u0153fieldName\u0153:\u0153context\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œcontextœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "context", @@ -1023,14 +1023,14 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-MemoryComponent-cdA1J{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153MemoryComponent\u0153,\u0153id\u0153:\u0153MemoryComponent-cdA1J\u0153}-Prompt-ODkUx{\u0153fieldName\u0153:\u0153context\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "id": "reactflow__edge-MemoryComponent-cdA1J{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}-Prompt-ODkUx{œfieldNameœ:œcontextœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "selected": false }, { "source": "ChatInput-t7F8v", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153object\u0153,\u0153Record\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-t7F8v\u0153}", + "sourceHandle": "{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-t7F8vœ}", "target": "Prompt-ODkUx", - "targetHandle": "{\u0153fieldName\u0153:\u0153user_message\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "user_message", @@ -1048,14 +1048,14 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-ChatInput-t7F8v{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153object\u0153,\u0153Record\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-t7F8v\u0153}-Prompt-ODkUx{\u0153fieldName\u0153:\u0153user_message\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "id": "reactflow__edge-ChatInput-t7F8v{œbaseClassesœ:[œTextœ,œobjectœ,œRecordœ,œstrœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-t7F8vœ}-Prompt-ODkUx{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-ODkUxœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "selected": false }, { "source": "Prompt-ODkUx", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153str\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153}", + "sourceHandle": "{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-ODkUxœ}", "target": "OpenAIModel-9RykF", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-9RykF\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-9RykFœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -1073,13 +1073,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-Prompt-ODkUx{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153str\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-ODkUx\u0153}-OpenAIModel-9RykF{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-9RykF\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-Prompt-ODkUx{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-ODkUxœ}-OpenAIModel-9RykF{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-9RykFœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "OpenAIModel-9RykF", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153object\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-9RykF\u0153}", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-9RykFœ}", "target": "ChatOutput-P1jEe", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-P1jEe\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-P1jEeœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -1097,13 +1097,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-OpenAIModel-9RykF{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153object\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-9RykF\u0153}-ChatOutput-P1jEe{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-P1jEe\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-OpenAIModel-9RykF{œbaseClassesœ:[œstrœ,œobjectœ,œTextœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-9RykFœ}-ChatOutput-P1jEe{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-P1jEeœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "MemoryComponent-cdA1J", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153MemoryComponent\u0153,\u0153id\u0153:\u0153MemoryComponent-cdA1J\u0153}", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}", "target": "TextOutput-vrs6T", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-vrs6T\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-vrs6Tœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -1121,7 +1121,7 @@ "stroke": "#555" }, "className": "stroke-foreground stroke-connection", - "id": "reactflow__edge-MemoryComponent-cdA1J{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153MemoryComponent\u0153,\u0153id\u0153:\u0153MemoryComponent-cdA1J\u0153}-TextOutput-vrs6T{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-vrs6T\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-MemoryComponent-cdA1J{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œMemoryComponentœ,œidœ:œMemoryComponent-cdA1Jœ}-TextOutput-vrs6T{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-vrs6Tœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}" } ], "viewport": { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json index 9269eeed0..b6dfa2f10 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json @@ -798,7 +798,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, @@ -953,7 +953,7 @@ }, "temperature": { "type": "float", - "required": true, + "required": false, "placeholder": "", "list": false, "show": true, @@ -1155,7 +1155,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, @@ -1310,7 +1310,7 @@ }, "temperature": { "type": "float", - "required": true, + "required": false, "placeholder": "", "list": false, "show": true, @@ -1382,9 +1382,9 @@ "edges": [ { "source": "TextInput-sptaH", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153TextInput\u0153,\u0153id\u0153:\u0153TextInput-sptaH\u0153}", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-sptaHœ}", "target": "Prompt-amqBu", - "targetHandle": "{\u0153fieldName\u0153:\u0153document\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œdocumentœ,œidœ:œPrompt-amqBuœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "document", @@ -1402,13 +1402,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-TextInput-sptaH{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153TextInput\u0153,\u0153id\u0153:\u0153TextInput-sptaH\u0153}-Prompt-amqBu{\u0153fieldName\u0153:\u0153document\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-TextInput-sptaH{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œTextInputœ,œidœ:œTextInput-sptaHœ}-Prompt-amqBu{œfieldNameœ:œdocumentœ,œidœ:œPrompt-amqBuœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" }, { "source": "Prompt-amqBu", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}", "target": "TextOutput-2MS4a", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-2MS4a\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-2MS4aœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -1426,13 +1426,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-Prompt-amqBu{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153}-TextOutput-2MS4a{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-2MS4a\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-Prompt-amqBu{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}-TextOutput-2MS4a{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-2MS4aœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}" }, { "source": "Prompt-amqBu", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}", "target": "OpenAIModel-uYXZJ", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-uYXZJœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -1450,13 +1450,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-Prompt-amqBu{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-amqBu\u0153}-OpenAIModel-uYXZJ{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-Prompt-amqBu{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-amqBuœ}-OpenAIModel-uYXZJ{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-uYXZJœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "OpenAIModel-uYXZJ", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153}", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}", "target": "Prompt-gTNiz", - "targetHandle": "{\u0153fieldName\u0153:\u0153summary\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œsummaryœ,œidœ:œPrompt-gTNizœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "summary", @@ -1474,13 +1474,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-OpenAIModel-uYXZJ{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153}-Prompt-gTNiz{\u0153fieldName\u0153:\u0153summary\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-OpenAIModel-uYXZJ{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}-Prompt-gTNiz{œfieldNameœ:œsummaryœ,œidœ:œPrompt-gTNizœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}" }, { "source": "OpenAIModel-uYXZJ", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153}", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}", "target": "ChatOutput-EJkG3", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-EJkG3\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-EJkG3œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -1498,13 +1498,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-OpenAIModel-uYXZJ{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-uYXZJ\u0153}-ChatOutput-EJkG3{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-EJkG3\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-OpenAIModel-uYXZJ{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-uYXZJœ}-ChatOutput-EJkG3{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-EJkG3œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "Prompt-gTNiz", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}", "target": "TextOutput-MUDOR", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-MUDOR\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-MUDORœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -1522,13 +1522,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-Prompt-gTNiz{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153}-TextOutput-MUDOR{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-MUDOR\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-Prompt-gTNiz{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}-TextOutput-MUDOR{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-MUDORœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}" }, { "source": "Prompt-gTNiz", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}", "target": "OpenAIModel-XawYB", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-XawYB\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-XawYBœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -1546,13 +1546,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-Prompt-gTNiz{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153str\u0153,\u0153Text\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-gTNiz\u0153}-OpenAIModel-XawYB{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-XawYB\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-Prompt-gTNiz{œbaseClassesœ:[œobjectœ,œstrœ,œTextœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-gTNizœ}-OpenAIModel-XawYB{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-XawYBœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "OpenAIModel-XawYB", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-XawYB\u0153}", + "sourceHandle": "{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-XawYBœ}", "target": "ChatOutput-DNmvg", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-DNmvg\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-DNmvgœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -1570,7 +1570,7 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-OpenAIModel-XawYB{\u0153baseClasses\u0153:[\u0153str\u0153,\u0153Text\u0153,\u0153object\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-XawYB\u0153}-ChatOutput-DNmvg{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-DNmvg\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-OpenAIModel-XawYB{œbaseClassesœ:[œstrœ,œTextœ,œobjectœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-XawYBœ}-ChatOutput-DNmvg{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-DNmvgœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" } ], "viewport": { diff --git a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json index 654f0771b..8a5df6666 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json @@ -793,7 +793,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, @@ -948,7 +948,7 @@ }, "temperature": { "type": "float", - "required": true, + "required": false, "placeholder": "", "list": false, "show": true, @@ -1727,7 +1727,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import List, Optional\n\nfrom langflow.components.vectorstores.AstraDB import AstraDBVectorStoreComponent\nfrom langflow.components.vectorstores.base.model import LCVectorStoreComponent\nfrom langflow.field_typing import Embeddings, Text\nfrom langflow.schema import Record\n\n\nclass AstraDBSearchComponent(LCVectorStoreComponent):\n display_name = \"Astra DB Search\"\n description = \"Searches an existing Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"input_value\", \"embedding\"]\n\n def build_config(self):\n return {\n \"search_type\": {\n \"display_name\": \"Search Type\",\n \"options\": [\"Similarity\", \"MMR\"],\n },\n \"input_value\": {\n \"display_name\": \"Input Value\",\n \"info\": \"Input value to search\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like \u201cSync\u201d, \u201cAsync\u201d, or \u201cOff\u201d.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n \"number_of_results\": {\n \"display_name\": \"Number of Results\",\n \"info\": \"Number of results to return.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n collection_name: str,\n input_value: Text,\n token: str,\n api_endpoint: str,\n search_type: str = \"Similarity\",\n number_of_results: int = 4,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> List[Record]:\n vector_store = AstraDBVectorStoreComponent().build(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n try:\n return self.search_with_vector_store(input_value, search_type, vector_store, k=number_of_results)\n except KeyError as e:\n if \"content\" in str(e):\n raise ValueError(\n \"You should ingest data through Langflow (or LangChain) to query it in Langflow. Your collection does not contain a field name 'content'.\"\n )\n else:\n raise e\n", + "value": "from typing import List, Optional\n\nfrom langflow.components.vectorstores.AstraDB import AstraDBVectorStoreComponent\nfrom langflow.components.vectorstores.base.model import LCVectorStoreComponent\nfrom langflow.field_typing import Embeddings, Text\nfrom langflow.schema import Record\n\n\nclass AstraDBSearchComponent(LCVectorStoreComponent):\n display_name = \"Astra DB Search\"\n description = \"Searches an existing Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"input_value\", \"embedding\"]\n\n def build_config(self):\n return {\n \"search_type\": {\n \"display_name\": \"Search Type\",\n \"options\": [\"Similarity\", \"MMR\"],\n },\n \"input_value\": {\n \"display_name\": \"Input Value\",\n \"info\": \"Input value to search\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n \"number_of_results\": {\n \"display_name\": \"Number of Results\",\n \"info\": \"Number of results to return.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n collection_name: str,\n input_value: Text,\n token: str,\n api_endpoint: str,\n search_type: str = \"Similarity\",\n number_of_results: int = 4,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> List[Record]:\n vector_store = AstraDBVectorStoreComponent().build(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n try:\n return self.search_with_vector_store(input_value, search_type, vector_store, k=number_of_results)\n except KeyError as e:\n if \"content\" in str(e):\n raise ValueError(\n \"You should ingest data through Langflow (or LangChain) to query it in Langflow. Your collection does not contain a field name 'content'.\"\n )\n else:\n raise e\n", "fileTypes": [], "file_path": "", "password": false, @@ -1927,7 +1927,7 @@ "display_name": "Setup Mode", "advanced": true, "dynamic": false, - "info": "Configuration mode for setting up the vector store, with options like \u201cSync\u201d, \u201cAsync\u201d, or \u201cOff\u201d.", + "info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.", "load_from_db": false, "title_case": false, "input_types": ["Text"] @@ -2148,7 +2148,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import List, Optional, Union\nfrom langchain_astradb import AstraDBVectorStore\nfrom langchain_astradb.utils.astradb import SetupMode\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, VectorStore\nfrom langflow.schema import Record\nfrom langchain_core.retrievers import BaseRetriever\n\n\nclass AstraDBVectorStoreComponent(CustomComponent):\n display_name = \"Astra DB\"\n description = \"Builds or loads an Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"inputs\", \"embedding\"]\n\n def build_config(self):\n return {\n \"inputs\": {\n \"display_name\": \"Inputs\",\n \"info\": \"Optional list of records to be processed and stored in the vector store.\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like \u201cSync\u201d, \u201cAsync\u201d, or \u201cOff\u201d.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n token: str,\n api_endpoint: str,\n collection_name: str,\n inputs: Optional[List[Record]] = None,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> Union[VectorStore, BaseRetriever]:\n try:\n setup_mode_value = SetupMode[setup_mode.upper()]\n except KeyError:\n raise ValueError(f\"Invalid setup mode: {setup_mode}\")\n if inputs:\n documents = [_input.to_lc_document() for _input in inputs]\n\n vector_store = AstraDBVectorStore.from_documents(\n documents=documents,\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n else:\n vector_store = AstraDBVectorStore(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n\n return vector_store\n", + "value": "from typing import List, Optional, Union\nfrom langchain_astradb import AstraDBVectorStore\nfrom langchain_astradb.utils.astradb import SetupMode\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Embeddings, VectorStore\nfrom langflow.schema import Record\nfrom langchain_core.retrievers import BaseRetriever\n\n\nclass AstraDBVectorStoreComponent(CustomComponent):\n display_name = \"Astra DB\"\n description = \"Builds or loads an Astra DB Vector Store.\"\n icon = \"AstraDB\"\n field_order = [\"token\", \"api_endpoint\", \"collection_name\", \"inputs\", \"embedding\"]\n\n def build_config(self):\n return {\n \"inputs\": {\n \"display_name\": \"Inputs\",\n \"info\": \"Optional list of records to be processed and stored in the vector store.\",\n },\n \"embedding\": {\"display_name\": \"Embedding\", \"info\": \"Embedding to use\"},\n \"collection_name\": {\n \"display_name\": \"Collection Name\",\n \"info\": \"The name of the collection within Astra DB where the vectors will be stored.\",\n },\n \"token\": {\n \"display_name\": \"Token\",\n \"info\": \"Authentication token for accessing Astra DB.\",\n \"password\": True,\n },\n \"api_endpoint\": {\n \"display_name\": \"API Endpoint\",\n \"info\": \"API endpoint URL for the Astra DB service.\",\n },\n \"namespace\": {\n \"display_name\": \"Namespace\",\n \"info\": \"Optional namespace within Astra DB to use for the collection.\",\n \"advanced\": True,\n },\n \"metric\": {\n \"display_name\": \"Metric\",\n \"info\": \"Optional distance metric for vector comparisons in the vector store.\",\n \"advanced\": True,\n },\n \"batch_size\": {\n \"display_name\": \"Batch Size\",\n \"info\": \"Optional number of records to process in a single batch.\",\n \"advanced\": True,\n },\n \"bulk_insert_batch_concurrency\": {\n \"display_name\": \"Bulk Insert Batch Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations.\",\n \"advanced\": True,\n },\n \"bulk_insert_overwrite_concurrency\": {\n \"display_name\": \"Bulk Insert Overwrite Concurrency\",\n \"info\": \"Optional concurrency level for bulk insert operations that overwrite existing records.\",\n \"advanced\": True,\n },\n \"bulk_delete_concurrency\": {\n \"display_name\": \"Bulk Delete Concurrency\",\n \"info\": \"Optional concurrency level for bulk delete operations.\",\n \"advanced\": True,\n },\n \"setup_mode\": {\n \"display_name\": \"Setup Mode\",\n \"info\": \"Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.\",\n \"options\": [\"Sync\", \"Async\", \"Off\"],\n \"advanced\": True,\n },\n \"pre_delete_collection\": {\n \"display_name\": \"Pre Delete Collection\",\n \"info\": \"Boolean flag to determine whether to delete the collection before creating a new one.\",\n \"advanced\": True,\n },\n \"metadata_indexing_include\": {\n \"display_name\": \"Metadata Indexing Include\",\n \"info\": \"Optional list of metadata fields to include in the indexing.\",\n \"advanced\": True,\n },\n \"metadata_indexing_exclude\": {\n \"display_name\": \"Metadata Indexing Exclude\",\n \"info\": \"Optional list of metadata fields to exclude from the indexing.\",\n \"advanced\": True,\n },\n \"collection_indexing_policy\": {\n \"display_name\": \"Collection Indexing Policy\",\n \"info\": \"Optional dictionary defining the indexing policy for the collection.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n embedding: Embeddings,\n token: str,\n api_endpoint: str,\n collection_name: str,\n inputs: Optional[List[Record]] = None,\n namespace: Optional[str] = None,\n metric: Optional[str] = None,\n batch_size: Optional[int] = None,\n bulk_insert_batch_concurrency: Optional[int] = None,\n bulk_insert_overwrite_concurrency: Optional[int] = None,\n bulk_delete_concurrency: Optional[int] = None,\n setup_mode: str = \"Sync\",\n pre_delete_collection: bool = False,\n metadata_indexing_include: Optional[List[str]] = None,\n metadata_indexing_exclude: Optional[List[str]] = None,\n collection_indexing_policy: Optional[dict] = None,\n ) -> Union[VectorStore, BaseRetriever]:\n try:\n setup_mode_value = SetupMode[setup_mode.upper()]\n except KeyError:\n raise ValueError(f\"Invalid setup mode: {setup_mode}\")\n if inputs:\n documents = [_input.to_lc_document() for _input in inputs]\n\n vector_store = AstraDBVectorStore.from_documents(\n documents=documents,\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n else:\n vector_store = AstraDBVectorStore(\n embedding=embedding,\n collection_name=collection_name,\n token=token,\n api_endpoint=api_endpoint,\n namespace=namespace,\n metric=metric,\n batch_size=batch_size,\n bulk_insert_batch_concurrency=bulk_insert_batch_concurrency,\n bulk_insert_overwrite_concurrency=bulk_insert_overwrite_concurrency,\n bulk_delete_concurrency=bulk_delete_concurrency,\n setup_mode=setup_mode_value,\n pre_delete_collection=pre_delete_collection,\n metadata_indexing_include=metadata_indexing_include,\n metadata_indexing_exclude=metadata_indexing_exclude,\n collection_indexing_policy=collection_indexing_policy,\n )\n\n return vector_store\n", "fileTypes": [], "file_path": "", "password": false, @@ -2308,7 +2308,7 @@ "display_name": "Setup Mode", "advanced": true, "dynamic": false, - "info": "Configuration mode for setting up the vector store, with options like \u201cSync\u201d, \u201cAsync\u201d, or \u201cOff\u201d.", + "info": "Configuration mode for setting up the vector store, with options like “Sync”, “Async”, or “Off”.", "load_from_db": false, "title_case": false, "input_types": ["Text"] @@ -2893,9 +2893,9 @@ { "source": "TextOutput-BDknO", "target": "Prompt-xeI6K", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153TextOutput\u0153,\u0153id\u0153:\u0153TextOutput-BDknO\u0153}", - "targetHandle": "{\u0153fieldName\u0153:\u0153context\u0153,\u0153id\u0153:\u0153Prompt-xeI6K\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", - "id": "reactflow__edge-TextOutput-BDknO{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153TextOutput\u0153,\u0153id\u0153:\u0153TextOutput-BDknO\u0153}-Prompt-xeI6K{\u0153fieldName\u0153:\u0153context\u0153,\u0153id\u0153:\u0153Prompt-xeI6K\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œTextOutputœ,œidœ:œTextOutput-BDknOœ}", + "targetHandle": "{œfieldNameœ:œcontextœ,œidœ:œPrompt-xeI6Kœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-TextOutput-BDknO{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œTextOutputœ,œidœ:œTextOutput-BDknOœ}-Prompt-xeI6K{œfieldNameœ:œcontextœ,œidœ:œPrompt-xeI6Kœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "context", @@ -2918,9 +2918,9 @@ { "source": "ChatInput-yxMKE", "target": "Prompt-xeI6K", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153str\u0153,\u0153object\u0153,\u0153Record\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-yxMKE\u0153}", - "targetHandle": "{\u0153fieldName\u0153:\u0153question\u0153,\u0153id\u0153:\u0153Prompt-xeI6K\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", - "id": "reactflow__edge-ChatInput-yxMKE{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153str\u0153,\u0153object\u0153,\u0153Record\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-yxMKE\u0153}-Prompt-xeI6K{\u0153fieldName\u0153:\u0153question\u0153,\u0153id\u0153:\u0153Prompt-xeI6K\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153BaseOutputParser\u0153,\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "sourceHandle": "{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ,œRecordœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-yxMKEœ}", + "targetHandle": "{œfieldNameœ:œquestionœ,œidœ:œPrompt-xeI6Kœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-yxMKE{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ,œRecordœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-yxMKEœ}-Prompt-xeI6K{œfieldNameœ:œquestionœ,œidœ:œPrompt-xeI6Kœ,œinputTypesœ:[œDocumentœ,œBaseOutputParserœ,œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "question", @@ -2943,9 +2943,9 @@ { "source": "Prompt-xeI6K", "target": "OpenAIModel-EjXlN", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-xeI6K\u0153}", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-EjXlN\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", - "id": "reactflow__edge-Prompt-xeI6K{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153Prompt\u0153,\u0153id\u0153:\u0153Prompt-xeI6K\u0153}-OpenAIModel-EjXlN{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153OpenAIModel-EjXlN\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-xeI6Kœ}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-EjXlNœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Prompt-xeI6K{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œPromptœ,œidœ:œPrompt-xeI6Kœ}-OpenAIModel-EjXlN{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-EjXlNœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -2968,9 +2968,9 @@ { "source": "OpenAIModel-EjXlN", "target": "ChatOutput-Q39I8", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-EjXlN\u0153}", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-Q39I8\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", - "id": "reactflow__edge-OpenAIModel-EjXlN{\u0153baseClasses\u0153:[\u0153object\u0153,\u0153Text\u0153,\u0153str\u0153],\u0153dataType\u0153:\u0153OpenAIModel\u0153,\u0153id\u0153:\u0153OpenAIModel-EjXlN\u0153}-ChatOutput-Q39I8{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153ChatOutput-Q39I8\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "sourceHandle": "{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-EjXlNœ}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Q39I8œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-OpenAIModel-EjXlN{œbaseClassesœ:[œobjectœ,œTextœ,œstrœ],œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-EjXlNœ}-ChatOutput-Q39I8{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Q39I8œ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -2993,9 +2993,9 @@ { "source": "File-t0a6a", "target": "RecursiveCharacterTextSplitter-tR9QM", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153File\u0153,\u0153id\u0153:\u0153File-t0a6a\u0153}", - "targetHandle": "{\u0153fieldName\u0153:\u0153inputs\u0153,\u0153id\u0153:\u0153RecursiveCharacterTextSplitter-tR9QM\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153Record\u0153],\u0153type\u0153:\u0153Document\u0153}", - "id": "reactflow__edge-File-t0a6a{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153File\u0153,\u0153id\u0153:\u0153File-t0a6a\u0153}-RecursiveCharacterTextSplitter-tR9QM{\u0153fieldName\u0153:\u0153inputs\u0153,\u0153id\u0153:\u0153RecursiveCharacterTextSplitter-tR9QM\u0153,\u0153inputTypes\u0153:[\u0153Document\u0153,\u0153Record\u0153],\u0153type\u0153:\u0153Document\u0153}", + "sourceHandle": "{œbaseClassesœ:[œRecordœ],œdataTypeœ:œFileœ,œidœ:œFile-t0a6aœ}", + "targetHandle": "{œfieldNameœ:œinputsœ,œidœ:œRecursiveCharacterTextSplitter-tR9QMœ,œinputTypesœ:[œDocumentœ,œRecordœ],œtypeœ:œDocumentœ}", + "id": "reactflow__edge-File-t0a6a{œbaseClassesœ:[œRecordœ],œdataTypeœ:œFileœ,œidœ:œFile-t0a6aœ}-RecursiveCharacterTextSplitter-tR9QM{œfieldNameœ:œinputsœ,œidœ:œRecursiveCharacterTextSplitter-tR9QMœ,œinputTypesœ:[œDocumentœ,œRecordœ],œtypeœ:œDocumentœ}", "data": { "targetHandle": { "fieldName": "inputs", @@ -3017,9 +3017,9 @@ }, { "source": "OpenAIEmbeddings-ZlOk1", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Embeddings\u0153],\u0153dataType\u0153:\u0153OpenAIEmbeddings\u0153,\u0153id\u0153:\u0153OpenAIEmbeddings-ZlOk1\u0153}", + "sourceHandle": "{œbaseClassesœ:[œEmbeddingsœ],œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-ZlOk1œ}", "target": "AstraDBSearch-41nRz", - "targetHandle": "{\u0153fieldName\u0153:\u0153embedding\u0153,\u0153id\u0153:\u0153AstraDBSearch-41nRz\u0153,\u0153inputTypes\u0153:null,\u0153type\u0153:\u0153Embeddings\u0153}", + "targetHandle": "{œfieldNameœ:œembeddingœ,œidœ:œAstraDBSearch-41nRzœ,œinputTypesœ:null,œtypeœ:œEmbeddingsœ}", "data": { "targetHandle": { "fieldName": "embedding", @@ -3037,13 +3037,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-OpenAIEmbeddings-ZlOk1{\u0153baseClasses\u0153:[\u0153Embeddings\u0153],\u0153dataType\u0153:\u0153OpenAIEmbeddings\u0153,\u0153id\u0153:\u0153OpenAIEmbeddings-ZlOk1\u0153}-AstraDBSearch-41nRz{\u0153fieldName\u0153:\u0153embedding\u0153,\u0153id\u0153:\u0153AstraDBSearch-41nRz\u0153,\u0153inputTypes\u0153:null,\u0153type\u0153:\u0153Embeddings\u0153}" + "id": "reactflow__edge-OpenAIEmbeddings-ZlOk1{œbaseClassesœ:[œEmbeddingsœ],œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-ZlOk1œ}-AstraDBSearch-41nRz{œfieldNameœ:œembeddingœ,œidœ:œAstraDBSearch-41nRzœ,œinputTypesœ:null,œtypeœ:œEmbeddingsœ}" }, { "source": "ChatInput-yxMKE", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153str\u0153,\u0153object\u0153,\u0153Record\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-yxMKE\u0153}", + "sourceHandle": "{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ,œRecordœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-yxMKEœ}", "target": "AstraDBSearch-41nRz", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153AstraDBSearch-41nRz\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œAstraDBSearch-41nRzœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -3061,13 +3061,13 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-ChatInput-yxMKE{\u0153baseClasses\u0153:[\u0153Text\u0153,\u0153str\u0153,\u0153object\u0153,\u0153Record\u0153],\u0153dataType\u0153:\u0153ChatInput\u0153,\u0153id\u0153:\u0153ChatInput-yxMKE\u0153}-AstraDBSearch-41nRz{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153AstraDBSearch-41nRz\u0153,\u0153inputTypes\u0153:[\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-ChatInput-yxMKE{œbaseClassesœ:[œTextœ,œstrœ,œobjectœ,œRecordœ],œdataTypeœ:œChatInputœ,œidœ:œChatInput-yxMKEœ}-AstraDBSearch-41nRz{œfieldNameœ:œinput_valueœ,œidœ:œAstraDBSearch-41nRzœ,œinputTypesœ:[œTextœ],œtypeœ:œstrœ}" }, { "source": "RecursiveCharacterTextSplitter-tR9QM", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153RecursiveCharacterTextSplitter\u0153,\u0153id\u0153:\u0153RecursiveCharacterTextSplitter-tR9QM\u0153}", + "sourceHandle": "{œbaseClassesœ:[œRecordœ],œdataTypeœ:œRecursiveCharacterTextSplitterœ,œidœ:œRecursiveCharacterTextSplitter-tR9QMœ}", "target": "AstraDB-eUCSS", - "targetHandle": "{\u0153fieldName\u0153:\u0153inputs\u0153,\u0153id\u0153:\u0153AstraDB-eUCSS\u0153,\u0153inputTypes\u0153:null,\u0153type\u0153:\u0153Record\u0153}", + "targetHandle": "{œfieldNameœ:œinputsœ,œidœ:œAstraDB-eUCSSœ,œinputTypesœ:null,œtypeœ:œRecordœ}", "data": { "targetHandle": { "fieldName": "inputs", @@ -3085,14 +3085,14 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-RecursiveCharacterTextSplitter-tR9QM{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153RecursiveCharacterTextSplitter\u0153,\u0153id\u0153:\u0153RecursiveCharacterTextSplitter-tR9QM\u0153}-AstraDB-eUCSS{\u0153fieldName\u0153:\u0153inputs\u0153,\u0153id\u0153:\u0153AstraDB-eUCSS\u0153,\u0153inputTypes\u0153:null,\u0153type\u0153:\u0153Record\u0153}", + "id": "reactflow__edge-RecursiveCharacterTextSplitter-tR9QM{œbaseClassesœ:[œRecordœ],œdataTypeœ:œRecursiveCharacterTextSplitterœ,œidœ:œRecursiveCharacterTextSplitter-tR9QMœ}-AstraDB-eUCSS{œfieldNameœ:œinputsœ,œidœ:œAstraDB-eUCSSœ,œinputTypesœ:null,œtypeœ:œRecordœ}", "selected": false }, { "source": "OpenAIEmbeddings-9TPjc", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Embeddings\u0153],\u0153dataType\u0153:\u0153OpenAIEmbeddings\u0153,\u0153id\u0153:\u0153OpenAIEmbeddings-9TPjc\u0153}", + "sourceHandle": "{œbaseClassesœ:[œEmbeddingsœ],œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-9TPjcœ}", "target": "AstraDB-eUCSS", - "targetHandle": "{\u0153fieldName\u0153:\u0153embedding\u0153,\u0153id\u0153:\u0153AstraDB-eUCSS\u0153,\u0153inputTypes\u0153:null,\u0153type\u0153:\u0153Embeddings\u0153}", + "targetHandle": "{œfieldNameœ:œembeddingœ,œidœ:œAstraDB-eUCSSœ,œinputTypesœ:null,œtypeœ:œEmbeddingsœ}", "data": { "targetHandle": { "fieldName": "embedding", @@ -3110,14 +3110,14 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-OpenAIEmbeddings-9TPjc{\u0153baseClasses\u0153:[\u0153Embeddings\u0153],\u0153dataType\u0153:\u0153OpenAIEmbeddings\u0153,\u0153id\u0153:\u0153OpenAIEmbeddings-9TPjc\u0153}-AstraDB-eUCSS{\u0153fieldName\u0153:\u0153embedding\u0153,\u0153id\u0153:\u0153AstraDB-eUCSS\u0153,\u0153inputTypes\u0153:null,\u0153type\u0153:\u0153Embeddings\u0153}", + "id": "reactflow__edge-OpenAIEmbeddings-9TPjc{œbaseClassesœ:[œEmbeddingsœ],œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-9TPjcœ}-AstraDB-eUCSS{œfieldNameœ:œembeddingœ,œidœ:œAstraDB-eUCSSœ,œinputTypesœ:null,œtypeœ:œEmbeddingsœ}", "selected": false }, { "source": "AstraDBSearch-41nRz", - "sourceHandle": "{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153AstraDBSearch\u0153,\u0153id\u0153:\u0153AstraDBSearch-41nRz\u0153}", + "sourceHandle": "{œbaseClassesœ:[œRecordœ],œdataTypeœ:œAstraDBSearchœ,œidœ:œAstraDBSearch-41nRzœ}", "target": "TextOutput-BDknO", - "targetHandle": "{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-BDknO\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-BDknOœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}", "data": { "targetHandle": { "fieldName": "input_value", @@ -3135,7 +3135,7 @@ "stroke": "#555" }, "className": "stroke-gray-900 stroke-connection", - "id": "reactflow__edge-AstraDBSearch-41nRz{\u0153baseClasses\u0153:[\u0153Record\u0153],\u0153dataType\u0153:\u0153AstraDBSearch\u0153,\u0153id\u0153:\u0153AstraDBSearch-41nRz\u0153}-TextOutput-BDknO{\u0153fieldName\u0153:\u0153input_value\u0153,\u0153id\u0153:\u0153TextOutput-BDknO\u0153,\u0153inputTypes\u0153:[\u0153Record\u0153,\u0153Text\u0153],\u0153type\u0153:\u0153str\u0153}" + "id": "reactflow__edge-AstraDBSearch-41nRz{œbaseClassesœ:[œRecordœ],œdataTypeœ:œAstraDBSearchœ,œidœ:œAstraDBSearch-41nRzœ}-TextOutput-BDknO{œfieldNameœ:œinput_valueœ,œidœ:œTextOutput-BDknOœ,œinputTypesœ:[œRecordœ,œTextœ],œtypeœ:œstrœ}" } ], "viewport": { diff --git a/src/frontend/src/components/shadTooltipComponent/index.tsx b/src/frontend/src/components/shadTooltipComponent/index.tsx index 080935b6f..95a14520c 100644 --- a/src/frontend/src/components/shadTooltipComponent/index.tsx +++ b/src/frontend/src/components/shadTooltipComponent/index.tsx @@ -1,6 +1,11 @@ import { ShadToolTipType } from "../../types/components"; import { cn } from "../../utils/utils"; -import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; +import { + Tooltip, + TooltipContent, + TooltipContentWithoutPortal, + TooltipTrigger, +} from "../ui/tooltip"; export default function ShadTooltip({ content, @@ -8,20 +13,23 @@ export default function ShadTooltip({ asChild = true, children, styleClasses, + portal = true, delayDuration = 500, }: ShadToolTipType): JSX.Element { + const TooltipContentComponent = portal + ? TooltipContent + : TooltipContentWithoutPortal; return ( {children} - - {content} - + ); } diff --git a/src/frontend/src/components/ui/tooltip.tsx b/src/frontend/src/components/ui/tooltip.tsx index 4120219ec..3ee8b9c7a 100644 --- a/src/frontend/src/components/ui/tooltip.tsx +++ b/src/frontend/src/components/ui/tooltip.tsx @@ -20,7 +20,7 @@ const TooltipContent = React.forwardRef< sideOffset={sideOffset} className={cn( "z-45 overflow-y-auto rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-50 data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1", - className + className, )} {...props} /> @@ -28,4 +28,26 @@ const TooltipContent = React.forwardRef< )); TooltipContent.displayName = TooltipPrimitive.Content.displayName; -export { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger }; +const TooltipContentWithoutPortal = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + +)); +TooltipContentWithoutPortal.displayName = TooltipPrimitive.Content.displayName; + +export { + Tooltip, + TooltipContent, + TooltipContentWithoutPortal, + TooltipProvider, + TooltipTrigger, +}; diff --git a/src/frontend/src/modals/editNodeModal/index.tsx b/src/frontend/src/modals/editNodeModal/index.tsx index b9295fc8a..8325a3ca2 100644 --- a/src/frontend/src/modals/editNodeModal/index.tsx +++ b/src/frontend/src/modals/editNodeModal/index.tsx @@ -143,6 +143,7 @@ const EditNodeModal = forwardRef( PARAM + DESC VALUE @@ -197,11 +198,17 @@ const EditNodeModal = forwardRef( > @@ -214,6 +221,20 @@ const EditNodeModal = forwardRef( + + + + {data.node?.template[templateParam]?.info ?? + ""} + + + Date: Sun, 2 Jun 2024 23:18:24 -0300 Subject: [PATCH 14/52] Removed shadow from card --- src/frontend/src/components/ui/card.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/components/ui/card.tsx b/src/frontend/src/components/ui/card.tsx index 459396f46..a558fb645 100644 --- a/src/frontend/src/components/ui/card.tsx +++ b/src/frontend/src/components/ui/card.tsx @@ -8,8 +8,8 @@ const Card = React.forwardRef<
@@ -36,7 +36,7 @@ const CardTitle = React.forwardRef< ref={ref} className={cn( "text-base font-semibold leading-tight tracking-tight", - className + className, )} {...props} /> From 06a568f11426d124134f42d73fd13413e0b46ba1 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Sun, 2 Jun 2024 23:55:20 -0300 Subject: [PATCH 15/52] Disabled accordion when it is empty --- .../components/accordionComponent/index.tsx | 17 ++++++++---- src/frontend/src/components/ui/accordion.tsx | 27 +++++++++++++++---- src/frontend/src/modals/IOModal/index.tsx | 4 +++ src/frontend/src/types/components/index.ts | 1 + 4 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/frontend/src/components/accordionComponent/index.tsx b/src/frontend/src/components/accordionComponent/index.tsx index fdcf8b96c..7c5562e7f 100644 --- a/src/frontend/src/components/accordionComponent/index.tsx +++ b/src/frontend/src/components/accordionComponent/index.tsx @@ -6,10 +6,13 @@ import { AccordionTrigger, } from "../../components/ui/accordion"; import { AccordionComponentType } from "../../types/components"; +import { cn } from "../../utils/utils"; +import ShadTooltip from "../shadTooltipComponent"; export default function AccordionComponent({ trigger, children, + disabled, open = [], keyValue, sideBar, @@ -29,7 +32,9 @@ export default function AccordionComponent({ } function handleClick(): void { - value === "" ? setValue(keyValue!) : setValue(""); + if (!disabled) { + value === "" ? setValue(keyValue!) : setValue(""); + } } return ( @@ -38,16 +43,18 @@ export default function AccordionComponent({ type="single" className="w-full" value={value} - onValueChange={setValue} + onValueChange={!disabled ? setValue : () => {}} > { handleClick(); }} - className={ - sideBar ? "w-full bg-muted px-[0.75rem] py-[0.5rem]" : "ml-3" - } + disabled={disabled} + className={cn( + sideBar ? "w-full bg-muted px-[0.75rem] py-[0.5rem]" : "ml-3", + disabled ? "cursor-not-allowed" : "cursor-pointer", + )} > {trigger} diff --git a/src/frontend/src/components/ui/accordion.tsx b/src/frontend/src/components/ui/accordion.tsx index b0eaaf47a..39578095b 100644 --- a/src/frontend/src/components/ui/accordion.tsx +++ b/src/frontend/src/components/ui/accordion.tsx @@ -4,6 +4,7 @@ import * as AccordionPrimitive from "@radix-ui/react-accordion"; import { ChevronDownIcon } from "@radix-ui/react-icons"; import * as React from "react"; import { cn } from "../../utils/utils"; +import ShadTooltip from "../shadTooltipComponent"; const Accordion = AccordionPrimitive.Root; @@ -22,17 +23,33 @@ AccordionItem.displayName = "AccordionItem"; const AccordionTrigger = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef ->(({ className, children, ...props }, ref) => ( +>(({ className, children, disabled, ...props }, ref) => ( - +
svg]:rotate-180", - className + className, )} > {children} - + + +
@@ -47,7 +64,7 @@ const AccordionContent = React.forwardRef< ref={ref} className={cn( "data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm", - className + className, )} {...props} > diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index 4d7453619..a3d00e89c 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -253,6 +253,10 @@ export default function IOModal({ key={index} > Date: Sun, 2 Jun 2024 23:56:36 -0300 Subject: [PATCH 16/52] Fixed tooltip that does not need removal of portal --- .../src/components/shadTooltipComponent/index.tsx | 15 +++------------ src/frontend/src/modals/editNodeModal/index.tsx | 4 ++-- src/frontend/src/types/components/index.ts | 1 - 3 files changed, 5 insertions(+), 15 deletions(-) diff --git a/src/frontend/src/components/shadTooltipComponent/index.tsx b/src/frontend/src/components/shadTooltipComponent/index.tsx index 95a14520c..6bfad61ee 100644 --- a/src/frontend/src/components/shadTooltipComponent/index.tsx +++ b/src/frontend/src/components/shadTooltipComponent/index.tsx @@ -1,11 +1,6 @@ import { ShadToolTipType } from "../../types/components"; import { cn } from "../../utils/utils"; -import { - Tooltip, - TooltipContent, - TooltipContentWithoutPortal, - TooltipTrigger, -} from "../ui/tooltip"; +import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip"; export default function ShadTooltip({ content, @@ -13,23 +8,19 @@ export default function ShadTooltip({ asChild = true, children, styleClasses, - portal = true, delayDuration = 500, }: ShadToolTipType): JSX.Element { - const TooltipContentComponent = portal - ? TooltipContent - : TooltipContentWithoutPortal; return ( {children} - {content} - + ); } diff --git a/src/frontend/src/modals/editNodeModal/index.tsx b/src/frontend/src/modals/editNodeModal/index.tsx index 8325a3ca2..252237ef8 100644 --- a/src/frontend/src/modals/editNodeModal/index.tsx +++ b/src/frontend/src/modals/editNodeModal/index.tsx @@ -198,7 +198,7 @@ const EditNodeModal = forwardRef( > Date: Sun, 2 Jun 2024 23:58:25 -0300 Subject: [PATCH 17/52] Fixed node toolbar allowing moving the nodes --- .../pages/FlowPage/components/nodeToolbarComponent/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index a4cf2840f..60fd2bfc7 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -387,7 +387,7 @@ export default function NodeToolbarComponent({ return ( <> -
+
{hasCode && ( From aa568f551a90d6e4d4797f1ebe9dc5c7943693b0 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Mon, 3 Jun 2024 00:00:28 -0300 Subject: [PATCH 18/52] Fixed Endpoint Name labeling --- src/frontend/src/components/editFlowSettingsComponent/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/components/editFlowSettingsComponent/index.tsx b/src/frontend/src/components/editFlowSettingsComponent/index.tsx index fc3be2197..4794ad802 100644 --- a/src/frontend/src/components/editFlowSettingsComponent/index.tsx +++ b/src/frontend/src/components/editFlowSettingsComponent/index.tsx @@ -109,7 +109,7 @@ export const EditFlowSettings: React.FC = ({ {setEndpointName && (
-
+
Select items to duplicate + ) : ( + Duplicate selected items + ) + } + > + + +
+
+ Select items to delete ) : ( Delete selected items ) } > - From 6a868729b8c24d92dbccd5f6068983ae9b85cd3c Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Mon, 3 Jun 2024 00:23:36 -0300 Subject: [PATCH 20/52] Made Folders more pleasing --- .../components/sideBarFolderButtons/index.tsx | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx index e842d439c..18b67068e 100644 --- a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx +++ b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx @@ -93,24 +93,23 @@ const SideBarFoldersButtonsComponent = ({ return ( <> -
-
From 5b2791a2c40e37f24534c13f9c178e72ca7103bd Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 08:19:10 -0300 Subject: [PATCH 21/52] Formatting --- .../starter_projects/Basic Prompting (Hello, world!).json | 2 +- .../initial_setup/starter_projects/Langflow Blog Writter.json | 2 +- .../initial_setup/starter_projects/Langflow Document QA.json | 2 +- .../starter_projects/Langflow Memory Conversation.json | 2 +- .../starter_projects/Langflow Prompt Chaining.json | 4 ++-- .../initial_setup/starter_projects/VectorStore-RAG-Flows.json | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json index 1dfe86b4a..38e01111b 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Basic Prompting (Hello, world!).json @@ -149,7 +149,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json index 6df211ba6..fcdef4056 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Blog Writter.json @@ -453,7 +453,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json index 71b1b5c41..5fa8547a1 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Document QA.json @@ -598,7 +598,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json index e26ffa21d..1002b721a 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Memory Conversation.json @@ -679,7 +679,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json index b6dfa2f10..d49eba054 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Langflow Prompt Chaining.json @@ -798,7 +798,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, @@ -1155,7 +1155,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, diff --git a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json index 8a5df6666..bbf59c8b1 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/VectorStore-RAG-Flows.json @@ -793,7 +793,7 @@ "list": false, "show": true, "multiline": true, - "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: Optional[float] = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", + "value": "from typing import Optional\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import NestedDict, Text\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n\n field_order = [\n \"max_tokens\",\n \"model_kwargs\",\n \"model_name\",\n \"openai_api_base\",\n \"openai_api_key\",\n \"temperature\",\n \"input_value\",\n \"system_message\",\n \"stream\",\n ]\n\n def build_config(self):\n return {\n \"input_value\": {\"display_name\": \"Input\"},\n \"max_tokens\": {\n \"display_name\": \"Max Tokens\",\n \"advanced\": True,\n \"info\": \"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n },\n \"model_kwargs\": {\n \"display_name\": \"Model Kwargs\",\n \"advanced\": True,\n },\n \"model_name\": {\n \"display_name\": \"Model Name\",\n \"advanced\": False,\n \"options\": MODEL_NAMES,\n },\n \"openai_api_base\": {\n \"display_name\": \"OpenAI API Base\",\n \"advanced\": True,\n \"info\": (\n \"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1.\\n\\n\"\n \"You can change this to use other APIs like JinaChat, LocalAI and Prem.\"\n ),\n },\n \"openai_api_key\": {\n \"display_name\": \"OpenAI API Key\",\n \"info\": \"The OpenAI API Key to use for the OpenAI model.\",\n \"advanced\": False,\n \"password\": True,\n },\n \"temperature\": {\n \"display_name\": \"Temperature\",\n \"advanced\": False,\n \"value\": 0.1,\n },\n \"stream\": {\n \"display_name\": \"Stream\",\n \"info\": STREAM_INFO_TEXT,\n \"advanced\": True,\n },\n \"system_message\": {\n \"display_name\": \"System Message\",\n \"info\": \"System message to pass to the model.\",\n \"advanced\": True,\n },\n }\n\n def build(\n self,\n input_value: Text,\n openai_api_key: str,\n temperature: float = 0.1,\n model_name: str = \"gpt-4o\",\n max_tokens: Optional[int] = 256,\n model_kwargs: NestedDict = {},\n openai_api_base: Optional[str] = None,\n stream: bool = False,\n system_message: Optional[str] = None,\n ) -> Text:\n if not openai_api_base:\n openai_api_base = \"https://api.openai.com/v1\"\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature,\n )\n\n return self.get_chat_result(output, stream, input_value, system_message)\n", "fileTypes": [], "file_path": "", "password": false, From 1f2cd3234047c6af66c7b494d939bc103f8e924f Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 09:12:28 -0300 Subject: [PATCH 22/52] Modularized Loading on buttons --- src/frontend/src/components/ui/button.tsx | 35 +++++++++++++++++------ 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index 431287f39..5fee364b4 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -2,6 +2,7 @@ import { Slot } from "@radix-ui/react-slot"; import { cva, type VariantProps } from "class-variance-authority"; import * as React from "react"; import { cn } from "../../utils/utils"; +import ForwardedIconComponent from "../genericIconComponent"; const buttonVariants = cva( "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", @@ -32,13 +33,14 @@ const buttonVariants = cva( variant: "default", size: "default", }, - } + }, ); export interface ButtonProps extends React.ButtonHTMLAttributes, VariantProps { asChild?: boolean; + loading?: boolean; } function toTitleCase(text: string) { @@ -49,21 +51,36 @@ function toTitleCase(text: string) { } const Button = React.forwardRef( - ({ className, variant, size, asChild = false, children, ...props }, ref) => { + ( + { className, variant, size, loading, asChild = false, children, ...props }, + ref, + ) => { const Comp = asChild ? Slot : "button"; let newChildren = children; if (typeof children === "string") { newChildren = toTitleCase(children); } return ( - + +
+ +
+
+ {newChildren} +
+
); - } + }, ); Button.displayName = "Button"; From 65ae9f04e4b07fe3b616d43e51ff86129f5b7396 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 09:20:32 -0300 Subject: [PATCH 23/52] Fixed bottom padding on settings pages --- src/frontend/src/pages/SettingsPage/index.tsx | 2 +- .../src/pages/SettingsPage/pages/GlobalVariablesPage/index.tsx | 2 +- .../src/pages/SettingsPage/pages/ShortcutsPage/index.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/pages/SettingsPage/index.tsx b/src/frontend/src/pages/SettingsPage/index.tsx index e4f856599..385c38cb6 100644 --- a/src/frontend/src/pages/SettingsPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/index.tsx @@ -53,7 +53,7 @@ export default function SettingsPage(): JSX.Element { -
+
diff --git a/src/frontend/src/pages/SettingsPage/pages/GlobalVariablesPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GlobalVariablesPage/index.tsx index 65f7ecd41..195e5d37c 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GlobalVariablesPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GlobalVariablesPage/index.tsx @@ -170,7 +170,7 @@ export default function GlobalVariablesPage() {
-
+
-
+
Date: Tue, 4 Jun 2024 09:20:55 -0300 Subject: [PATCH 24/52] Added Store API Key configuration on General Settings page --- src/frontend/src/constants/constants.ts | 1 + .../SettingsPage/pages/GeneralPage/index.tsx | 105 +++++++++++++++++- src/frontend/src/types/components/index.ts | 1 + 3 files changed, 104 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/constants/constants.ts b/src/frontend/src/constants/constants.ts index 47563b9c3..b0a0b55c5 100644 --- a/src/frontend/src/constants/constants.ts +++ b/src/frontend/src/constants/constants.ts @@ -590,6 +590,7 @@ export const CONTROL_PATCH_USER_STATE = { password: "", cnfPassword: "", gradient: "", + apikey: "", }; export const CONTROL_LOGIN_STATE = { diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx index 5c53e0aa8..d10c450c1 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx @@ -14,14 +14,25 @@ import { CardTitle, } from "../../../../components/ui/card"; import { + API_ERROR_ALERT, + API_SUCCESS_ALERT, EDIT_PASSWORD_ALERT_LIST, EDIT_PASSWORD_ERROR_ALERT, SAVE_ERROR_ALERT, SAVE_SUCCESS_ALERT, } from "../../../../constants/alerts_constants"; -import { CONTROL_PATCH_USER_STATE } from "../../../../constants/constants"; +import { + CONTROL_PATCH_USER_STATE, + INSERT_API_KEY, + INVALID_API_KEY, + NO_API_KEY, +} from "../../../../constants/constants"; import { AuthContext } from "../../../../contexts/authContext"; -import { resetPassword, updateUser } from "../../../../controllers/API"; +import { + addApiKeyStore, + resetPassword, + updateUser, +} from "../../../../controllers/API"; import useAlertStore from "../../../../stores/alertStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { @@ -29,6 +40,7 @@ import { patchUserInputStateType, } from "../../../../types/components"; import { gradients } from "../../../../utils/styleUtils"; +import { useStoreStore } from "../../../../stores/storeStore"; export default function GeneralPage() { const setCurrentFlowId = useFlowsManagerStore( @@ -48,7 +60,16 @@ export default function GeneralPage() { const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const { userData, setUserData } = useContext(AuthContext); - const { password, cnfPassword, gradient } = inputState; + const hasStore = useStoreStore((state) => state.hasStore); + const { storeApiKey } = useContext(AuthContext); + + const validApiKey = useStoreStore((state) => state.validApiKey); + const hasApiKey = useStoreStore((state) => state.hasApiKey); + const setHasApiKey = useStoreStore((state) => state.updateHasApiKey); + const loadingApiKey = useStoreStore((state) => state.loadingApiKey); + const setValidApiKey = useStoreStore((state) => state.updateValidApiKey); + const setLoadingApiKey = useStoreStore((state) => state.updateLoadingApiKey); + const { password, cnfPassword, gradient, apikey } = inputState; async function handlePatchPassword() { if (password !== cnfPassword) { @@ -90,11 +111,38 @@ export default function GeneralPage() { } } + const handleSaveKey = () => { + if (apikey) { + addApiKeyStore(apikey).then( + () => { + setSuccessData({ + title: API_SUCCESS_ALERT, + }); + storeApiKey(apikey); + setHasApiKey(true); + setValidApiKey(true); + setLoadingApiKey(false); + handleInput({ target: { name: "apikey", value: "" } }); + }, + (error) => { + setErrorData({ + title: API_ERROR_ALERT, + list: [error["response"]["data"]["detail"]], + }); + setHasApiKey(false); + setValidApiKey(false); + setLoadingApiKey(false); + }, + ); + } + }; + function handleInput({ target: { name, value }, }: inputHandlerEventType): void { setInputState((prev) => ({ ...prev, [name]: value })); } + return (
@@ -217,6 +265,57 @@ export default function GeneralPage() { )} + {hasStore && ( + { + event.preventDefault(); + handleSaveKey(); + }} + > + + + API Key + + {(hasApiKey && !validApiKey + ? INVALID_API_KEY + : !hasApiKey + ? NO_API_KEY + : "") + INSERT_API_KEY} + + + +
+ + { + handleInput({ target: { name: "apikey", value } }); + }} + value={apikey} + isForm + password={true} + placeholder="Insert your API Key" + className="w-full" + /> + + Please enter your API Key + + +
+
+ + + + + +
+
+ )}
); diff --git a/src/frontend/src/types/components/index.ts b/src/frontend/src/types/components/index.ts index b955003de..199cbb1f2 100644 --- a/src/frontend/src/types/components/index.ts +++ b/src/frontend/src/types/components/index.ts @@ -379,6 +379,7 @@ export type patchUserInputStateType = { password: string; cnfPassword: string; gradient: string; + apikey: string; }; export type UserInputType = { From 3d8a0acbc1fec4289b83c5cd3770494f89ecf54f Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 09:37:23 -0300 Subject: [PATCH 25/52] Fixed sidebar buttons not being different when path is equal --- .../components/sideBarButtons/index.tsx | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx b/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx index 24282a2b3..3509c396a 100644 --- a/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx +++ b/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx @@ -11,7 +11,10 @@ type SideBarButtonsComponentProps = { pathname: string; handleOpenNewFolderModal?: () => void; }; -const SideBarButtonsComponent = ({ items }: SideBarButtonsComponentProps) => { +const SideBarButtonsComponent = ({ + items, + pathname, +}: SideBarButtonsComponentProps) => { return ( <> {items.map((item) => ( @@ -21,7 +24,10 @@ const SideBarButtonsComponent = ({ items }: SideBarButtonsComponentProps) => { data-testid={`sidebar-nav-${item.title}`} className={cn( buttonVariants({ variant: "ghost" }), - "!w-[200px] cursor-pointer justify-start gap-2 border border-transparent hover:border-border hover:bg-transparent", + pathname === item.href + ? "border border-border bg-muted hover:bg-muted" + : "border border-transparent hover:border-border hover:bg-transparent", + "w-full justify-start gap-2", )} > {item.title} From 59c8eba1326310406ad7c371fd9ef470b80f93d4 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 09:58:44 -0300 Subject: [PATCH 26/52] Fixed Button classes to allow loading --- src/frontend/src/components/ui/button.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index 5fee364b4..d84f8f9c9 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -4,8 +4,11 @@ import * as React from "react"; import { cn } from "../../utils/utils"; import ForwardedIconComponent from "../genericIconComponent"; +const buttonChildrenClasses = + "inline-flex items-center justify-center text-sm font-medium"; + const buttonVariants = cva( - "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", + "inline-flex items-center justify-center text-sm font-medium relative rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", { variants: { variant: { @@ -61,7 +64,7 @@ const Button = React.forwardRef( newChildren = toTitleCase(children); } return ( - +
(
{newChildren} From b5d9e0608d8b5bbcfcba951a0e089d5df95c3915 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 10:15:02 -0300 Subject: [PATCH 27/52] Fixed classes on Button to work with all of the current buttons --- src/frontend/src/components/ui/button.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index d84f8f9c9..33a8e8ba0 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -23,6 +23,7 @@ const buttonVariants = cva( "border border-muted bg-muted text-secondary-foreground hover:bg-secondary-foreground/5", ghost: "hover:bg-accent hover:text-accent-foreground", link: "underline-offset-4 hover:underline text-primary", + none: "", }, size: { default: "h-10 py-2 px-4", @@ -30,6 +31,7 @@ const buttonVariants = cva( xs: "py-0.5 px-3 rounded-md", lg: "h-11 px-8 rounded-md", icon: "py-1 px-1 rounded-md", + none: "", }, }, defaultVariants: { @@ -64,7 +66,11 @@ const Button = React.forwardRef( newChildren = toTitleCase(children); } return ( - +
(
{newChildren} From 9eacd3f9d6cb06f086461a85902e342f5ce310d4 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 10:15:24 -0300 Subject: [PATCH 28/52] Fixed sidebar button to not look strange with the new Button classes --- .../sidebarComponent/components/sideBarFolderButtons/index.tsx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx index 18b67068e..dcdefe060 100644 --- a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx +++ b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx @@ -304,7 +304,8 @@ const SideBarFoldersButtonsComponent = ({ e.stopPropagation(); e.preventDefault(); }} - variant={"ghost"} + size="none" + variant="none" > Date: Tue, 4 Jun 2024 10:26:52 -0300 Subject: [PATCH 29/52] Fixed button not being able to handle multiple children when asChild is done --- src/frontend/src/components/ui/button.tsx | 49 ++++++++++++----------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index 33a8e8ba0..ed0abc120 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -66,30 +66,31 @@ const Button = React.forwardRef( newChildren = toTitleCase(children); } return ( - -
- -
-
- {newChildren} + +
+
+ +
+
+ {newChildren} +
); From f1aaa162e2dd68f2cd896d2542aa5c2c564c093d Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 10:49:36 -0300 Subject: [PATCH 30/52] Changed strategy to not have to apply classes to different div elements, not breaking the other buttons. --- src/frontend/src/components/ui/button.tsx | 68 ++++++++++++----------- 1 file changed, 37 insertions(+), 31 deletions(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index ed0abc120..6107b78a3 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -4,11 +4,8 @@ import * as React from "react"; import { cn } from "../../utils/utils"; import ForwardedIconComponent from "../genericIconComponent"; -const buttonChildrenClasses = - "inline-flex items-center justify-center text-sm font-medium"; - const buttonVariants = cva( - "inline-flex items-center justify-center text-sm font-medium relative rounded-md transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", + "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", { variants: { variant: { @@ -23,7 +20,6 @@ const buttonVariants = cva( "border border-muted bg-muted text-secondary-foreground hover:bg-secondary-foreground/5", ghost: "hover:bg-accent hover:text-accent-foreground", link: "underline-offset-4 hover:underline text-primary", - none: "", }, size: { default: "h-10 py-2 px-4", @@ -31,7 +27,6 @@ const buttonVariants = cva( xs: "py-0.5 px-3 rounded-md", lg: "h-11 px-8 rounded-md", icon: "py-1 px-1 rounded-md", - none: "", }, }, defaultVariants: { @@ -65,34 +60,45 @@ const Button = React.forwardRef( if (typeof children === "string") { newChildren = toTitleCase(children); } + const [width, setWidth] = React.useState(null); + const hiddenRef = React.useRef(null); + + React.useEffect(() => { + if (hiddenRef.current) { + setWidth(hiddenRef.current.offsetWidth); + } + }, [children, hiddenRef]); return ( - -
-
+ + <> + {loading ? ( +
+ +
+ ) : ( + children )} - > - -
-
- {newChildren} -
+ + +
+ {children}
- + ); }, ); From 2c851a9d5c50be96abff1c2cba68d794de868439 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 11:09:00 -0300 Subject: [PATCH 31/52] Fix naming of endpoint --- src/frontend/src/components/editFlowSettingsComponent/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/components/editFlowSettingsComponent/index.tsx b/src/frontend/src/components/editFlowSettingsComponent/index.tsx index 4794ad802..26bc138e3 100644 --- a/src/frontend/src/components/editFlowSettingsComponent/index.tsx +++ b/src/frontend/src/components/editFlowSettingsComponent/index.tsx @@ -123,7 +123,7 @@ export const EditFlowSettings: React.FC = ({ type="text" name="endpoint_name" value={endpointName ?? ""} - placeholder="An alternative name for the run endpoint" + placeholder="An alternative name to run the endpoint" maxLength={maxLength} id="endpoint_name" onDoubleClickCapture={(event) => { From 5b83bde64e1a6365bd82e9ad226a350ab82f3ef9 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 11:17:17 -0300 Subject: [PATCH 32/52] Returned loader to default, fixed buttons not working with asChild --- src/frontend/src/components/ui/button.tsx | 38 +++++------------------ 1 file changed, 8 insertions(+), 30 deletions(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index 6107b78a3..ca3001511 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -60,14 +60,6 @@ const Button = React.forwardRef( if (typeof children === "string") { newChildren = toTitleCase(children); } - const [width, setWidth] = React.useState(null); - const hiddenRef = React.useRef(null); - - React.useEffect(() => { - if (hiddenRef.current) { - setWidth(hiddenRef.current.offsetWidth); - } - }, [children, hiddenRef]); return ( <> ( ref={ref} {...props} > - <> - {loading ? ( -
- -
- ) : ( - children - )} - + {loading ? ( + + ) : ( + newChildren + )}
-
- {children} -
); }, From cad4c508a9f405e69fb2549a36768c895cf6ca45 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 11:35:37 -0300 Subject: [PATCH 33/52] Fixed button layout to have loading the same size as the original button --- src/frontend/src/components/ui/button.tsx | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index ca3001511..d81c31f31 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -68,10 +68,17 @@ const Button = React.forwardRef( {...props} > {loading ? ( - + + + {newChildren} + + + + + ) : ( newChildren )} From 98e40f25e68c5ec88c629a50c04588bcb25942fe Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 11:52:08 -0300 Subject: [PATCH 34/52] Fixed id scrolling when clicking from Store --- .../SettingsPage/pages/GeneralPage/index.tsx | 15 +++++++-- src/frontend/src/pages/StorePage/index.tsx | 31 +++++++++---------- src/frontend/src/routes.tsx | 21 ++++++------- 3 files changed, 36 insertions(+), 31 deletions(-) diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx index d10c450c1..52966ac9c 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx @@ -41,12 +41,23 @@ import { } from "../../../../types/components"; import { gradients } from "../../../../utils/styleUtils"; import { useStoreStore } from "../../../../stores/storeStore"; +import { useParams } from "react-router-dom"; export default function GeneralPage() { const setCurrentFlowId = useFlowsManagerStore( (state) => state.setCurrentFlowId, ); + const { scrollId } = useParams(); + + useEffect(() => { + const element = document.getElementById(scrollId ?? "null"); + if (element) { + // 👇 Will scroll smoothly to the top of the next section + element.scrollIntoView({ behavior: "smooth" }); + } + }, [scrollId]); + const [inputState, setInputState] = useState( CONTROL_PATCH_USER_STATE, ); @@ -272,9 +283,9 @@ export default function GeneralPage() { handleSaveKey(); }} > - + - API Key + Store API Key {(hasApiKey && !validApiKey ? INVALID_API_KEY diff --git a/src/frontend/src/pages/StorePage/index.tsx b/src/frontend/src/pages/StorePage/index.tsx index 3b4612281..f105c8c86 100644 --- a/src/frontend/src/pages/StorePage/index.tsx +++ b/src/frontend/src/pages/StorePage/index.tsx @@ -180,24 +180,21 @@ export default function StorePage(): JSX.Element { title={STORE_TITLE} description={STORE_DESC} button={ - <> - {StoreApiKeyModal && ( - - - + } >
diff --git a/src/frontend/src/routes.tsx b/src/frontend/src/routes.tsx index 49b905f41..d49e035f7 100644 --- a/src/frontend/src/routes.tsx +++ b/src/frontend/src/routes.tsx @@ -56,7 +56,7 @@ const Router = () => { > } /> } /> - } /> + } /> } /> { } /> - element= - { - - - - } - /> - } + + + + } + /> Date: Tue, 4 Jun 2024 11:54:32 -0300 Subject: [PATCH 35/52] Added icons --- .../sidebarComponent/components/sideBarButtons/index.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx b/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx index 3509c396a..21584c041 100644 --- a/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx +++ b/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx @@ -1,6 +1,7 @@ import { Link } from "react-router-dom"; import { cn } from "../../../../utils/utils"; import { buttonVariants } from "../../../ui/button"; +import ForwardedIconComponent from "../../../genericIconComponent"; type SideBarButtonsComponentProps = { items: { @@ -27,9 +28,10 @@ const SideBarButtonsComponent = ({ pathname === item.href ? "border border-border bg-muted hover:bg-muted" : "border border-transparent hover:border-border hover:bg-transparent", - "w-full justify-start gap-2", + "w-full justify-start gap-3", )} > + {item.icon} {item.title}
From bb6578e0a5771856fd40874552a85fee46ed97f2 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 14:21:12 -0300 Subject: [PATCH 36/52] Fixed classes of sidebar to make it more like the main page --- .../components/sideBarButtons/index.tsx | 10 ++++---- .../components/sideBarFolderButtons/index.tsx | 4 ++-- .../src/components/sidebarComponent/index.tsx | 24 +++++++++++-------- src/frontend/src/components/ui/button.tsx | 2 ++ src/frontend/src/pages/SettingsPage/index.tsx | 11 +++++---- 5 files changed, 31 insertions(+), 20 deletions(-) diff --git a/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx b/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx index 21584c041..10f3cd54d 100644 --- a/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx +++ b/src/frontend/src/components/sidebarComponent/components/sideBarButtons/index.tsx @@ -17,7 +17,7 @@ const SideBarButtonsComponent = ({ pathname, }: SideBarButtonsComponentProps) => { return ( - <> +
{items.map((item) => (
{item.icon} - {item.title} + + {item.title} +
))} - +
); }; export default SideBarButtonsComponent; diff --git a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx index dcdefe060..e7ae65b05 100644 --- a/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx +++ b/src/frontend/src/components/sidebarComponent/components/sideBarFolderButtons/index.tsx @@ -175,11 +175,11 @@ const SideBarFoldersButtonsComponent = ({ event.stopPropagation(); event.preventDefault(); }} - className="flex w-full items-center gap-2" + className="flex w-full items-center gap-4" > {editFolderName?.edit ? (
diff --git a/src/frontend/src/components/sidebarComponent/index.tsx b/src/frontend/src/components/sidebarComponent/index.tsx index 396373705..b4fbf11b3 100644 --- a/src/frontend/src/components/sidebarComponent/index.tsx +++ b/src/frontend/src/components/sidebarComponent/index.tsx @@ -41,16 +41,20 @@ export default function SidebarNav({ return ( diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index d81c31f31..faab61370 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -20,6 +20,7 @@ const buttonVariants = cva( "border border-muted bg-muted text-secondary-foreground hover:bg-secondary-foreground/5", ghost: "hover:bg-accent hover:text-accent-foreground", link: "underline-offset-4 hover:underline text-primary", + none: "", }, size: { default: "h-10 py-2 px-4", @@ -27,6 +28,7 @@ const buttonVariants = cva( xs: "py-0.5 px-3 rounded-md", lg: "h-11 px-8 rounded-md", icon: "py-1 px-1 rounded-md", + none: "", }, }, defaultVariants: { diff --git a/src/frontend/src/pages/SettingsPage/index.tsx b/src/frontend/src/pages/SettingsPage/index.tsx index 385c38cb6..0abab2aec 100644 --- a/src/frontend/src/pages/SettingsPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/index.tsx @@ -21,7 +21,7 @@ export default function SettingsPage(): JSX.Element { icon: ( ), }, @@ -32,7 +32,7 @@ export default function SettingsPage(): JSX.Element { icon: ( ), }, @@ -40,7 +40,10 @@ export default function SettingsPage(): JSX.Element { title: "Shortcuts", href: "/settings/shortcuts", icon: ( - + ), }, ]; @@ -50,7 +53,7 @@ export default function SettingsPage(): JSX.Element { description="Manage the general settings for Langflow." >
-
- - - - ); -} diff --git a/src/frontend/src/pages/StorePage/index.tsx b/src/frontend/src/pages/StorePage/index.tsx index f105c8c86..2fc401d8a 100644 --- a/src/frontend/src/pages/StorePage/index.tsx +++ b/src/frontend/src/pages/StorePage/index.tsx @@ -29,7 +29,6 @@ import { import { STORE_DESC, STORE_TITLE } from "../../constants/constants"; import { AuthContext } from "../../contexts/authContext"; import { getStoreComponents, getStoreTags } from "../../controllers/API"; -import StoreApiKeyModal from "../../modals/storeApiKeyModal"; import useAlertStore from "../../stores/alertStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore"; import { useStoreStore } from "../../stores/storeStore"; From 0ae32f89d2c95ecb60205d21aff13986ca301927 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Tue, 4 Jun 2024 17:49:58 -0300 Subject: [PATCH 42/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(playwright.config.t?= =?UTF-8?q?s):=20disable=20fullyParallel=20and=20increase=20workers=20to?= =?UTF-8?q?=205=20to=20optimize=20test=20execution=20=E2=99=BB=EF=B8=8F=20?= =?UTF-8?q?(addNewVariableButton.tsx):=20remove=20unused=20Button=20import?= =?UTF-8?q?=20and=20add=20data-testid=20to=20Save=20Variable=20button=20fo?= =?UTF-8?q?r=20better=20testing=20=E2=99=BB=EF=B8=8F=20(index.tsx):=20add?= =?UTF-8?q?=20preventDefault=20to=20button=20click=20handlers=20to=20preve?= =?UTF-8?q?nt=20form=20submission=20=E2=99=BB=EF=B8=8F=20(sideBarFolderBut?= =?UTF-8?q?tons):=20add=20data-testid=20attributes=20to=20buttons=20and=20?= =?UTF-8?q?inputs=20for=20better=20testing=20and=20readability?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ♻️ (api.tsx): refactor duplicate request check logic into helper function ✨ (api.tsx): add helper function to check and store duplicate requests ♻️ (genericNode): refactor code for better readability and maintainability 💡 (genericNode): add data-testid attribute for testing purposes 🐛 (use-fetch-data-on-mount): fix error handling to prevent undefined errors 🐛 (use-handle-new-value.tsx, use-handle-refresh-buttons.tsx): fix potential undefined errors in error handling ✨ (baseModal/index.tsx, flowSettingsModal/index.tsx): add data-testid attributes for better testability ♻️ (editNodeModal/index.tsx): refactor code for better readability and consistency ♻️ (nodeToolbarComponent): remove trailing commas to improve code style ♻️ (GeneralPage): reorder imports and remove trailing commas for consistency ✅ (chatInputOutput.spec.ts): update path to ChatTest.json for better file organization ✅ (chatInputOutputUser.spec.ts): add additional interactions to chat test ✅ (dragAndDrop.spec.ts): update file path for collection.json ✅ (dropdownComponent.spec.ts): fix formatting and improve locator usage ✅ (filterEdge.spec.ts): fix locator strings and remove redundant code ✅ (floatComponent.spec.ts): remove redundant cache checks and fix locator strings ✅ (flowSettings.spec.ts): update test to use data-testid for save button ✅ (folders.spec.ts): refactor folder creation and editing to use data-testid ✅ (globalVariables.spec.ts): remove redundant save button click in test ✅ (tests): update test locators to use more specific selectors - Change locators for "Save Changes" button to use `getByText` with exact match - Update locators for `showchroma_server_http_port` in `inputComponent.spec.ts` - Add additional test steps in `intComponent.spec.ts` for better coverage ✅ (keyPairListComponent.spec.ts, langflowShortcuts.spec.ts, logs.spec.ts): update locators for better test stability and readability ✅ (tests): update end-to-end tests for improved stability and accuracy ✅ (textInputOutput.spec.ts): update locators and placeholders for accuracy ✅ (toggleComponent.spec.ts): replace locators with getByText for clarity --- src/frontend/playwright.config.ts | 28 +++++----- .../addNewVariableButton.tsx | 13 ++--- .../components/inputListComponent/index.tsx | 6 ++- .../components/sideBarFolderButtons/index.tsx | 21 ++++---- src/frontend/src/controllers/API/api.tsx | 34 ++++-------- .../API/helpers/check-duplicate-requests.ts | 30 +++++++++++ .../src/customNodes/genericNode/index.tsx | 37 ++++++------- .../hooks/use-fetch-data-on-mount.tsx | 4 +- .../hooks/use-handle-new-value.tsx | 6 ++- .../hooks/use-handle-refresh-buttons.tsx | 2 +- src/frontend/src/modals/baseModal/index.tsx | 14 ++--- .../src/modals/editNodeModal/index.tsx | 42 +++++++-------- .../src/modals/flowSettingsModal/index.tsx | 4 +- .../components/nodeToolbarComponent/index.tsx | 28 +++++----- .../SettingsPage/pages/GeneralPage/index.tsx | 20 ++++--- .../tests/end-to-end/chatInputOutput.spec.ts | 2 +- .../end-to-end/chatInputOutputUser.spec.ts | 5 +- .../tests/end-to-end/dragAndDrop.spec.ts | 6 +-- .../end-to-end/dropdownComponent.spec.ts | 30 +++++------ .../tests/end-to-end/filterEdge.spec.ts | 30 +++++------ .../tests/end-to-end/floatComponent.spec.ts | 40 ++++++-------- .../tests/end-to-end/flowSettings.spec.ts | 7 +-- src/frontend/tests/end-to-end/folders.spec.ts | 53 ++++++------------- .../tests/end-to-end/globalVariables.spec.ts | 1 - .../tests/end-to-end/inputComponent.spec.ts | 36 ++++++------- .../end-to-end/inputListComponent.spec.ts | 2 +- .../tests/end-to-end/intComponent.spec.ts | 42 ++++++++------- .../end-to-end/keyPairListComponent.spec.ts | 8 +-- .../end-to-end/langflowShortcuts.spec.ts | 12 ++--- src/frontend/tests/end-to-end/logs.spec.ts | 2 + .../tests/end-to-end/nestedComponent.spec.ts | 44 +++++++-------- .../end-to-end/promptModalComponent.spec.ts | 6 +-- .../tests/end-to-end/saveComponents.spec.ts | 6 +-- src/frontend/tests/end-to-end/store.spec.ts | 6 ++- .../tests/end-to-end/textInputOutput.spec.ts | 14 ++--- .../tests/end-to-end/toggleComponent.spec.ts | 40 +++++++------- 36 files changed, 348 insertions(+), 333 deletions(-) create mode 100644 src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts diff --git a/src/frontend/playwright.config.ts b/src/frontend/playwright.config.ts index 9535e0a15..cf7d59472 100644 --- a/src/frontend/playwright.config.ts +++ b/src/frontend/playwright.config.ts @@ -15,13 +15,13 @@ dotenv.config({ path: path.resolve(__dirname, "../../.env") }); export default defineConfig({ testDir: "./tests", /* Run tests in files in parallel */ - fullyParallel: true, + fullyParallel: false, /* Fail the build on CI if you accidentally left test.only in the source code. */ forbidOnly: !!process.env.CI, /* Retry on CI only */ retries: process.env.CI ? 2 : 0, /* Opt out of parallel tests on CI. */ - workers: 1, + workers: 5, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ timeout: 120 * 1000, // reporter: [ @@ -52,18 +52,18 @@ export default defineConfig({ }, }, - { - name: "firefox", - use: { - ...devices["Desktop Firefox"], - launchOptions: { - firefoxUserPrefs: { - "dom.events.asyncClipboard.readText": true, - "dom.events.testing.asyncClipboard": true, - }, - }, - }, - }, + // { + // name: "firefox", + // use: { + // ...devices["Desktop Firefox"], + // launchOptions: { + // firefoxUserPrefs: { + // "dom.events.asyncClipboard.readText": true, + // "dom.events.testing.asyncClipboard": true, + // }, + // }, + // }, + // }, ], webServer: [ { diff --git a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx index 0076fa926..61dada650 100644 --- a/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx +++ b/src/frontend/src/components/addNewVariableButtonComponent/addNewVariableButton.tsx @@ -7,7 +7,6 @@ import { useTypesStore } from "../../stores/typesStore"; import { ResponseErrorDetailAPI } from "../../types/api"; import ForwardedIconComponent from "../genericIconComponent"; import InputComponent from "../inputComponent"; -import { Button } from "../ui/button"; import { Input } from "../ui/input"; import { Label } from "../ui/label"; import { Textarea } from "../ui/textarea"; @@ -24,19 +23,19 @@ export default function AddNewVariableButton({ children }): JSX.Element { const setErrorData = useAlertStore((state) => state.setErrorData); const componentFields = useTypesStore((state) => state.ComponentFields); const unavaliableFields = new Set( - Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields)), + Object.keys(useGlobalVariablesStore((state) => state.unavaliableFields)) ); const availableFields = () => { const fields = Array.from(componentFields).filter( - (field) => !unavaliableFields.has(field), + (field) => !unavaliableFields.has(field) ); return sortByName(fields); }; const addGlobalVariable = useGlobalVariablesStore( - (state) => state.addGlobalVariable, + (state) => state.addGlobalVariable ); function handleSaveVariable() { @@ -65,7 +64,7 @@ export default function AddNewVariableButton({ children }): JSX.Element { let responseError = error as ResponseErrorDetailAPI; setErrorData({ title: "Error creating variable", - list: [responseError.response.data.detail ?? "Unknown error"], + list: [responseError?.response?.data?.detail ?? "Unknown error"], }); }); } @@ -142,7 +141,9 @@ export default function AddNewVariableButton({ children }): JSX.Element { >
- + ); } diff --git a/src/frontend/src/components/inputListComponent/index.tsx b/src/frontend/src/components/inputListComponent/index.tsx index f0aa8cca7..ce55ff1d4 100644 --- a/src/frontend/src/components/inputListComponent/index.tsx +++ b/src/frontend/src/components/inputListComponent/index.tsx @@ -55,10 +55,11 @@ export default function InputListComponent({ /> {idx === value.length - 1 ? ( @@ -108,6 +109,7 @@ const SideBarFoldersButtonsComponent = ({ size="icon" className="px-2" onClick={handleUploadFlowsToFolder} + data-testid="upload-folder-button" > @@ -117,7 +119,7 @@ const SideBarFoldersButtonsComponent = ({ <> {folders.map((item, index) => { const editFolderName = editFolders?.filter( - (folder) => folder.name === item.name, + (folder) => folder.name === item.name )[0]; return (
handleChangeFolder!(item.id!)} > @@ -203,7 +205,7 @@ const SideBarFoldersButtonsComponent = ({ folders.map((obj) => ({ name: obj.name, edit: false, - })), + })) ); } if (e.key === "Enter") { @@ -236,10 +238,10 @@ const SideBarFoldersButtonsComponent = ({ }; const updatedFolder = await updateFolder( body, - item.id!, + item.id! ); const updateFolders = folders.filter( - (f) => f.name !== item.name, + (f) => f.name !== item.name ); setFolders([...updateFolders, updatedFolder]); setFoldersNames({}); @@ -247,7 +249,7 @@ const SideBarFoldersButtonsComponent = ({ folders.map((obj) => ({ name: obj.name, edit: false, - })), + })) ); } else { setFoldersNames((old) => ({ @@ -258,6 +260,7 @@ const SideBarFoldersButtonsComponent = ({ }} value={foldersNames[item.name]} id={`input-folder-${item.name}`} + data-testid={`input-folder`} />
) : ( diff --git a/src/frontend/src/controllers/API/api.tsx b/src/frontend/src/controllers/API/api.tsx index 363576ae0..560519d94 100644 --- a/src/frontend/src/controllers/API/api.tsx +++ b/src/frontend/src/controllers/API/api.tsx @@ -2,11 +2,11 @@ import axios, { AxiosError, AxiosInstance } from "axios"; import { useContext, useEffect } from "react"; import { Cookies } from "react-cookie"; import { renewAccessToken } from "."; -import { AUTHORIZED_DUPLICATE_REQUESTS } from "../../constants/constants"; import { BuildStatus } from "../../constants/enums"; import { AuthContext } from "../../contexts/authContext"; import useAlertStore from "../../stores/alertStore"; import useFlowStore from "../../stores/flowStore"; +import { checkDuplicateRequestAndStoreRequest } from "./helpers/check-duplicate-requests"; // Create a new Axios instance const api: AxiosInstance = axios.create({ @@ -48,7 +48,7 @@ function ApiInterceptor() { } await clearBuildVerticesState(error); return Promise.reject(error); - }, + } ); const isAuthorizedURL = (url) => { @@ -65,10 +65,10 @@ function ApiInterceptor() { const parsedURL = new URL(url); const isDomainAllowed = authorizedDomains.some( - (domain) => parsedURL.origin === new URL(domain).origin, + (domain) => parsedURL.origin === new URL(domain).origin ); const isEndpointAllowed = authorizedEndpoints.some((endpoint) => - parsedURL.pathname.includes(endpoint), + parsedURL.pathname.includes(endpoint) ); return isDomainAllowed || isEndpointAllowed; @@ -81,28 +81,12 @@ function ApiInterceptor() { // Request interceptor to add access token to every request const requestInterceptor = api.interceptors.request.use( (config) => { - const lastUrl = localStorage.getItem("lastUrlCalled"); - const lastMethodCalled = localStorage.getItem("lastMethodCalled"); + const checkRequest = checkDuplicateRequestAndStoreRequest(config); - const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) => - config?.url!.includes(request), - ); - - if ( - config?.url === lastUrl && - !isContained && - lastMethodCalled === config.method - ) { - return Promise.reject("Duplicate request"); + if (!checkRequest) { + return Promise.reject("Duplicate request."); } - localStorage.setItem("lastUrlCalled", config.url ?? ""); - localStorage.setItem("lastMethodCalled", config.method ?? ""); - localStorage.setItem( - "lastRequestData", - JSON.stringify(config.data) ?? "", - ); - const accessToken = cookies.get("access_token_lf"); if (accessToken && !isAuthorizedURL(config?.url)) { config.headers["Authorization"] = `Bearer ${accessToken}`; @@ -112,7 +96,7 @@ function ApiInterceptor() { }, (error) => { return Promise.reject(error); - }, + } ); return () => { @@ -144,7 +128,7 @@ function ApiInterceptor() { if (error?.config?.headers) { delete error.config.headers["Authorization"]; error.config.headers["Authorization"] = `Bearer ${cookies.get( - "access_token_lf", + "access_token_lf" )}`; const response = await axios.request(error.config); return response; diff --git a/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts new file mode 100644 index 000000000..79a47c7a7 --- /dev/null +++ b/src/frontend/src/controllers/API/helpers/check-duplicate-requests.ts @@ -0,0 +1,30 @@ +import { AUTHORIZED_DUPLICATE_REQUESTS } from "../../../constants/constants"; + +export function checkDuplicateRequestAndStoreRequest(config) { + const lastUrl = localStorage.getItem("lastUrlCalled"); + const lastMethodCalled = localStorage.getItem("lastMethodCalled"); + const lastRequestTime = localStorage.getItem("lastRequestTime"); + + const currentTime = Date.now(); + + const isContained = AUTHORIZED_DUPLICATE_REQUESTS.some((request) => + config?.url!.includes(request), + ); + + if ( + config?.url === lastUrl && + !isContained && + lastMethodCalled === config.method && + lastMethodCalled === "get" && // Assuming you want to check only for GET requests + lastRequestTime && + currentTime - parseInt(lastRequestTime, 10) < 800 + ) { + return false; + } + + localStorage.setItem("lastUrlCalled", config.url ?? ""); + localStorage.setItem("lastMethodCalled", config.method ?? ""); + localStorage.setItem("lastRequestTime", currentTime.toString()); + + return true; +} diff --git a/src/frontend/src/customNodes/genericNode/index.tsx b/src/frontend/src/customNodes/genericNode/index.tsx index 5c83cad03..0881c6207 100644 --- a/src/frontend/src/customNodes/genericNode/index.tsx +++ b/src/frontend/src/customNodes/genericNode/index.tsx @@ -55,14 +55,14 @@ export default function GenericNode({ const [nodeName, setNodeName] = useState(data.node!.display_name); const [inputDescription, setInputDescription] = useState(false); const [nodeDescription, setNodeDescription] = useState( - data.node?.description!, + data.node?.description! ); const [isOutdated, setIsOutdated] = useState(false); const buildStatus = useFlowStore( - (state) => state.flowBuildStatus[data.id]?.status, + (state) => state.flowBuildStatus[data.id]?.status ); const lastRunTime = useFlowStore( - (state) => state.flowBuildStatus[data.id]?.timestamp, + (state) => state.flowBuildStatus[data.id]?.timestamp ); const [validationStatus, setValidationStatus] = useState(null); @@ -77,10 +77,10 @@ export default function GenericNode({ // first check if data.type in NATIVE_CATEGORIES // if not return if (!data.node?.template?.code?.value) return; - const thisNodeTemplate = templates[data.type].template; + const thisNodeTemplate = templates[data.type]?.template; // if the template does not have a code key // return - if (!thisNodeTemplate.code) return; + if (!thisNodeTemplate?.code) return; const currentCode = thisNodeTemplate.code?.value; const thisNodesCode = data.node!.template?.code?.value; const componentsToIgnore = ["Custom Component"]; @@ -115,7 +115,7 @@ export default function GenericNode({ updateNodeInternals(data.id); }, - [data.id, data.node, setNode, setIsOutdated], + [data.id, data.node, setNode, setIsOutdated] ); if (!data.node!.template) { @@ -255,7 +255,7 @@ export default function GenericNode({ const isDark = useDarkStore((state) => state.dark); const renderIconStatus = ( buildStatus: BuildStatus | undefined, - validationStatus: validationStatusType | null, + validationStatus: validationStatusType | null ) => { if (buildStatus === BuildStatus.BUILDING) { return ; @@ -296,7 +296,7 @@ export default function GenericNode({ }; const getSpecificClassFromBuildStatus = ( buildStatus: BuildStatus | undefined, - validationStatus: validationStatusType | null, + validationStatus: validationStatusType | null ) => { let isInvalid = validationStatus && !validationStatus.valid; @@ -320,11 +320,11 @@ export default function GenericNode({ selected: boolean, showNode: boolean, buildStatus: BuildStatus | undefined, - validationStatus: validationStatusType | null, + validationStatus: validationStatusType | null ) => { const specificClassFromBuildStatus = getSpecificClassFromBuildStatus( buildStatus, - validationStatus, + validationStatus ); const baseBorderClass = getBaseBorderClass(selected); @@ -333,7 +333,7 @@ export default function GenericNode({ baseBorderClass, nodeSizeClass, "generic-node-div", - specificClassFromBuildStatus, + specificClassFromBuildStatus ); return names; }; @@ -393,7 +393,7 @@ export default function GenericNode({ selected, showNode, buildStatus, - validationStatus, + validationStatus )} > {data.node?.beta && showNode && ( @@ -416,6 +416,7 @@ export default function GenericNode({ "generic-node-title-arrangement rounded-full" + (!showNode && " justify-center ") } + data-testid="generic-node-title-arrangement" > {iconNodeRender()} {showNode && ( @@ -523,7 +524,7 @@ export default function GenericNode({ } title={getFieldTitle( data.node?.template!, - templateField, + templateField )} info={data.node?.template[templateField].info} name={templateField} @@ -551,7 +552,7 @@ export default function GenericNode({ proxy={data.node?.template[templateField].proxy} showNode={showNode} /> - ), + ) )} { setInputDescription(true); @@ -770,13 +771,13 @@ export default function GenericNode({ } title={getFieldTitle( data.node?.template!, - templateField, + templateField )} info={data.node?.template[templateField].info} name={templateField} tooltipTitle={ data.node?.template[templateField].input_types?.join( - "\n", + "\n" ) ?? data.node?.template[templateField].type } required={data.node!.template[templateField].required} @@ -803,7 +804,7 @@ export default function GenericNode({
{" "} diff --git a/src/frontend/src/customNodes/hooks/use-fetch-data-on-mount.tsx b/src/frontend/src/customNodes/hooks/use-fetch-data-on-mount.tsx index 3fc3fbe72..7426164f7 100644 --- a/src/frontend/src/customNodes/hooks/use-fetch-data-on-mount.tsx +++ b/src/frontend/src/customNodes/hooks/use-fetch-data-on-mount.tsx @@ -9,7 +9,7 @@ const useFetchDataOnMount = ( handleUpdateValues, setNode, renderTooltips, - setIsLoading, + setIsLoading ) => { const setErrorData = useAlertStore((state) => state.setErrorData); @@ -40,7 +40,7 @@ const useFetchDataOnMount = ( setErrorData({ title: "Error while updating the Component", - list: [responseError.response.data.detail ?? "Unknown error"], + list: [responseError?.response?.data?.detail ?? "Unknown error"], }); } setIsLoading(false); diff --git a/src/frontend/src/customNodes/hooks/use-handle-new-value.tsx b/src/frontend/src/customNodes/hooks/use-handle-new-value.tsx index 03d305ddb..dc416646b 100644 --- a/src/frontend/src/customNodes/hooks/use-handle-new-value.tsx +++ b/src/frontend/src/customNodes/hooks/use-handle-new-value.tsx @@ -10,7 +10,7 @@ const useHandleOnNewValue = ( debouncedHandleUpdateValues, setNode, renderTooltips, - setIsLoading, + setIsLoading ) => { const setErrorData = useAlertStore((state) => state.setErrorData); @@ -44,7 +44,9 @@ const useHandleOnNewValue = ( let responseError = error as ResponseErrorTypeAPI; setErrorData({ title: "Error while updating the Component", - list: [responseError.response.data.detail.error ?? "Unknown error"], + list: [ + responseError?.response?.data?.detail.error ?? "Unknown error", + ], }); } setIsLoading(false); diff --git a/src/frontend/src/customNodes/hooks/use-handle-refresh-buttons.tsx b/src/frontend/src/customNodes/hooks/use-handle-refresh-buttons.tsx index 19f2a3c29..4696aa994 100644 --- a/src/frontend/src/customNodes/hooks/use-handle-refresh-buttons.tsx +++ b/src/frontend/src/customNodes/hooks/use-handle-refresh-buttons.tsx @@ -26,7 +26,7 @@ const useHandleRefreshButtonPress = (setIsLoading, setNode, renderTooltips) => { setErrorData({ title: "Error while updating the Component", - list: [responseError.response.data.detail ?? "Unknown error"], + list: [responseError?.response?.data?.detail ?? "Unknown error"], }); } setIsLoading(false); diff --git a/src/frontend/src/modals/baseModal/index.tsx b/src/frontend/src/modals/baseModal/index.tsx index a899bd071..a9efe3ddf 100644 --- a/src/frontend/src/modals/baseModal/index.tsx +++ b/src/frontend/src/modals/baseModal/index.tsx @@ -71,6 +71,7 @@ const Footer: React.FC<{ icon?: ReactNode; loading?: boolean; disabled?: boolean; + dataTestId?: string; }; }> = ({ children, submit }) => { return submit ? ( @@ -83,6 +84,7 @@ const Footer: React.FC<{ @@ -474,7 +474,7 @@ export default function NodeToolbarComponent({
state.setCurrentFlowId, + (state) => state.setCurrentFlowId ); const { scrollId } = useParams(); @@ -59,7 +59,7 @@ export default function GeneralPage() { }, [scrollId]); const [inputState, setInputState] = useState( - CONTROL_PATCH_USER_STATE, + CONTROL_PATCH_USER_STATE ); const { autoLogin } = useContext(AuthContext); @@ -143,7 +143,7 @@ export default function GeneralPage() { setHasApiKey(false); setValidApiKey(false); setLoadingApiKey(false); - }, + } ); } }; @@ -290,8 +290,8 @@ export default function GeneralPage() { {(hasApiKey && !validApiKey ? INVALID_API_KEY : !hasApiKey - ? NO_API_KEY - : "") + INSERT_API_KEY} + ? NO_API_KEY + : "") + INSERT_API_KEY} @@ -319,7 +319,11 @@ export default function GeneralPage() { - diff --git a/src/frontend/tests/end-to-end/chatInputOutput.spec.ts b/src/frontend/tests/end-to-end/chatInputOutput.spec.ts index 0828e66e6..a1429ddec 100644 --- a/src/frontend/tests/end-to-end/chatInputOutput.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutput.spec.ts @@ -23,7 +23,7 @@ test("chat_io_teste", async ({ page }) => { } const jsonContent = readFileSync( - "tests/end-to-end/assets/ChatTest.json", + "src/frontend/tests/end-to-end/assets/ChatTest.json", "utf-8" ); diff --git a/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts b/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts index 7c442116e..4c6b90081 100644 --- a/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts +++ b/src/frontend/tests/end-to-end/chatInputOutputUser.spec.ts @@ -59,6 +59,9 @@ test("user must interact with chat with Input/Output", async ({ page }) => { .fill( "testtesttesttesttesttestte;.;.,;,.;,.;.,;,..,;;;;;;;;;;;;;;;;;;;;;,;.;,.;,.,;.,;.;.,~~çççççççççççççççççççççççççççççççççççççççisdajfdasiopjfaodisjhvoicxjiovjcxizopjviopasjioasfhjaiohf23432432432423423sttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttesttestççççççççççççççççççççççççççççççççç,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,.,!" ); + await page.getByTestId("icon-LucideSend").click(); + await page.getByText("Close", { exact: true }).click(); + await page .getByTestId("popover-anchor-input-sender_name") .nth(1) @@ -68,7 +71,7 @@ test("user must interact with chat with Input/Output", async ({ page }) => { .nth(0) .fill("TestSenderNameAI"); - await page.getByText("Playground", { exact: true }).click(); + await page.getByText("Playground", { exact: true }).last().click(); await page.getByTestId("icon-LucideSend").click(); valueUser = await page diff --git a/src/frontend/tests/end-to-end/dragAndDrop.spec.ts b/src/frontend/tests/end-to-end/dragAndDrop.spec.ts index ee1789d2d..3e760901d 100644 --- a/src/frontend/tests/end-to-end/dragAndDrop.spec.ts +++ b/src/frontend/tests/end-to-end/dragAndDrop.spec.ts @@ -26,8 +26,8 @@ test.describe("drag and drop test", () => { await page.locator("span").filter({ hasText: "My Collection" }).isVisible(); // Read your file into a buffer. const jsonContent = readFileSync( - "tests/end-to-end/assets/collection.json", - "utf-8", + "src/frontend/tests/end-to-end/assets/collection.json", + "utf-8" ); // Create the DataTransfer and File @@ -47,7 +47,7 @@ test.describe("drag and drop test", () => { "drop", { dataTransfer, - }, + } ); const genericNoda = page.getByTestId("div-generic-node"); diff --git a/src/frontend/tests/end-to-end/dropdownComponent.spec.ts b/src/frontend/tests/end-to-end/dropdownComponent.spec.ts index 186226a54..70c1b4a64 100644 --- a/src/frontend/tests/end-to-end/dropdownComponent.spec.ts +++ b/src/frontend/tests/end-to-end/dropdownComponent.spec.ts @@ -78,32 +78,32 @@ test("dropDownComponent", async ({ page }) => { await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showendpoint_url"]').click(); expect( - await page.locator('//*[@id="showendpoint_url"]').isChecked(), + await page.locator('//*[@id="showendpoint_url"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showendpoint_url"]').click(); expect( - await page.locator('//*[@id="showendpoint_url"]').isChecked(), + await page.locator('//*[@id="showendpoint_url"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showregion_name"]').click(); expect( - await page.locator('//*[@id="showregion_name"]').isChecked(), + await page.locator('//*[@id="showregion_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showregion_name"]').click(); expect( - await page.locator('//*[@id="showregion_name"]').isChecked(), + await page.locator('//*[@id="showregion_name"]').isChecked() ).toBeTruthy(); // showmodel_id @@ -113,7 +113,7 @@ test("dropDownComponent", async ({ page }) => { // showmodel_id await page.locator('//*[@id="showmodel_id"]').click(); expect( - await page.locator('//*[@id="showmodel_id"]').isChecked(), + await page.locator('//*[@id="showmodel_id"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showcache"]').click(); @@ -124,32 +124,32 @@ test("dropDownComponent", async ({ page }) => { await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showendpoint_url"]').click(); expect( - await page.locator('//*[@id="showendpoint_url"]').isChecked(), + await page.locator('//*[@id="showendpoint_url"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showendpoint_url"]').click(); expect( - await page.locator('//*[@id="showendpoint_url"]').isChecked(), + await page.locator('//*[@id="showendpoint_url"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showregion_name"]').click(); expect( - await page.locator('//*[@id="showregion_name"]').isChecked(), + await page.locator('//*[@id="showregion_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showregion_name"]').click(); expect( - await page.locator('//*[@id="showregion_name"]').isChecked(), + await page.locator('//*[@id="showregion_name"]').isChecked() ).toBeTruthy(); // showmodel_id @@ -159,7 +159,7 @@ test("dropDownComponent", async ({ page }) => { // showmodel_id await page.locator('//*[@id="showmodel_id"]').click(); expect( - await page.locator('//*[@id="showmodel_id"]').isChecked(), + await page.locator('//*[@id="showmodel_id"]').isChecked() ).toBeTruthy(); await page.getByTestId("dropdown-edit-model_id").click(); @@ -170,7 +170,7 @@ test("dropDownComponent", async ({ page }) => { expect(false).toBeTruthy(); } - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); value = await page.getByTestId("dropdown-model_id").innerText(); if (value !== "ai21.j2-mid-v1") { diff --git a/src/frontend/tests/end-to-end/filterEdge.spec.ts b/src/frontend/tests/end-to-end/filterEdge.spec.ts index 7ba874f43..e2f7579c5 100644 --- a/src/frontend/tests/end-to-end/filterEdge.spec.ts +++ b/src/frontend/tests/end-to-end/filterEdge.spec.ts @@ -40,7 +40,7 @@ test("LLMChain - Tooltip", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[3]/div/button/div/div', + '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[3]/div/button/div/div' ) .hover() .then(async () => { @@ -60,17 +60,17 @@ test("LLMChain - Tooltip", async ({ page }) => { await page.getByTitle("zoom out").click(); await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[4]/div/button/div/div', + '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[4]/div/button/div/div' ) .hover() .then(async () => { await expect( - page.getByTestId("tooltip-Model Specs").first(), + page.getByTestId("tooltip-Model Specs").first() ).toBeVisible(); await page.waitForTimeout(2000); await expect( - page.getByTestId("tooltip-Model Specs").first(), + page.getByTestId("tooltip-Model Specs").first() ).toBeVisible(); await page.getByTestId("icon-Search").click(); @@ -81,12 +81,12 @@ test("LLMChain - Tooltip", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[5]/div/button/div/div', + '//*[@id="react-flow-id"]/div[1]/div[1]/div/div/div[2]/div/div/div[2]/div[5]/div/button/div/div' ) .hover() .then(async () => { await expect( - page.getByTestId("empty-tooltip-filter").first(), + page.getByTestId("empty-tooltip-filter").first() ).toBeVisible(); }); }); @@ -113,7 +113,7 @@ test("LLMChain - Filter", async ({ page }) => { await page.waitForTimeout(1000); await page.getByTestId( - "input-list-plus-btn-edit_metadata_indexing_include-2", + "input-list-plus-btn-edit_metadata_indexing_include-2" ); await page.getByTestId("blank-flow").click(); @@ -136,7 +136,7 @@ test("LLMChain - Filter", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[4]/div/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[4]/div/button/div/div' ) .click(); @@ -148,16 +148,15 @@ test("LLMChain - Filter", async ({ page }) => { await expect(page.getByTestId("model_specsChatOllama")).toBeVisible(); await expect(page.getByTestId("model_specsChatOpenAI")).toBeVisible(); await expect(page.getByTestId("model_specsChatVertexAI")).toBeVisible(); - await expect(page.getByTestId("model_specsCohere")).toBeVisible(); await expect( - page.getByTestId("model_specsGoogle Generative AI"), + page.getByTestId("model_specsGoogle Generative AI") ).toBeVisible(); await expect( - page.getByTestId("model_specsHugging Face Inference API"), + page.getByTestId("model_specsHugging Face Inference API") ).toBeVisible(); await expect(page.getByTestId("model_specsOllama")).toBeVisible(); await expect( - page.getByTestId("model_specsQianfanChatEndpoint"), + page.getByTestId("model_specsQianfanChatEndpoint") ).toBeVisible(); await expect(page.getByTestId("model_specsQianfanLLMEndpoint")).toBeVisible(); await expect(page.getByTestId("model_specsVertexAI")).toBeVisible(); @@ -169,24 +168,23 @@ test("LLMChain - Filter", async ({ page }) => { await expect(page.getByTestId("model_specsAmazon Bedrock")).not.toBeVisible(); await expect(page.getByTestId("modelsAzure OpenAI")).not.toBeVisible(); await expect( - page.getByTestId("model_specsAzureChatOpenAI"), + page.getByTestId("model_specsAzureChatOpenAI") ).not.toBeVisible(); await expect(page.getByTestId("model_specsChatAnthropic")).not.toBeVisible(); await expect(page.getByTestId("model_specsChatLiteLLM")).not.toBeVisible(); await expect(page.getByTestId("model_specsChatOllama")).not.toBeVisible(); await expect(page.getByTestId("model_specsChatOpenAI")).not.toBeVisible(); await expect(page.getByTestId("model_specsChatVertexAI")).not.toBeVisible(); - await expect(page.getByTestId("model_specsCohere")).not.toBeVisible(); await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div' ) .click(); await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div/div/div[2]/div[7]/button/div/div' ) .click(); diff --git a/src/frontend/tests/end-to-end/floatComponent.spec.ts b/src/frontend/tests/end-to-end/floatComponent.spec.ts index 70f7bae7e..4989a0f5f 100644 --- a/src/frontend/tests/end-to-end/floatComponent.spec.ts +++ b/src/frontend/tests/end-to-end/floatComponent.spec.ts @@ -60,44 +60,38 @@ test("FloatComponent", async ({ page }) => { await page.getByTestId("more-options-modal").click(); await page.getByTestId("edit-button-modal").click(); - await page.locator('//*[@id="showcache"]').click(); - expect(await page.locator('//*[@id="showcache"]').isChecked()).toBeTruthy(); - - await page.locator('//*[@id="showcache"]').click(); - expect(await page.locator('//*[@id="showcache"]').isChecked()).toBeFalsy(); - await page.getByTestId("showformat").click(); expect(await page.locator('//*[@id="showformat"]').isChecked()).toBeTruthy(); await page.getByTestId("showformat").click(); expect(await page.locator('//*[@id="showformat"]').isChecked()).toBeFalsy(); - await page.getByTestId("showmirostat").click(); - expect( - await page.locator('//*[@id="showmirostat"]').isChecked(), - ).toBeTruthy(); - await page.getByTestId("showmirostat").click(); expect(await page.locator('//*[@id="showmirostat"]').isChecked()).toBeFalsy(); - await page.getByTestId("showmirostat_eta").click(); + await page.getByTestId("showmirostat").click(); expect( - await page.locator('//*[@id="showmirostat_eta"]').isChecked(), + await page.locator('//*[@id="showmirostat"]').isChecked() ).toBeTruthy(); await page.getByTestId("showmirostat_eta").click(); expect( - await page.locator('//*[@id="showmirostat_eta"]').isChecked(), + await page.locator('//*[@id="showmirostat_eta"]').isChecked() + ).toBeTruthy(); + + await page.getByTestId("showmirostat_eta").click(); + expect( + await page.locator('//*[@id="showmirostat_eta"]').isChecked() ).toBeFalsy(); await page.getByTestId("showmirostat_tau").click(); expect( - await page.locator('//*[@id="showmirostat_tau"]').isChecked(), + await page.locator('//*[@id="showmirostat_tau"]').isChecked() ).toBeTruthy(); await page.getByTestId("showmirostat_tau").click(); expect( - await page.locator('//*[@id="showmirostat_tau"]').isChecked(), + await page.locator('//*[@id="showmirostat_tau"]').isChecked() ).toBeFalsy(); await page.getByTestId("showmodel").click(); @@ -120,25 +114,25 @@ test("FloatComponent", async ({ page }) => { await page.getByTestId("shownum_thread").click(); expect( - await page.locator('//*[@id="shownum_thread"]').isChecked(), + await page.locator('//*[@id="shownum_thread"]').isChecked() ).toBeTruthy(); await page.getByTestId("shownum_thread").click(); expect( - await page.locator('//*[@id="shownum_thread"]').isChecked(), + await page.locator('//*[@id="shownum_thread"]').isChecked() ).toBeFalsy(); await page.getByTestId("showrepeat_last_n").click(); expect( - await page.locator('//*[@id="showrepeat_last_n"]').isChecked(), + await page.locator('//*[@id="showrepeat_last_n"]').isChecked() ).toBeTruthy(); await page.getByTestId("showrepeat_last_n").click(); expect( - await page.locator('//*[@id="showrepeat_last_n"]').isChecked(), + await page.locator('//*[@id="showrepeat_last_n"]').isChecked() ).toBeFalsy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); const plusButtonLocator = page.locator('//*[@id="float-input"]'); const elementCount = await plusButtonLocator?.count(); @@ -151,10 +145,10 @@ test("FloatComponent", async ({ page }) => { // showtemperature await page.locator('//*[@id="showtemperature"]').click(); expect( - await page.locator('//*[@id="showtemperature"]').isChecked(), + await page.locator('//*[@id="showtemperature"]').isChecked() ).toBeTruthy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); await page.locator('//*[@id="float-input"]').click(); await page.locator('//*[@id="float-input"]').fill("3"); diff --git a/src/frontend/tests/end-to-end/flowSettings.spec.ts b/src/frontend/tests/end-to-end/flowSettings.spec.ts index 607c64677..e0a3b8f27 100644 --- a/src/frontend/tests/end-to-end/flowSettings.spec.ts +++ b/src/frontend/tests/end-to-end/flowSettings.spec.ts @@ -29,7 +29,7 @@ test("flowSettings", async ({ page }) => { await page .getByPlaceholder("Flow name") .fill( - "Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test", + "Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test" ); await page.getByText("Character limit reached").isVisible(); @@ -41,10 +41,11 @@ test("flowSettings", async ({ page }) => { await page .getByPlaceholder("Flow description") .fill( - "Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test", + "Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test Flow Name Test" ); - await page.getByText("Save").last().click(); + await page.getByTestId("save-flow-settings").click(); + await page.getByTestId("save-flow-settings").click(); await page.waitForTimeout(1000); diff --git a/src/frontend/tests/end-to-end/folders.spec.ts b/src/frontend/tests/end-to-end/folders.spec.ts index 3b82a5f75..384816b3f 100644 --- a/src/frontend/tests/end-to-end/folders.spec.ts +++ b/src/frontend/tests/end-to-end/folders.spec.ts @@ -31,33 +31,16 @@ test("CRUD folders", async ({ page }) => { await page.getByText("All").first().isVisible(); await page.getByText("Select All").isVisible(); - await page.getByText("New Folder", { exact: true }).last().click(); - await page.getByPlaceholder("Insert a name for the folder").fill("test"); - await page - .getByPlaceholder("Insert a description for the folder") - .fill("test"); - await page.getByText("Save Folder").click(); - + await page.getByTestId("add-folder-button").click(); + await page.getByText("New Folder").last().isVisible(); await page.waitForTimeout(1000); - await page.getByText("Folder created succefully").isVisible(); - await page.getByText("test").last().isVisible(); - await page - .getByText("test") - .last() - .hover() - .then(async () => { - await page.getByTestId("icon-pencil").last().click(); - }); - - await page.getByPlaceholder("Insert a name for the folder").fill("test edit"); - await page - .getByPlaceholder("Insert a description for the folder") - .fill("test edit"); - await page.getByText("Edit Folder").last().click(); - await page.getByText("test edit").last().isVisible(); + await page.getByText("New Folder").last().dblclick(); + await page.getByTestId("input-folder").fill("new folder test name"); + await page.keyboard.press("Enter"); + await page.getByText("new folder test name").last().isVisible(); await page - .getByText("test edit") + .getByText("new folder test name") .last() .hover() .then(async () => { @@ -74,8 +57,8 @@ test("add folder by drag and drop", async ({ page }) => { await page.waitForTimeout(2000); const jsonContent = readFileSync( - "tests/end-to-end/assets/collection.json", - "utf-8", + "src/frontend/tests/end-to-end/assets/collection.json", + "utf-8" ); // Create the DataTransfer and File @@ -95,7 +78,7 @@ test("add folder by drag and drop", async ({ page }) => { "drop", { dataTransfer, - }, + } ); await page.getByText("Getting Started").first().isVisible(); @@ -131,17 +114,13 @@ test("change flow folder", async ({ page }) => { await page.getByText("All").first().isVisible(); await page.getByText("Select All").isVisible(); - await page.getByText("New Folder", { exact: true }).last().click(); - await page.getByPlaceholder("Insert a name for the folder").fill("test"); - await page - .getByPlaceholder("Insert a description for the folder") - .fill("test"); - - await page.getByText("Save Folder").click(); - + await page.getByTestId("add-folder-button").click(); + await page.getByText("New Folder").last().isVisible(); await page.waitForTimeout(1000); - await page.getByText("Folder created succefully").isVisible(); - await page.getByText("test").last().isVisible(); + await page.getByText("New Folder").last().dblclick(); + await page.getByTestId("input-folder").fill("new folder test name"); + await page.keyboard.press("Enter"); + await page.getByText("new folder test name").last().isVisible(); await page.getByText("My Projects").last().click(); await page.getByText("Basic Prompting").first().hover(); diff --git a/src/frontend/tests/end-to-end/globalVariables.spec.ts b/src/frontend/tests/end-to-end/globalVariables.spec.ts index 920ba743b..546c332f6 100644 --- a/src/frontend/tests/end-to-end/globalVariables.spec.ts +++ b/src/frontend/tests/end-to-end/globalVariables.spec.ts @@ -69,7 +69,6 @@ test("GlobalVariables", async ({ page }) => { await page.getByText("Save Variable", { exact: true }).click(); expect(page.getByText(credentialName, { exact: true })).not.toBeNull(); await page.getByText(credentialName, { exact: true }).isVisible(); - await page.getByText("Save Variable", { exact: true }).click(); await page.waitForTimeout(2000); await page diff --git a/src/frontend/tests/end-to-end/inputComponent.spec.ts b/src/frontend/tests/end-to-end/inputComponent.spec.ts index 2fcbc9831..5a84c9f67 100644 --- a/src/frontend/tests/end-to-end/inputComponent.spec.ts +++ b/src/frontend/tests/end-to-end/inputComponent.spec.ts @@ -60,69 +60,69 @@ test("InputComponent", async ({ page }) => { expect( await page .locator('//*[@id="showchroma_server_cors_allow_origins"]') - .isChecked(), + .isChecked() ).toBeTruthy(); await page.locator('//*[@id="showchroma_server_grpc_port"]').click(); expect( - await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked(), + await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showchroma_server_host"]').click(); expect( - await page.locator('//*[@id="showchroma_server_host"]').isChecked(), + await page.locator('//*[@id="showchroma_server_host"]').isChecked() ).toBeTruthy(); - await page.locator('//*[@id="showchroma_server_port"]').click(); + await page.locator('//*[@id="showchroma_server_http_port"]').click(); expect( - await page.locator('//*[@id="showchroma_server_port"]').isChecked(), + await page.locator('//*[@id="showchroma_server_http_port"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click(); expect( - await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked(), + await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showcollection_name"]').click(); expect( - await page.locator('//*[@id="showcollection_name"]').isChecked(), + await page.locator('//*[@id="showcollection_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showindex_directory"]').click(); expect( - await page.locator('//*[@id="showindex_directory"]').isChecked(), + await page.locator('//*[@id="showindex_directory"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showchroma_server_cors_allow_origins"]').click(); expect( await page .locator('//*[@id="showchroma_server_cors_allow_origins"]') - .isChecked(), + .isChecked() ).toBeFalsy(); await page.locator('//*[@id="showchroma_server_grpc_port"]').click(); expect( - await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked(), + await page.locator('//*[@id="showchroma_server_grpc_port"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showchroma_server_host"]').click(); expect( - await page.locator('//*[@id="showchroma_server_host"]').isChecked(), + await page.locator('//*[@id="showchroma_server_host"]').isChecked() ).toBeFalsy(); - await page.locator('//*[@id="showchroma_server_port"]').click(); + await page.locator('//*[@id="showchroma_server_http_port"]').click(); expect( - await page.locator('//*[@id="showchroma_server_port"]').isChecked(), + await page.locator('//*[@id="showchroma_server_http_port"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showchroma_server_ssl_enabled"]').click(); expect( - await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked(), + await page.locator('//*[@id="showchroma_server_ssl_enabled"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showindex_directory"]').click(); expect( - await page.locator('//*[@id="showindex_directory"]').isChecked(), + await page.locator('//*[@id="showindex_directory"]').isChecked() ).toBeTruthy(); let valueEditNode = await page @@ -138,7 +138,7 @@ test("InputComponent", async ({ page }) => { .getByTestId("popover-anchor-input-collection_name-edit") .fill("NEW_collection_name_test_123123123!@#$&*(&%$@ÇÇÇÀõe"); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); const plusButtonLocator = page.getByTestId("input-collection_name"); const elementCount = await plusButtonLocator?.count(); @@ -152,10 +152,10 @@ test("InputComponent", async ({ page }) => { await page.locator('//*[@id="showcollection_name"]').click(); expect( - await page.locator('//*[@id="showcollection_name"]').isChecked(), + await page.locator('//*[@id="showcollection_name"]').isChecked() ).toBeTruthy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); let value = await page .getByTestId("popover-anchor-input-collection_name") diff --git a/src/frontend/tests/end-to-end/inputListComponent.spec.ts b/src/frontend/tests/end-to-end/inputListComponent.spec.ts index a434a4529..5b6dcdea8 100644 --- a/src/frontend/tests/end-to-end/inputListComponent.spec.ts +++ b/src/frontend/tests/end-to-end/inputListComponent.spec.ts @@ -68,7 +68,7 @@ test("InputListComponent", async ({ page }) => { .getByTestId("input-list-input-edit_metadata_indexing_include-1") .fill("test1 test1 test1 test1"); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); await page .getByTestId("input-list-input_metadata_indexing_include-0") diff --git a/src/frontend/tests/end-to-end/intComponent.spec.ts b/src/frontend/tests/end-to-end/intComponent.spec.ts index e2a539f01..a9af11987 100644 --- a/src/frontend/tests/end-to-end/intComponent.spec.ts +++ b/src/frontend/tests/end-to-end/intComponent.spec.ts @@ -39,6 +39,12 @@ test("IntComponent", async ({ page }) => { await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); + + await page.getByTestId("more-options-modal").click(); + await page.getByTestId("edit-button-modal").click(); + await page.getByTestId("showmax_tokens").click(); + + await page.getByText("Save Changes", { exact: true }).click(); await page.getByTestId("int-input-max_tokens").click(); await page .getByTestId("int-input-max_tokens") @@ -81,80 +87,80 @@ test("IntComponent", async ({ page }) => { await page.locator('//*[@id="showmodel_kwargs"]').click(); expect( - await page.locator('//*[@id="showmodel_kwargs"]').isChecked(), + await page.locator('//*[@id="showmodel_kwargs"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showmodel_name"]').click(); expect( - await page.locator('//*[@id="showmodel_name"]').isChecked(), + await page.locator('//*[@id="showmodel_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showopenai_api_base"]').click(); expect( - await page.locator('//*[@id="showopenai_api_base"]').isChecked(), + await page.locator('//*[@id="showopenai_api_base"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showopenai_api_key"]').click(); expect( - await page.locator('//*[@id="showopenai_api_key"]').isChecked(), + await page.locator('//*[@id="showopenai_api_key"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showtemperature"]').click(); expect( - await page.locator('//*[@id="showtemperature"]').isChecked(), + await page.locator('//*[@id="showtemperature"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showmodel_kwargs"]').click(); expect( - await page.locator('//*[@id="showmodel_kwargs"]').isChecked(), + await page.locator('//*[@id="showmodel_kwargs"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showmodel_name"]').click(); expect( - await page.locator('//*[@id="showmodel_name"]').isChecked(), + await page.locator('//*[@id="showmodel_name"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showopenai_api_base"]').click(); expect( - await page.locator('//*[@id="showopenai_api_base"]').isChecked(), + await page.locator('//*[@id="showopenai_api_base"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showopenai_api_key"]').click(); expect( - await page.locator('//*[@id="showopenai_api_key"]').isChecked(), + await page.locator('//*[@id="showopenai_api_key"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showtemperature"]').click(); expect( - await page.locator('//*[@id="showtemperature"]').isChecked(), + await page.locator('//*[@id="showtemperature"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showmodel_kwargs"]').click(); expect( - await page.locator('//*[@id="showmodel_kwargs"]').isChecked(), + await page.locator('//*[@id="showmodel_kwargs"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showmodel_name"]').click(); expect( - await page.locator('//*[@id="showmodel_name"]').isChecked(), + await page.locator('//*[@id="showmodel_name"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showopenai_api_base"]').click(); expect( - await page.locator('//*[@id="showopenai_api_base"]').isChecked(), + await page.locator('//*[@id="showopenai_api_base"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showopenai_api_key"]').click(); expect( - await page.locator('//*[@id="showopenai_api_key"]').isChecked(), + await page.locator('//*[@id="showopenai_api_key"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showtemperature"]').click(); expect( - await page.locator('//*[@id="showtemperature"]').isChecked(), + await page.locator('//*[@id="showtemperature"]').isChecked() ).toBeFalsy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); const plusButtonLocator = page.getByTestId("int-input-max_tokens"); const elementCount = await plusButtonLocator?.count(); @@ -166,7 +172,7 @@ test("IntComponent", async ({ page }) => { await page.locator('//*[@id="showtimeout"]').click(); expect( - await page.locator('//*[@id="showtimeout"]').isChecked(), + await page.locator('//*[@id="showtimeout"]').isChecked() ).toBeTruthy(); const valueEditNode = await page @@ -177,7 +183,7 @@ test("IntComponent", async ({ page }) => { expect(false).toBeTruthy(); } - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); await page.getByTestId("int-input-max_tokens").click(); await page.getByTestId("int-input-max_tokens").fill("3"); diff --git a/src/frontend/tests/end-to-end/keyPairListComponent.spec.ts b/src/frontend/tests/end-to-end/keyPairListComponent.spec.ts index 42e6ccd4f..e5763bd2f 100644 --- a/src/frontend/tests/end-to-end/keyPairListComponent.spec.ts +++ b/src/frontend/tests/end-to-end/keyPairListComponent.spec.ts @@ -81,9 +81,9 @@ test("KeypairListComponent", async ({ page }) => { expect(await page.locator('//*[@id="showcache"]').isChecked()).toBeFalsy(); await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeFalsy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); const plusButtonLocator = page.locator('//*[@id="plusbtn0"]'); const elementCount = await plusButtonLocator?.count(); @@ -96,7 +96,7 @@ test("KeypairListComponent", async ({ page }) => { await page.locator('//*[@id="showcredentials_profile_name"]').click(); expect( - await page.locator('//*[@id="showcredentials_profile_name"]').isChecked(), + await page.locator('//*[@id="showcredentials_profile_name"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showcache"]').click(); expect(await page.locator('//*[@id="showcache"]').isChecked()).toBeTruthy(); @@ -108,7 +108,7 @@ test("KeypairListComponent", async ({ page }) => { const elementKeyCount = await keyPairVerification?.count(); if (elementKeyCount === 1) { - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); await page.getByTestId("div-generic-node").click(); diff --git a/src/frontend/tests/end-to-end/langflowShortcuts.spec.ts b/src/frontend/tests/end-to-end/langflowShortcuts.spec.ts index 9d9688410..77d35cb03 100644 --- a/src/frontend/tests/end-to-end/langflowShortcuts.spec.ts +++ b/src/frontend/tests/end-to-end/langflowShortcuts.spec.ts @@ -47,11 +47,11 @@ test("LangflowShortcuts", async ({ page }) => { await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); - await page.getByTestId("title-Ollama").click(); + await page.getByTestId("generic-node-title-arrangement").click(); await page.keyboard.press(`${control}+Shift+A`); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); - await page.getByTestId("title-Ollama").click(); + await page.getByTestId("generic-node-title-arrangement").click(); await page.keyboard.press(`${control}+d`); let numberOfNodes = await page.getByTestId("title-Ollama")?.count(); @@ -61,7 +61,7 @@ test("LangflowShortcuts", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div[2]/div/div[1]/div/div[1]/div/div/div[1]', + '//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div[2]/div/div[1]/div/div[1]/div/div/div[1]' ) .click(); await page.keyboard.press("Backspace"); @@ -71,7 +71,7 @@ test("LangflowShortcuts", async ({ page }) => { expect(false).toBeTruthy(); } - await page.getByTestId("title-Ollama").click(); + await page.getByTestId("generic-node-title-arrangement").click(); await page.keyboard.press(`${control}+c`); await page.getByTestId("title-Ollama").click(); @@ -84,7 +84,7 @@ test("LangflowShortcuts", async ({ page }) => { await page .locator( - '//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div[2]/div/div[1]/div/div[1]/div/div/div[1]', + '//*[@id="react-flow-id"]/div[1]/div[1]/div[1]/div/div[2]/div[2]/div/div[1]/div/div[1]/div/div/div[1]' ) .click(); await page.keyboard.press("Backspace"); diff --git a/src/frontend/tests/end-to-end/logs.spec.ts b/src/frontend/tests/end-to-end/logs.spec.ts index e430529ac..ba340d436 100644 --- a/src/frontend/tests/end-to-end/logs.spec.ts +++ b/src/frontend/tests/end-to-end/logs.spec.ts @@ -30,6 +30,8 @@ test("should able to see and interact with logs", async ({ page }) => { await page.getByText("No Data Available", { exact: true }).isVisible(); await page.keyboard.press("Escape"); + await page.getByText("Close").last().click(); + await page .getByTestId("popover-anchor-input-openai_api_key") .fill(process.env.OPENAI_API_KEY ?? ""); diff --git a/src/frontend/tests/end-to-end/nestedComponent.spec.ts b/src/frontend/tests/end-to-end/nestedComponent.spec.ts index 60e1a9fb9..a6906132f 100644 --- a/src/frontend/tests/end-to-end/nestedComponent.spec.ts +++ b/src/frontend/tests/end-to-end/nestedComponent.spec.ts @@ -41,7 +41,7 @@ test("NestedComponent", async ({ page }) => { await page.locator('//*[@id="showpool_threads"]').click(); expect( - await page.locator('//*[@id="showpool_threads"]').isChecked(), + await page.locator('//*[@id="showpool_threads"]').isChecked() ).toBeTruthy(); //showtext_key @@ -53,141 +53,141 @@ test("NestedComponent", async ({ page }) => { await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeFalsy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeFalsy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeFalsy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeTruthy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeTruthy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeTruthy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeFalsy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeFalsy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeFalsy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeTruthy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeTruthy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeTruthy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeFalsy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeFalsy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeFalsy(); // showindex_name await page.locator('//*[@id="showindex_name"]').click(); expect( - await page.locator('//*[@id="showindex_name"]').isChecked(), + await page.locator('//*[@id="showindex_name"]').isChecked() ).toBeTruthy(); // shownamespace await page.locator('//*[@id="shownamespace"]').click(); expect( - await page.locator('//*[@id="shownamespace"]').isChecked(), + await page.locator('//*[@id="shownamespace"]').isChecked() ).toBeTruthy(); // showpinecone_api_key await page.locator('//*[@id="showpinecone_api_key"]').click(); expect( - await page.locator('//*[@id="showpinecone_api_key"]').isChecked(), + await page.locator('//*[@id="showpinecone_api_key"]').isChecked() ).toBeTruthy(); //showpool_threads await page.locator('//*[@id="showpool_threads"]').click(); expect( - await page.locator('//*[@id="showpool_threads"]').isChecked(), + await page.locator('//*[@id="showpool_threads"]').isChecked() ).toBeFalsy(); //showtext_key await page.locator('//*[@id="showtext_key"]').click(); expect( - await page.locator('//*[@id="showtext_key"]').isChecked(), + await page.locator('//*[@id="showtext_key"]').isChecked() ).toBeTruthy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); }); diff --git a/src/frontend/tests/end-to-end/promptModalComponent.spec.ts b/src/frontend/tests/end-to-end/promptModalComponent.spec.ts index 631d69549..b7c86ee97 100644 --- a/src/frontend/tests/end-to-end/promptModalComponent.spec.ts +++ b/src/frontend/tests/end-to-end/promptModalComponent.spec.ts @@ -138,7 +138,7 @@ test("PromptTemplateComponent", async ({ page }) => { await page.locator('//*[@id="showtemplate"]').click(); expect( - await page.locator('//*[@id="showtemplate"]').isChecked(), + await page.locator('//*[@id="showtemplate"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showprompt"]').click(); @@ -158,13 +158,13 @@ test("PromptTemplateComponent", async ({ page }) => { await page.locator('//*[@id="showtemplate"]').click(); expect( - await page.locator('//*[@id="showtemplate"]').isChecked(), + await page.locator('//*[@id="showtemplate"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showprompt"]').click(); expect(await page.locator('//*[@id="showprompt"]').isChecked()).toBeTruthy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); await page.getByTestId("more-options-modal").click(); await page.getByTestId("edit-button-modal").click(); diff --git a/src/frontend/tests/end-to-end/saveComponents.spec.ts b/src/frontend/tests/end-to-end/saveComponents.spec.ts index 49ec8fd0b..163cb8dcd 100644 --- a/src/frontend/tests/end-to-end/saveComponents.spec.ts +++ b/src/frontend/tests/end-to-end/saveComponents.spec.ts @@ -26,8 +26,8 @@ test.describe("save component tests", () => { // Read your file into a buffer. const jsonContent = readFileSync( - "tests/end-to-end/assets/flow_group_test.json", - "utf-8", + "src/frontend/tests/end-to-end/assets/flow_group_test.json", + "utf-8" ); // Create the DataTransfer and File @@ -49,7 +49,7 @@ test.describe("save component tests", () => { "drop", { dataTransfer, - }, + } ); const genericNoda = page.getByTestId("div-generic-node"); diff --git a/src/frontend/tests/end-to-end/store.spec.ts b/src/frontend/tests/end-to-end/store.spec.ts index ec526c0de..f1ea0d84c 100644 --- a/src/frontend/tests/end-to-end/store.spec.ts +++ b/src/frontend/tests/end-to-end/store.spec.ts @@ -141,7 +141,6 @@ test("should add API-KEY", async ({ page }) => { await page.waitForTimeout(2000); await page.getByText("API Key Error").isVisible(); - await page.getByTestId("api-key-button-store").click(); await page .getByPlaceholder("Insert your API Key") .fill(process.env.STORE_API_KEY ?? ""); @@ -174,6 +173,9 @@ test("should like and add components and flows", async ({ page }) => { await page.waitForTimeout(2000); await page.getByText("API Key Error").isHidden(); + await page.waitForTimeout(2000); + + await page.getByTestId("button-store").click(); await page.waitForTimeout(5000); const likedValue = await page @@ -257,7 +259,7 @@ test("should share component with share button", async ({ page }) => { await page.getByText("Set workflow status to public").isVisible(); await page .getByText( - "Attention: API keys in specified fields are automatically removed upon sharing.", + "Attention: API keys in specified fields are automatically removed upon sharing." ) .isVisible(); await page.getByText("Export").first().isVisible(); diff --git a/src/frontend/tests/end-to-end/textInputOutput.spec.ts b/src/frontend/tests/end-to-end/textInputOutput.spec.ts index b5aee8fba..88e6b9f08 100644 --- a/src/frontend/tests/end-to-end/textInputOutput.spec.ts +++ b/src/frontend/tests/end-to-end/textInputOutput.spec.ts @@ -60,7 +60,7 @@ test("TextInputOutputComponent", async ({ page }) => { // Click and hold on the first element await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[1]/div/div[2]/div[6]/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[1]/div/div[2]/div[6]/button/div/div' ) .hover(); await page.mouse.down(); @@ -68,7 +68,7 @@ test("TextInputOutputComponent", async ({ page }) => { // Move to the second element await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[2]/div/div[2]/div[9]/div/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[2]/div/div[2]/div[9]/div/button/div/div' ) .hover(); @@ -92,7 +92,7 @@ test("TextInputOutputComponent", async ({ page }) => { // Click and hold on the first element await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[2]/div/div[2]/div[13]/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[2]/div/div[2]/div[13]/button/div/div' ) .hover(); await page.mouse.down(); @@ -100,7 +100,7 @@ test("TextInputOutputComponent", async ({ page }) => { // Move to the second element await page .locator( - '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[3]/div/div[2]/div[3]/div/button/div/div', + '//*[@id="react-flow-id"]/div/div[1]/div[1]/div/div[2]/div[3]/div/div[2]/div[3]/div/button/div/div' ) .hover(); @@ -132,11 +132,13 @@ test("TextInputOutputComponent", async ({ page }) => { await page.getByText("Outputs", { exact: true }).nth(1).click(); await page.getByText("Text Output", { exact: true }).nth(2).click(); - let contentOutput = await page.getByPlaceholder("Empty").inputValue(); + let contentOutput = await page.getByPlaceholder("Enter text...").inputValue(); expect(contentOutput).not.toBe(null); await page.keyboard.press("Escape"); + await page.getByText("Close", { exact: true }).last().click(); + await page .getByTestId("popover-anchor-input-input_value") .nth(0) @@ -151,6 +153,6 @@ test("TextInputOutputComponent", async ({ page }) => { await page.getByText("Outputs", { exact: true }).nth(1).click(); await page.getByText("Text Output", { exact: true }).nth(2).click(); - contentOutput = await page.getByPlaceholder("Empty").inputValue(); + contentOutput = await page.getByPlaceholder("Enter text...").inputValue(); expect(contentOutput).not.toBe(null); }); diff --git a/src/frontend/tests/end-to-end/toggleComponent.spec.ts b/src/frontend/tests/end-to-end/toggleComponent.spec.ts index 63c4d473b..cbe77f1e3 100644 --- a/src/frontend/tests/end-to-end/toggleComponent.spec.ts +++ b/src/frontend/tests/end-to-end/toggleComponent.spec.ts @@ -45,10 +45,10 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showload_hidden"]').click(); expect( - await page.locator('//*[@id="showload_hidden"]').isChecked(), + await page.locator('//*[@id="showload_hidden"]').isChecked() ).toBeTruthy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); await page.getByTitle("fit view").click(); @@ -81,12 +81,12 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showload_hidden"]').click(); expect( - await page.locator('//*[@id="showload_hidden"]').isChecked(), + await page.locator('//*[@id="showload_hidden"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showmax_concurrency"]').click(); expect( - await page.locator('//*[@id="showmax_concurrency"]').isChecked(), + await page.locator('//*[@id="showmax_concurrency"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showpath"]').click(); @@ -94,22 +94,22 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showrecursive"]').click(); expect( - await page.locator('//*[@id="showrecursive"]').isChecked(), + await page.locator('//*[@id="showrecursive"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showsilent_errors"]').click(); expect( - await page.locator('//*[@id="showsilent_errors"]').isChecked(), + await page.locator('//*[@id="showsilent_errors"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showuse_multithreading"]').click(); expect( - await page.locator('//*[@id="showuse_multithreading"]').isChecked(), + await page.locator('//*[@id="showuse_multithreading"]').isChecked() ).toBeTruthy(); await page.locator('//*[@id="showmax_concurrency"]').click(); expect( - await page.locator('//*[@id="showmax_concurrency"]').isChecked(), + await page.locator('//*[@id="showmax_concurrency"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showpath"]').click(); @@ -117,20 +117,20 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showrecursive"]').click(); expect( - await page.locator('//*[@id="showrecursive"]').isChecked(), + await page.locator('//*[@id="showrecursive"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showsilent_errors"]').click(); expect( - await page.locator('//*[@id="showsilent_errors"]').isChecked(), + await page.locator('//*[@id="showsilent_errors"]').isChecked() ).toBeFalsy(); await page.locator('//*[@id="showuse_multithreading"]').click(); expect( - await page.locator('//*[@id="showuse_multithreading"]').isChecked(), + await page.locator('//*[@id="showuse_multithreading"]').isChecked() ).toBeFalsy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); const plusButtonLocator = page.getByTestId("toggle-load_hidden"); const elementCount = await plusButtonLocator?.count(); @@ -144,38 +144,38 @@ test("ToggleComponent", async ({ page }) => { await page.locator('//*[@id="showload_hidden"]').click(); expect( - await page.locator('//*[@id="showload_hidden"]').isChecked(), + await page.locator('//*[@id="showload_hidden"]').isChecked() ).toBeTruthy(); expect( - await page.getByTestId("toggle-edit-load_hidden").isChecked(), + await page.getByTestId("toggle-edit-load_hidden").isChecked() ).toBeTruthy(); - await page.locator('//*[@id="saveChangesBtn"]').click(); + await page.getByText("Save Changes", { exact: true }).click(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeFalsy(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeTruthy(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeFalsy(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeTruthy(); await page.getByTestId("toggle-load_hidden").click(); expect( - await page.getByTestId("toggle-load_hidden").isChecked(), + await page.getByTestId("toggle-load_hidden").isChecked() ).toBeFalsy(); } }); From 9e6f3a064df2bebbddbd29af05a048f37348ce48 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 19:01:01 -0300 Subject: [PATCH 43/52] Made Submit button use loading of button --- src/frontend/src/components/ui/button.tsx | 14 ++++++++-- src/frontend/src/modals/baseModal/index.tsx | 30 ++++++--------------- 2 files changed, 20 insertions(+), 24 deletions(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index faab61370..d7562d58a 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -5,7 +5,7 @@ import { cn } from "../../utils/utils"; import ForwardedIconComponent from "../genericIconComponent"; const buttonVariants = cva( - "inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", + "inline-flex items-center justify-center gap-2 rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background", { variants: { variant: { @@ -54,7 +54,16 @@ function toTitleCase(text: string) { const Button = React.forwardRef( ( - { className, variant, size, loading, asChild = false, children, ...props }, + { + className, + variant, + size, + loading, + disabled, + asChild = false, + children, + ...props + }, ref, ) => { const Comp = asChild ? Slot : "button"; @@ -66,6 +75,7 @@ const Button = React.forwardRef( <> diff --git a/src/frontend/src/modals/baseModal/index.tsx b/src/frontend/src/modals/baseModal/index.tsx index a9efe3ddf..2b1c67db7 100644 --- a/src/frontend/src/modals/baseModal/index.tsx +++ b/src/frontend/src/modals/baseModal/index.tsx @@ -86,24 +86,10 @@ const Footer: React.FC<{
@@ -116,7 +102,7 @@ interface BaseModalProps { React.ReactElement, React.ReactElement, React.ReactElement?, - React.ReactElement? + React.ReactElement?, ]; open?: boolean; setOpen?: (open: boolean) => void; @@ -150,16 +136,16 @@ function BaseModal({ onSubmit, }: BaseModalProps) { const headerChild = React.Children.toArray(children).find( - (child) => (child as React.ReactElement).type === Header + (child) => (child as React.ReactElement).type === Header, ); const triggerChild = React.Children.toArray(children).find( - (child) => (child as React.ReactElement).type === Trigger + (child) => (child as React.ReactElement).type === Trigger, ); const ContentChild = React.Children.toArray(children).find( - (child) => (child as React.ReactElement).type === Content + (child) => (child as React.ReactElement).type === Content, ); const ContentFooter = React.Children.toArray(children).find( - (child) => (child as React.ReactElement).type === Footer + (child) => (child as React.ReactElement).type === Footer, ); let minWidth: string; From 42d22ae86e7fa1126fcf9f1a7fbfe9e1c3c946ab Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Tue, 4 Jun 2024 19:07:08 -0300 Subject: [PATCH 44/52] fixed unnecessary code at Button --- src/frontend/src/components/ui/button.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index d7562d58a..da9107a69 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -80,10 +80,8 @@ const Button = React.forwardRef( {...props} > {loading ? ( - - - {newChildren} - + + {newChildren} Date: Tue, 4 Jun 2024 19:09:27 -0300 Subject: [PATCH 45/52] fixed button --- src/frontend/src/components/ui/button.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index da9107a69..cbcadf2c3 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -81,7 +81,7 @@ const Button = React.forwardRef( > {loading ? ( - {newChildren} + {newChildren} Date: Tue, 4 Jun 2024 19:09:39 -0300 Subject: [PATCH 46/52] Fixed loading not being of the right size --- src/frontend/src/modals/shareModal/index.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/frontend/src/modals/shareModal/index.tsx b/src/frontend/src/modals/shareModal/index.tsx index f8f8ea447..bde63de7e 100644 --- a/src/frontend/src/modals/shareModal/index.tsx +++ b/src/frontend/src/modals/shareModal/index.tsx @@ -263,9 +263,7 @@ export default function ShareModal({ From 898e9f101ae02311ec56c009c59dfb3063a19088 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Tue, 4 Jun 2024 20:48:39 -0300 Subject: [PATCH 47/52] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20(GeneralPage):=20ref?= =?UTF-8?q?actor=20API=20calls=20into=20custom=20hooks=20for=20better=20mo?= =?UTF-8?q?dularity=20=E2=9C=A8=20(GeneralPage):=20add=20custom=20hooks=20?= =?UTF-8?q?for=20patching=20gradient,=20password,=20and=20saving=20API=20k?= =?UTF-8?q?ey=20=F0=9F=92=A1=20(GeneralPage):=20add=20useScrollToElement?= =?UTF-8?q?=20hook=20to=20handle=20smooth=20scrolling=20to=20elements?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ (hooks): add useSaveKey and useScrollToElement custom hooks ✅ (tests): update end-to-end tests for improved stability and accuracy --- .../SettingsPage/pages/GeneralPage/index.tsx | 120 ++++-------------- .../pages/hooks/use-patch-gradient.tsx | 37 ++++++ .../pages/hooks/use-patch-password.tsx | 36 ++++++ .../SettingsPage/pages/hooks/use-save-key.tsx | 48 +++++++ .../pages/hooks/use-scroll-to-element.tsx | 17 +++ .../end-to-end/deleteComponentFlows.spec.ts | 1 + .../tests/end-to-end/flowSettings.spec.ts | 3 +- src/frontend/tests/end-to-end/store.spec.ts | 19 +-- 8 files changed, 179 insertions(+), 102 deletions(-) create mode 100644 src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-gradient.tsx create mode 100644 src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-password.tsx create mode 100644 src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx create mode 100644 src/frontend/src/pages/SettingsPage/pages/hooks/use-scroll-to-element.tsx diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx index 4ba04692f..755e7959c 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx @@ -1,6 +1,5 @@ import * as Form from "@radix-ui/react-form"; -import { cloneDeep } from "lodash"; -import { useContext, useEffect, useState } from "react"; +import { useContext, useState } from "react"; import { useParams } from "react-router-dom"; import ForwardedIconComponent from "../../../../components/genericIconComponent"; import GradientChooserComponent from "../../../../components/gradientChooserComponent"; @@ -14,14 +13,6 @@ import { CardHeader, CardTitle, } from "../../../../components/ui/card"; -import { - API_ERROR_ALERT, - API_SUCCESS_ALERT, - EDIT_PASSWORD_ALERT_LIST, - EDIT_PASSWORD_ERROR_ALERT, - SAVE_ERROR_ALERT, - SAVE_SUCCESS_ALERT, -} from "../../../../constants/alerts_constants"; import { CONTROL_PATCH_USER_STATE, CREATE_API_KEY, @@ -30,11 +21,6 @@ import { NO_API_KEY, } from "../../../../constants/constants"; import { AuthContext } from "../../../../contexts/authContext"; -import { - addApiKeyStore, - resetPassword, - updateUser, -} from "../../../../controllers/API"; import useAlertStore from "../../../../stores/alertStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; import { useStoreStore } from "../../../../stores/storeStore"; @@ -43,6 +29,10 @@ import { patchUserInputStateType, } from "../../../../types/components"; import { gradients } from "../../../../utils/styleUtils"; +import usePatchGradient from "../hooks/use-patch-gradient"; +import usePatchPassword from "../hooks/use-patch-password"; +import useSaveKey from "../hooks/use-save-key"; +import useScrollToElement from "../hooks/use-scroll-to-element"; export default function GeneralPage() { const setCurrentFlowId = useFlowsManagerStore( @@ -51,29 +41,16 @@ export default function GeneralPage() { const { scrollId } = useParams(); - useEffect(() => { - const element = document.getElementById(scrollId ?? "null"); - if (element) { - // 👇 Will scroll smoothly to the top of the next section - element.scrollIntoView({ behavior: "smooth" }); - } - }, [scrollId]); - const [inputState, setInputState] = useState( CONTROL_PATCH_USER_STATE ); const { autoLogin } = useContext(AuthContext); - // set null id - useEffect(() => { - setCurrentFlowId(""); - }, []); const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const { userData, setUserData } = useContext(AuthContext); const hasStore = useStoreStore((state) => state.hasStore); - const { storeApiKey } = useContext(AuthContext); const validApiKey = useStoreStore((state) => state.validApiKey); const hasApiKey = useStoreStore((state) => state.hasApiKey); @@ -83,71 +60,28 @@ export default function GeneralPage() { const setLoadingApiKey = useStoreStore((state) => state.updateLoadingApiKey); const { password, cnfPassword, gradient, apikey } = inputState; - async function handlePatchPassword() { - if (password !== cnfPassword) { - setErrorData({ - title: EDIT_PASSWORD_ERROR_ALERT, - list: [EDIT_PASSWORD_ALERT_LIST], - }); - return; - } - try { - if (password !== "") await resetPassword(userData!.id, { password }); - handleInput({ target: { name: "password", value: "" } }); - handleInput({ target: { name: "cnfPassword", value: "" } }); - setSuccessData({ title: SAVE_SUCCESS_ALERT }); - } catch (error) { - setErrorData({ - title: SAVE_ERROR_ALERT, - list: [(error as any).response.data.detail], - }); - } - } + const { handlePatchPassword } = usePatchPassword( + userData, + setSuccessData, + setErrorData + ); - async function handlePatchGradient() { - try { - if (gradient !== "") - await updateUser(userData!.id, { profile_image: gradient }); - if (gradient !== "") { - let newUserData = cloneDeep(userData); - newUserData!.profile_image = gradient; + const { handlePatchGradient } = usePatchGradient( + setSuccessData, + setErrorData, + userData, + setUserData + ); - setUserData(newUserData); - } - setSuccessData({ title: SAVE_SUCCESS_ALERT }); - } catch (error) { - setErrorData({ - title: SAVE_ERROR_ALERT, - list: [(error as any).response.data.detail], - }); - } - } + useScrollToElement(scrollId, setCurrentFlowId); - const handleSaveKey = () => { - if (apikey) { - addApiKeyStore(apikey).then( - () => { - setSuccessData({ - title: API_SUCCESS_ALERT, - }); - storeApiKey(apikey); - setHasApiKey(true); - setValidApiKey(true); - setLoadingApiKey(false); - handleInput({ target: { name: "apikey", value: "" } }); - }, - (error) => { - setErrorData({ - title: API_ERROR_ALERT, - list: [error["response"]["data"]["detail"]], - }); - setHasApiKey(false); - setValidApiKey(false); - setLoadingApiKey(false); - } - ); - } - }; + const { handleSaveKey } = useSaveKey( + setSuccessData, + setErrorData, + setHasApiKey, + setValidApiKey, + setLoadingApiKey + ); function handleInput({ target: { name, value }, @@ -175,7 +109,7 @@ export default function GeneralPage() {
{ - handlePatchGradient(); + handlePatchGradient(gradient); event.preventDefault(); }} > @@ -213,7 +147,7 @@ export default function GeneralPage() { {!autoLogin && ( { - handlePatchPassword(); + handlePatchPassword(password, cnfPassword, handleInput); event.preventDefault(); }} > @@ -281,7 +215,7 @@ export default function GeneralPage() { { event.preventDefault(); - handleSaveKey(); + handleSaveKey(apikey, handleInput); }} > diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-gradient.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-gradient.tsx new file mode 100644 index 000000000..9adb923a7 --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-gradient.tsx @@ -0,0 +1,37 @@ +import cloneDeep from "lodash/cloneDeep"; +import { + SAVE_ERROR_ALERT, + SAVE_SUCCESS_ALERT, +} from "../../../../constants/alerts_constants"; +import { updateUser } from "../../../../controllers/API"; + +const usePatchGradient = ( + setSuccessData, + setErrorData, + currentUserData, + setUserData +) => { + const handlePatchGradient = async (gradient) => { + try { + if (gradient !== "") { + await updateUser(currentUserData.id, { profile_image: gradient }); + let newUserData = cloneDeep(currentUserData); + newUserData.profile_image = gradient; + setUserData(newUserData); + } + setSuccessData({ title: SAVE_SUCCESS_ALERT }); + } catch (error) { + setErrorData({ + title: SAVE_ERROR_ALERT, + list: [(error as any)?.response?.data?.detail], + }); + } + }; + + return { + currentUserData, + handlePatchGradient, + }; +}; + +export default usePatchGradient; diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-password.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-password.tsx new file mode 100644 index 000000000..c4b452e6b --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-patch-password.tsx @@ -0,0 +1,36 @@ +import { + EDIT_PASSWORD_ALERT_LIST, + EDIT_PASSWORD_ERROR_ALERT, + SAVE_ERROR_ALERT, + SAVE_SUCCESS_ALERT, +} from "../../../../constants/alerts_constants"; +import { resetPassword } from "../../../../controllers/API"; + +const usePatchPassword = (userData, setSuccessData, setErrorData) => { + const handlePatchPassword = async (password, cnfPassword, handleInput) => { + if (password !== cnfPassword) { + setErrorData({ + title: EDIT_PASSWORD_ERROR_ALERT, + list: [EDIT_PASSWORD_ALERT_LIST], + }); + return; + } + try { + if (password !== "") await resetPassword(userData.id, { password }); + handleInput({ target: { name: "password", value: "" } }); + handleInput({ target: { name: "cnfPassword", value: "" } }); + setSuccessData({ title: SAVE_SUCCESS_ALERT }); + } catch (error) { + setErrorData({ + title: SAVE_ERROR_ALERT, + list: [(error as any)?.response?.data?.detail], + }); + } + }; + + return { + handlePatchPassword, + }; +}; + +export default usePatchPassword; diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx new file mode 100644 index 000000000..bdd105fef --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-save-key.tsx @@ -0,0 +1,48 @@ +import { useContext } from "react"; +import { + API_ERROR_ALERT, + API_SUCCESS_ALERT, +} from "../../../../constants/alerts_constants"; +import { AuthContext } from "../../../../contexts/authContext"; +import { addApiKeyStore } from "../../../../controllers/API"; + +const useSaveKey = ( + setSuccessData, + setErrorData, + setHasApiKey, + setValidApiKey, + setLoadingApiKey +) => { + const { storeApiKey } = useContext(AuthContext); + + const handleSaveKey = (apikey, handleInput) => { + if (apikey) { + setLoadingApiKey(true); + addApiKeyStore(apikey).then( + () => { + setSuccessData({ title: API_SUCCESS_ALERT }); + storeApiKey(apikey); + setHasApiKey(true); + setValidApiKey(true); + setLoadingApiKey(false); + handleInput({ target: { name: "apikey", value: "" } }); + }, + (error) => { + setErrorData({ + title: API_ERROR_ALERT, + list: [error.response.data.detail], + }); + setHasApiKey(false); + setValidApiKey(false); + setLoadingApiKey(false); + } + ); + } + }; + + return { + handleSaveKey, + }; +}; + +export default useSaveKey; diff --git a/src/frontend/src/pages/SettingsPage/pages/hooks/use-scroll-to-element.tsx b/src/frontend/src/pages/SettingsPage/pages/hooks/use-scroll-to-element.tsx new file mode 100644 index 000000000..a56c2d5d6 --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/hooks/use-scroll-to-element.tsx @@ -0,0 +1,17 @@ +import { useEffect } from "react"; + +const useScrollToElement = (scrollId, setCurrentFlowId) => { + useEffect(() => { + const element = document.getElementById(scrollId ?? "null"); + if (element) { + // Scroll smoothly to the top of the next section + element.scrollIntoView({ behavior: "smooth" }); + } + }, [scrollId]); + + useEffect(() => { + setCurrentFlowId(""); + }, [setCurrentFlowId]); +}; + +export default useScrollToElement; diff --git a/src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts b/src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts index 854ac388c..836e8fe81 100644 --- a/src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts +++ b/src/frontend/tests/end-to-end/deleteComponentFlows.spec.ts @@ -11,6 +11,7 @@ test("shoud delete a flow", async ({ page }) => { .fill(process.env.STORE_API_KEY ?? ""); await page.getByText("Save").last().click(); await page.waitForTimeout(8000); + await page.getByText("Store").nth(0).click(); await page.getByTestId("install-Website Content QA").click(); await page.waitForTimeout(5000); diff --git a/src/frontend/tests/end-to-end/flowSettings.spec.ts b/src/frontend/tests/end-to-end/flowSettings.spec.ts index e0a3b8f27..a2e9f25c0 100644 --- a/src/frontend/tests/end-to-end/flowSettings.spec.ts +++ b/src/frontend/tests/end-to-end/flowSettings.spec.ts @@ -45,7 +45,8 @@ test("flowSettings", async ({ page }) => { ); await page.getByTestId("save-flow-settings").click(); - await page.getByTestId("save-flow-settings").click(); + + await page.getByText("Close").last().click(); await page.waitForTimeout(1000); diff --git a/src/frontend/tests/end-to-end/store.spec.ts b/src/frontend/tests/end-to-end/store.spec.ts index f1ea0d84c..13963d698 100644 --- a/src/frontend/tests/end-to-end/store.spec.ts +++ b/src/frontend/tests/end-to-end/store.spec.ts @@ -96,27 +96,29 @@ test("should filter by type", async ({ page }) => { await page.getByText("Website Content QA").isVisible(); await page.getByTestId("flows-button-store").click(); - await page.waitForTimeout(3000); + await page.waitForTimeout(8000); let iconGroup = await page.getByTestId("icon-Group")?.count(); expect(iconGroup).not.toBe(0); - await page.getByText("icon-ToyBrick").isHidden(); + await page.getByText("icon-ToyBrick").last().isHidden(); await page.getByTestId("components-button-store").click(); - await page.waitForTimeout(3000); + await page.waitForTimeout(8000); - await page.getByTestId("icon-Group").isHidden(); + await page.getByTestId("icon-Group").last().isHidden(); let toyBrick = await page.getByTestId("icon-ToyBrick")?.count(); expect(toyBrick).not.toBe(0); await page.getByTestId("all-button-store").click(); - await page.waitForTimeout(3000); + await page.waitForTimeout(8000); - iconGroup = await page.getByTestId("icon-Group")?.count(); - toyBrick = await page.getByTestId("icon-ToyBrick")?.count(); + let iconGroupAllCount = await page.getByTestId("icon-Group")?.count(); + await page.waitForTimeout(2000); + let toyBrickAllCount = await page.getByTestId("icon-ToyBrick")?.count(); + await page.waitForTimeout(2000); - if (iconGroup === 0 || toyBrick === 0) { + if (iconGroupAllCount === 0 || toyBrickAllCount === 0) { expect(false).toBe(true); } }); @@ -252,6 +254,7 @@ test("should share component with share button", async ({ page }) => { .getByPlaceholder("Flow description") .inputValue(); await page.getByText("Save").last().click(); + await page.getByText("Close").last().click(); await page.getByTestId("icon-Share3").first().click(); await page.getByText("Name:").isVisible(); From 14a7e2835e09449820c04b7df2eaf7b5ae27591a Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Wed, 5 Jun 2024 09:38:03 -0300 Subject: [PATCH 48/52] =?UTF-8?q?=E2=9C=A8=20(frontend):=20add=20switch-ca?= =?UTF-8?q?se-size=20helper=20for=20modal=20size=20management=20=E2=99=BB?= =?UTF-8?q?=EF=B8=8F=20(frontend):=20refactor=20BaseModal=20to=20use=20swi?= =?UTF-8?q?tchCaseModalSize=20helper=20=E2=9C=A8=20(frontend):=20add=20Gen?= =?UTF-8?q?eralPageHeaderComponent=20for=20settings=20page=20header=20?= =?UTF-8?q?=E2=9C=A8=20(frontend):=20add=20PasswordFormComponent=20for=20p?= =?UTF-8?q?assword=20management=20in=20settings=20page?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ (SettingsPage): add ProfileGradientFormComponent to allow users to choose profile gradient ✨ (SettingsPage): add StoreApiKeyFormComponent to enable users to store API keys ♻️ (GeneralPage): refactor GeneralPage to use modular components for forms --- .../baseModal/helpers/switch-case-size.ts | 67 +++++ src/frontend/src/modals/baseModal/index.tsx | 68 +---- .../components/GeneralPageHeader/index.tsx | 23 ++ .../components/PasswordForm/index.tsx | 93 +++++++ .../components/ProfileGradientForm/index.tsx | 68 +++++ .../components/StoreApiKeyForm/index.tsx | 102 ++++++++ .../SettingsPage/pages/GeneralPage/index.tsx | 239 +++--------------- 7 files changed, 387 insertions(+), 273 deletions(-) create mode 100644 src/frontend/src/modals/baseModal/helpers/switch-case-size.ts create mode 100644 src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/GeneralPageHeader/index.tsx create mode 100644 src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/PasswordForm/index.tsx create mode 100644 src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfileGradientForm/index.tsx create mode 100644 src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/StoreApiKeyForm/index.tsx diff --git a/src/frontend/src/modals/baseModal/helpers/switch-case-size.ts b/src/frontend/src/modals/baseModal/helpers/switch-case-size.ts new file mode 100644 index 000000000..35004c2ec --- /dev/null +++ b/src/frontend/src/modals/baseModal/helpers/switch-case-size.ts @@ -0,0 +1,67 @@ +export const switchCaseModalSize = (size: string) => { + let minWidth: string; + let height: string; + switch (size) { + case "x-small": + minWidth = "min-w-[20vw]"; + height = "h-full"; + break; + case "smaller": + minWidth = "min-w-[40vw]"; + height = "h-[11rem]"; + break; + case "smaller-h-full": + minWidth = "min-w-[40vw]"; + height = "h-full"; + break; + case "small": + minWidth = "min-w-[40vw]"; + height = "h-[40vh]"; + break; + case "small-h-full": + minWidth = "min-w-[40vw]"; + height = "h-full"; + break; + case "medium": + minWidth = "min-w-[60vw]"; + height = "h-[60vh]"; + break; + case "medium-h-full": + minWidth = "min-w-[60vw]"; + height = "h-full"; + + break; + case "large": + minWidth = "min-w-[85vw]"; + height = "h-[80vh]"; + break; + case "three-cards": + minWidth = "min-w-[1066px]"; + height = "h-fit"; + break; + case "large-thin": + minWidth = "min-w-[65vw]"; + height = "h-[80vh]"; + break; + + case "md-thin": + minWidth = "min-w-[85vw]"; + height = "h-[70vh]"; + break; + + case "sm-thin": + minWidth = "min-w-[65vw]"; + height = "h-[70vh]"; + break; + + case "large-h-full": + minWidth = "min-w-[80vw]"; + height = "h-full"; + break; + default: + minWidth = "min-w-[80vw]"; + height = "h-[80vh]"; + break; + } + return { minWidth, height }; +}; diff --git a/src/frontend/src/modals/baseModal/index.tsx b/src/frontend/src/modals/baseModal/index.tsx index 2b1c67db7..a53aab173 100644 --- a/src/frontend/src/modals/baseModal/index.tsx +++ b/src/frontend/src/modals/baseModal/index.tsx @@ -16,10 +16,10 @@ import { } from "../../components/ui/dialog-with-no-close"; import { DialogClose } from "@radix-ui/react-dialog"; -import ForwardedIconComponent from "../../components/genericIconComponent"; import { Button } from "../../components/ui/button"; import { modalHeaderType } from "../../types/components"; import { cn } from "../../utils/utils"; +import { switchCaseModalSize } from "./helpers/switch-case-size"; type ContentProps = { children: ReactNode }; type HeaderProps = { children: ReactNode; description: string }; @@ -148,71 +148,7 @@ function BaseModal({ (child) => (child as React.ReactElement).type === Footer, ); - let minWidth: string; - let height: string; - - switch (size) { - case "x-small": - minWidth = "min-w-[20vw]"; - height = "h-full"; - break; - case "smaller": - minWidth = "min-w-[40vw]"; - height = "h-[11rem]"; - break; - case "smaller-h-full": - minWidth = "min-w-[40vw]"; - height = "h-full"; - break; - case "small": - minWidth = "min-w-[40vw]"; - height = "h-[40vh]"; - break; - case "small-h-full": - minWidth = "min-w-[40vw]"; - height = "h-full"; - break; - case "medium": - minWidth = "min-w-[60vw]"; - height = "h-[60vh]"; - break; - case "medium-h-full": - minWidth = "min-w-[60vw]"; - height = "h-full"; - - break; - case "large": - minWidth = "min-w-[85vw]"; - height = "h-[80vh]"; - break; - case "three-cards": - minWidth = "min-w-[1066px]"; - height = "h-fit"; - break; - case "large-thin": - minWidth = "min-w-[65vw]"; - height = "h-[80vh]"; - break; - - case "md-thin": - minWidth = "min-w-[85vw]"; - height = "h-[70vh]"; - break; - - case "sm-thin": - minWidth = "min-w-[65vw]"; - height = "h-[70vh]"; - break; - - case "large-h-full": - minWidth = "min-w-[80vw]"; - height = "h-full"; - break; - default: - minWidth = "min-w-[80vw]"; - height = "h-[80vh]"; - break; - } + let { minWidth, height } = switchCaseModalSize(size); useEffect(() => { if (onChangeOpenModal) { diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/GeneralPageHeader/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/GeneralPageHeader/index.tsx new file mode 100644 index 000000000..cb65942bd --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/GeneralPageHeader/index.tsx @@ -0,0 +1,23 @@ +import ForwardedIconComponent from "../../../../../../components/genericIconComponent"; + +const GeneralPageHeaderComponent = () => { + return ( + <> +
+
+

+ General + +

+

+ Manage settings related to Langflow and your account. +

+
+
+ + ); +}; +export default GeneralPageHeaderComponent; diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/PasswordForm/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/PasswordForm/index.tsx new file mode 100644 index 000000000..28edb2f09 --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/PasswordForm/index.tsx @@ -0,0 +1,93 @@ +import * as Form from "@radix-ui/react-form"; +import InputComponent from "../../../../../../components/inputComponent"; +import { Button } from "../../../../../../components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "../../../../../../components/ui/card"; + +type PasswordFormComponentProps = { + password: string; + cnfPassword: string; + handleInput: (event: any) => void; + handlePatchPassword: ( + password: string, + cnfPassword: string, + handleInput: any, + ) => void; +}; +const PasswordFormComponent = ({ + password, + cnfPassword, + handleInput, + handlePatchPassword, +}: PasswordFormComponentProps) => { + return ( + <> + { + handlePatchPassword(password, cnfPassword, handleInput); + event.preventDefault(); + }} + > + + + Password + + Type your new password and confirm it. + + + +
+ + { + handleInput({ target: { name: "password", value } }); + }} + value={password} + isForm + password={true} + placeholder="Password" + className="w-full" + /> + + Please enter your password + + + + { + handleInput({ + target: { name: "cnfPassword", value }, + }); + }} + value={cnfPassword} + isForm + password={true} + placeholder="Confirm Password" + className="w-full" + /> + + + Please confirm your password + + +
+
+ + + + + +
+
+ + ); +}; +export default PasswordFormComponent; diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfileGradientForm/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfileGradientForm/index.tsx new file mode 100644 index 000000000..a92a00103 --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/ProfileGradientForm/index.tsx @@ -0,0 +1,68 @@ +import * as Form from "@radix-ui/react-form"; +import GradientChooserComponent from "../../../../../../components/gradientChooserComponent"; +import { Button } from "../../../../../../components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "../../../../../../components/ui/card"; +import { gradients } from "../../../../../../utils/styleUtils"; + +type ProfileGradientFormComponentProps = { + gradient: string; + handleInput: (event: any) => void; + handlePatchGradient: (gradient: string) => void; + userData: any; +}; +const ProfileGradientFormComponent = ({ + gradient, + handleInput, + handlePatchGradient, + userData, +}: ProfileGradientFormComponentProps) => { + return ( + <> + { + handlePatchGradient(gradient); + event.preventDefault(); + }} + > + + + Profile Gradient + + Choose the gradient that appears as your profile picture. + + + +
+ { + handleInput({ target: { name: "gradient", value } }); + }} + /> +
+
+ + + + + +
+
+ + ); +}; +export default ProfileGradientFormComponent; diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/StoreApiKeyForm/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/StoreApiKeyForm/index.tsx new file mode 100644 index 000000000..5bc5ea38b --- /dev/null +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/components/StoreApiKeyForm/index.tsx @@ -0,0 +1,102 @@ +import * as Form from "@radix-ui/react-form"; +import InputComponent from "../../../../../../components/inputComponent"; +import { Button } from "../../../../../../components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "../../../../../../components/ui/card"; +import { + CREATE_API_KEY, + INSERT_API_KEY, + INVALID_API_KEY, + NO_API_KEY, +} from "../../../../../../constants/constants"; + +type StoreApiKeyFormComponentProps = { + apikey: string; + handleInput: (event: any) => void; + handleSaveKey: (apikey: string, handleInput: any) => void; + loadingApiKey: boolean; + validApiKey: boolean; + hasApiKey: boolean; +}; +const StoreApiKeyFormComponent = ({ + apikey, + handleInput, + handleSaveKey, + loadingApiKey, + validApiKey, + hasApiKey, +}: StoreApiKeyFormComponentProps) => { + return ( + <> + { + event.preventDefault(); + handleSaveKey(apikey, handleInput); + }} + > + + + Store API Key + + {(hasApiKey && !validApiKey + ? INVALID_API_KEY + : !hasApiKey + ? NO_API_KEY + : "") + INSERT_API_KEY} + + + +
+
+ + { + handleInput({ target: { name: "apikey", value } }); + }} + value={apikey} + isForm + password={true} + placeholder="Insert your API Key" + className="w-full" + /> + + Please enter your API Key + + +
+ + {CREATE_API_KEY}{" "} + + langflow.store + + +
+
+ + + + + +
+
+ + ); +}; +export default StoreApiKeyFormComponent; diff --git a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx index 755e7959c..718097152 100644 --- a/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx +++ b/src/frontend/src/pages/SettingsPage/pages/GeneralPage/index.tsx @@ -1,25 +1,6 @@ -import * as Form from "@radix-ui/react-form"; import { useContext, useState } from "react"; import { useParams } from "react-router-dom"; -import ForwardedIconComponent from "../../../../components/genericIconComponent"; -import GradientChooserComponent from "../../../../components/gradientChooserComponent"; -import InputComponent from "../../../../components/inputComponent"; -import { Button } from "../../../../components/ui/button"; -import { - Card, - CardContent, - CardDescription, - CardFooter, - CardHeader, - CardTitle, -} from "../../../../components/ui/card"; -import { - CONTROL_PATCH_USER_STATE, - CREATE_API_KEY, - INSERT_API_KEY, - INVALID_API_KEY, - NO_API_KEY, -} from "../../../../constants/constants"; +import { CONTROL_PATCH_USER_STATE } from "../../../../constants/constants"; import { AuthContext } from "../../../../contexts/authContext"; import useAlertStore from "../../../../stores/alertStore"; import useFlowsManagerStore from "../../../../stores/flowsManagerStore"; @@ -28,21 +9,24 @@ import { inputHandlerEventType, patchUserInputStateType, } from "../../../../types/components"; -import { gradients } from "../../../../utils/styleUtils"; import usePatchGradient from "../hooks/use-patch-gradient"; import usePatchPassword from "../hooks/use-patch-password"; import useSaveKey from "../hooks/use-save-key"; import useScrollToElement from "../hooks/use-scroll-to-element"; +import GeneralPageHeaderComponent from "./components/GeneralPageHeader"; +import PasswordFormComponent from "./components/PasswordForm"; +import ProfileGradientFormComponent from "./components/ProfileGradientForm"; +import StoreApiKeyFormComponent from "./components/StoreApiKeyForm"; export default function GeneralPage() { const setCurrentFlowId = useFlowsManagerStore( - (state) => state.setCurrentFlowId + (state) => state.setCurrentFlowId, ); const { scrollId } = useParams(); const [inputState, setInputState] = useState( - CONTROL_PATCH_USER_STATE + CONTROL_PATCH_USER_STATE, ); const { autoLogin } = useContext(AuthContext); @@ -63,14 +47,14 @@ export default function GeneralPage() { const { handlePatchPassword } = usePatchPassword( userData, setSuccessData, - setErrorData + setErrorData, ); const { handlePatchGradient } = usePatchGradient( setSuccessData, setErrorData, userData, - setUserData + setUserData, ); useScrollToElement(scrollId, setCurrentFlowId); @@ -80,7 +64,7 @@ export default function GeneralPage() { setErrorData, setHasApiKey, setValidApiKey, - setLoadingApiKey + setLoadingApiKey, ); function handleInput({ @@ -91,192 +75,33 @@ export default function GeneralPage() { return (
-
-
-

- General - -

-

- Manage settings related to Langflow and your account. -

-
-
+
- { - handlePatchGradient(gradient); - event.preventDefault(); - }} - > - - - Profile Gradient - - Choose the gradient that appears as your profile picture. - - - -
- { - handleInput({ target: { name: "gradient", value } }); - }} - /> -
-
- - - - - -
-
- {!autoLogin && ( - { - handlePatchPassword(password, cnfPassword, handleInput); - event.preventDefault(); - }} - > - - - Password - - Type your new password and confirm it. - - - -
- - { - handleInput({ target: { name: "password", value } }); - }} - value={password} - isForm - password={true} - placeholder="Password" - className="w-full" - /> - - Please enter your password - - - - { - handleInput({ - target: { name: "cnfPassword", value }, - }); - }} - value={cnfPassword} - isForm - password={true} - placeholder="Confirm Password" - className="w-full" - /> + - - Please confirm your password - - -
-
- - - - - -
-
+ {!autoLogin && ( + )} {hasStore && ( - { - event.preventDefault(); - handleSaveKey(apikey, handleInput); - }} - > - - - Store API Key - - {(hasApiKey && !validApiKey - ? INVALID_API_KEY - : !hasApiKey - ? NO_API_KEY - : "") + INSERT_API_KEY} - - - -
-
- - { - handleInput({ target: { name: "apikey", value } }); - }} - value={apikey} - isForm - password={true} - placeholder="Insert your API Key" - className="w-full" - /> - - Please enter your API Key - - -
- - {CREATE_API_KEY}{" "} - - langflow.store - - -
-
- - - - - -
-
+ )}
From 9bafb1f5dd68687774d89e00050c6d71a6708304 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Wed, 5 Jun 2024 09:45:17 -0300 Subject: [PATCH 49/52] Fixed success alert not appearing at front, and removed alert displayed at playground --- src/frontend/src/App.tsx | 27 ++++++++------------ src/frontend/src/modals/IOModal/index.tsx | 1 + src/frontend/src/stores/flowStore.ts | 26 +++++++++++-------- src/frontend/src/types/zustand/flow/index.ts | 3 ++- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/frontend/src/App.tsx b/src/frontend/src/App.tsx index 5eee2bfa6..b9e02a27d 100644 --- a/src/frontend/src/App.tsx +++ b/src/frontend/src/App.tsx @@ -222,12 +222,19 @@ export default function App() { id={alert.id} removeAlert={removeAlert} /> + ) : alert.type === "notice" ? ( + ) : ( - alert.type === "notice" && ( - @@ -236,20 +243,6 @@ export default function App() {
))}
-
- {tempNotificationList.map((alert) => ( -
- {alert.type === "success" && ( - - )} -
- ))} -
); diff --git a/src/frontend/src/modals/IOModal/index.tsx b/src/frontend/src/modals/IOModal/index.tsx index a3d00e89c..e956c5262 100644 --- a/src/frontend/src/modals/IOModal/index.tsx +++ b/src/frontend/src/modals/IOModal/index.tsx @@ -91,6 +91,7 @@ export default function IOModal({ await buildFlow({ input_value: chatValue, startNodeId: chatInput?.id, + silent: true, }).catch((err) => { console.error(err); setLockChat(false); diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index ece6fb4e3..d3cbbef6f 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -430,10 +430,12 @@ const useFlowStore = create((set, get) => ({ startNodeId, stopNodeId, input_value, + silent, }: { startNodeId?: string; stopNodeId?: string; input_value?: string; + silent?: boolean; }) => { get().setIsBuilding(true); const currentFlow = useFlowsManagerStore.getState().currentFlow; @@ -536,19 +538,23 @@ const useFlowStore = create((set, get) => ({ startNodeId, stopNodeId, onGetOrderSuccess: () => { - setNoticeData({ title: "Running components" }); + if (!silent) { + setNoticeData({ title: "Running components" }); + } }, onBuildComplete: (allNodesValid) => { const nodeId = startNodeId || stopNodeId; - if (nodeId && allNodesValid) { - setSuccessData({ - title: `${ - get().nodes.find((node) => node.id === nodeId)?.data.node - ?.display_name - } built successfully`, - }); - } else { - setSuccessData({ title: FLOW_BUILD_SUCCESS_ALERT }); + if (!silent) { + if (nodeId && allNodesValid) { + setSuccessData({ + title: `${ + get().nodes.find((node) => node.id === nodeId)?.data.node + ?.display_name + } built successfully`, + }); + } else { + setSuccessData({ title: FLOW_BUILD_SUCCESS_ALERT }); + } } get().setIsBuilding(false); }, diff --git a/src/frontend/src/types/zustand/flow/index.ts b/src/frontend/src/types/zustand/flow/index.ts index 71ff76f55..bbb68d1c9 100644 --- a/src/frontend/src/types/zustand/flow/index.ts +++ b/src/frontend/src/types/zustand/flow/index.ts @@ -106,11 +106,12 @@ export type FlowStoreType = { startNodeId, stopNodeId, input_value, + silent, }: { - nodeId?: string; startNodeId?: string; stopNodeId?: string; input_value?: string; + silent?: boolean; }) => Promise; getFlow: () => { nodes: Node[]; edges: Edge[]; viewport: Viewport }; updateVerticesBuild: ( From 1cdc8e0efcda51c10a2da3f060eef0dc808fba50 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Wed, 5 Jun 2024 10:11:59 -0300 Subject: [PATCH 50/52] Changed debounce to use promise-debounce, making the Save promise work correctly --- src/frontend/package-lock.json | 6 ++++++ src/frontend/package.json | 1 + src/frontend/src/modals/flowSettingsModal/index.tsx | 5 ++++- src/frontend/src/stores/flowsManagerStore.ts | 3 ++- 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/frontend/package-lock.json b/src/frontend/package-lock.json index 363415ccf..69e621774 100644 --- a/src/frontend/package-lock.json +++ b/src/frontend/package-lock.json @@ -40,6 +40,7 @@ "class-variance-authority": "^0.6.1", "clsx": "^1.2.1", "cmdk": "^1.0.0", + "debounce-promise": "^3.1.2", "dompurify": "^3.0.5", "dotenv": "^16.4.5", "esbuild": "^0.17.19", @@ -5670,6 +5671,11 @@ "node": ">=12" } }, + "node_modules/debounce-promise": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/debounce-promise/-/debounce-promise-3.1.2.tgz", + "integrity": "sha512-rZHcgBkbYavBeD9ej6sP56XfG53d51CD4dnaw989YX/nZ/ZJfgRx/9ePKmTNiUiyQvh4mtrMoS3OAWW+yoYtpg==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", diff --git a/src/frontend/package.json b/src/frontend/package.json index 053a246ee..84feb826f 100644 --- a/src/frontend/package.json +++ b/src/frontend/package.json @@ -35,6 +35,7 @@ "class-variance-authority": "^0.6.1", "clsx": "^1.2.1", "cmdk": "^1.0.0", + "debounce-promise": "^3.1.2", "dompurify": "^3.0.5", "dotenv": "^16.4.5", "esbuild": "^0.17.19", diff --git a/src/frontend/src/modals/flowSettingsModal/index.tsx b/src/frontend/src/modals/flowSettingsModal/index.tsx index dbe861e00..022bd9aa0 100644 --- a/src/frontend/src/modals/flowSettingsModal/index.tsx +++ b/src/frontend/src/modals/flowSettingsModal/index.tsx @@ -23,14 +23,16 @@ export default function FlowSettingsModal({ const [name, setName] = useState(currentFlow!.name); const [description, setDescription] = useState(currentFlow!.description); const [endpoint_name, setEndpointName] = useState(currentFlow!.endpoint_name); - + const [isSaving, setIsSaving] = useState(false); function handleClick(): void { + setIsSaving(true); currentFlow!.name = name; currentFlow!.description = description; currentFlow!.endpoint_name = endpoint_name; saveFlow(currentFlow!) ?.then(() => { setOpen(false); + setIsSaving(false); }) .catch((err) => { useAlertStore.getState().setErrorData({ @@ -79,6 +81,7 @@ export default function FlowSettingsModal({ label: "Save", disabled: nameLists.includes(name) && name !== currentFlow!.name, dataTestId: "save-flow-settings", + loading: isSaving, }} /> diff --git a/src/frontend/src/stores/flowsManagerStore.ts b/src/frontend/src/stores/flowsManagerStore.ts index b6baaa180..28537a56e 100644 --- a/src/frontend/src/stores/flowsManagerStore.ts +++ b/src/frontend/src/stores/flowsManagerStore.ts @@ -1,4 +1,5 @@ -import { cloneDeep, debounce } from "lodash"; +import { cloneDeep } from "lodash"; +import * as debounce from "debounce-promise"; import { Edge, Node, Viewport, XYPosition } from "reactflow"; import { create } from "zustand"; import { SAVE_DEBOUNCE_TIME } from "../constants/constants"; From c2b58d40fa58986bffc232abeababdc95cb4bda3 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira Date: Wed, 5 Jun 2024 10:13:30 -0300 Subject: [PATCH 51/52] Removed unused code from flowLogsModal --- .../src/modals/flowLogsModal/index.tsx | 36 ------------------- 1 file changed, 36 deletions(-) diff --git a/src/frontend/src/modals/flowLogsModal/index.tsx b/src/frontend/src/modals/flowLogsModal/index.tsx index f914f529a..d7c9625b7 100644 --- a/src/frontend/src/modals/flowLogsModal/index.tsx +++ b/src/frontend/src/modals/flowLogsModal/index.tsx @@ -16,41 +16,15 @@ export default function FlowLogsModal({ open, setOpen, }: FlowSettingsPropsType): JSX.Element { - const saveFlow = useFlowsManagerStore((state) => state.saveFlow); const nodes = useFlowStore((state) => state.nodes); - const currentFlow = useFlowsManagerStore((state) => state.currentFlow); const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); - const flows = useFlowsManagerStore((state) => state.flows); const setNoticeData = useAlertStore((state) => state.setNoticeData); - useEffect(() => { - setName(currentFlow!.name); - setDescription(currentFlow!.description); - }, [currentFlow!.name, currentFlow!.description, open]); - - const [name, setName] = useState(currentFlow!.name); - const [description, setDescription] = useState(currentFlow!.description); const [columns, setColumns] = useState>([]); const [rows, setRows] = useState([]); const [activeTab, setActiveTab] = useState("Executions"); const noticed = useRef(false); - function handleClick(): void { - currentFlow!.name = name; - currentFlow!.description = description; - saveFlow(currentFlow!) - ?.then(() => { - setOpen(false); - }) - .catch((err) => { - useAlertStore.getState().setErrorData({ - title: "Error while saving changes", - list: [(err as AxiosError).response?.data.detail ?? ""], - }); - console.error(err); - }); - } - useEffect(() => { if (activeTab === "Executions") { getTransactionTable(currentFlowId, "union").then((data) => { @@ -86,16 +60,6 @@ export default function FlowLogsModal({ } }, [open, activeTab]); - const [nameLists, setNameList] = useState([]); - - useEffect(() => { - const tempNameList: string[] = []; - flows.forEach((flow: FlowType) => { - if ((flow.is_component ?? false) === false) tempNameList.push(flow.name); - }); - setNameList(tempNameList.filter((name) => name !== currentFlow!.name)); - }, [flows]); - return ( From 6096c8b73f26ebc054b791004463c13b70707516 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Wed, 5 Jun 2024 10:56:47 -0300 Subject: [PATCH 52/52] =?UTF-8?q?=F0=9F=94=A7=20(playwright.config.ts):=20?= =?UTF-8?q?reduce=20workers=20to=201=20for=20CI=20to=20avoid=20resource=20?= =?UTF-8?q?contention?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/frontend/playwright.config.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/playwright.config.ts b/src/frontend/playwright.config.ts index cf7d59472..eeb9497ae 100644 --- a/src/frontend/playwright.config.ts +++ b/src/frontend/playwright.config.ts @@ -21,7 +21,7 @@ export default defineConfig({ /* Retry on CI only */ retries: process.env.CI ? 2 : 0, /* Opt out of parallel tests on CI. */ - workers: 5, + workers: 1, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ timeout: 120 * 1000, // reporter: [