diff --git a/src/backend/base/langflow/api/v1/monitor.py b/src/backend/base/langflow/api/v1/monitor.py index f6c1fc4ac..224a26912 100644 --- a/src/backend/base/langflow/api/v1/monitor.py +++ b/src/backend/base/langflow/api/v1/monitor.py @@ -80,7 +80,8 @@ async def delete_messages( current_user: User = Depends(get_current_active_user), ): try: - session.exec(select(MessageTable).where(MessageTable.id.in_(message_ids))) # type: ignore + session.exec(delete(MessageTable).where(MessageTable.id.in_(message_ids))) # type: ignore + session.commit() except Exception as e: raise HTTPException(status_code=500, detail=str(e)) diff --git a/src/backend/base/langflow/components/helpers/CreateList.py b/src/backend/base/langflow/components/helpers/CreateList.py new file mode 100644 index 000000000..ea0130ecc --- /dev/null +++ b/src/backend/base/langflow/components/helpers/CreateList.py @@ -0,0 +1,28 @@ +from langflow.custom import Component +from langflow.inputs import StrInput +from langflow.schema import Data +from langflow.template import Output + + +class CreateListComponent(Component): + display_name = "Create List" + description = "Creates a list of texts." + icon = "list" + + inputs = [ + StrInput( + name="texts", + display_name="Texts", + info="Enter one or more texts.", + is_list=True, + ), + ] + + outputs = [ + Output(display_name="Data List", name="list", method="create_list"), + ] + + def create_list(self) -> list[Data]: + data = [Data(text=text) for text in self.texts] + self.status = data + return data diff --git a/src/backend/base/langflow/components/helpers/IDGenerator.py b/src/backend/base/langflow/components/helpers/IDGenerator.py index 72a944f71..7e63f870b 100644 --- a/src/backend/base/langflow/components/helpers/IDGenerator.py +++ b/src/backend/base/langflow/components/helpers/IDGenerator.py @@ -5,7 +5,7 @@ from langflow.custom import CustomComponent from langflow.schema.dotdict import dotdict -class UUIDGeneratorComponent(CustomComponent): +class IDGeneratorComponent(CustomComponent): display_name = "ID Generator" description = "Generates a unique ID." diff --git a/src/backend/base/langflow/components/helpers/StoreMessage.py b/src/backend/base/langflow/components/helpers/StoreMessage.py index 5d0abfbb9..ce1abbe29 100644 --- a/src/backend/base/langflow/components/helpers/StoreMessage.py +++ b/src/backend/base/langflow/components/helpers/StoreMessage.py @@ -1,22 +1,47 @@ -from langflow.custom import CustomComponent -from langflow.memory import get_messages, store_message +from langflow.custom import Component +from langflow.inputs import MessageInput, StrInput from langflow.schema.message import Message +from langflow.template import Output +from langflow.memory import get_messages, store_message -class StoreMessageComponent(CustomComponent): +class StoreMessageComponent(Component): display_name = "Store Message" - description = "Stores a chat message." + description = "Stores a chat message or text." + icon = "save" - def build_config(self): - return { - "message": {"display_name": "Message"}, - } + inputs = [ + MessageInput(name="message", display_name="Message", info="The chat message to be stored.", required=True), + StrInput( + name="sender", + display_name="Sender", + info="The sender of the message.", + value="AI", + advanced=True, + ), + StrInput( + name="sender_name", display_name="Sender Name", info="The name of the sender.", value="AI", advanced=True + ), + StrInput( + name="session_id", + display_name="Session ID", + info="The session ID of the chat.", + value="", + ), + ] + + outputs = [ + Output(display_name="Stored Messages", name="stored_messages", method="store_message"), + ] + + def store_message(self) -> Message: + message = self.message + + message.session_id = self.session_id or message.session_id + message.sender = self.sender or message.sender + message.sender_name = self.sender_name or message.sender_name - def build( - self, - message: Message, - ) -> Message: store_message(message, flow_id=self.graph.flow_id) - self.status = get_messages() - - return message + stored = get_messages(session_id=message.session_id, sender_name=message.sender_name, sender=message.sender) + self.status = stored + return stored diff --git a/src/backend/base/langflow/components/helpers/__init__.py b/src/backend/base/langflow/components/helpers/__init__.py index 1941e38b8..fcc9e83ee 100644 --- a/src/backend/base/langflow/components/helpers/__init__.py +++ b/src/backend/base/langflow/components/helpers/__init__.py @@ -1,22 +1,25 @@ from .CombineText import CombineTextComponent from .CustomComponent import CustomComponent from .FilterData import FilterDataComponent -from .IDGenerator import UUIDGeneratorComponent +from .IDGenerator import IDGeneratorComponent from .Memory import MemoryComponent from .MergeData import MergeDataComponent from .ParseData import ParseDataComponent from .SplitText import SplitTextComponent from .StoreMessage import StoreMessageComponent +from .CreateList import CreateListComponent __all__ = [ + "CreateListComponent", "CombineTextComponent", "CustomComponent", "FilterDataComponent", - "UUIDGeneratorComponent", + "IDGeneratorComponent", "MemoryComponent", "MergeDataComponent", "ParseDataComponent", "SplitTextComponent", "StoreMessageComponent", + "ListComponent", ] diff --git a/src/backend/base/langflow/components/prototypes/ConditionalRouter.py b/src/backend/base/langflow/components/prototypes/ConditionalRouter.py index 8305730ba..07c8103f0 100644 --- a/src/backend/base/langflow/components/prototypes/ConditionalRouter.py +++ b/src/backend/base/langflow/components/prototypes/ConditionalRouter.py @@ -66,21 +66,17 @@ class ConditionalRouterComponent(Component): def true_response(self) -> Message: result = self.evaluate_condition(self.input_text, self.match_text, self.operator, self.case_sensitive) if result: - self.stop("false_result") - response = self.message if self.message else self.input_text - self.status = response - return response + self.status = self.message + return self.message else: self.stop("true_result") - return Message() + return None def false_response(self) -> Message: result = self.evaluate_condition(self.input_text, self.match_text, self.operator, self.case_sensitive) if not result: - self.stop("true_result") - response = self.message if self.message else self.input_text - self.status = response - return response + self.status = self.message + return self.message else: self.stop("false_result") - return Message() + return None diff --git a/src/backend/base/langflow/components/prototypes/Pass.py b/src/backend/base/langflow/components/prototypes/Pass.py index 4e2f234c9..28e9ea524 100644 --- a/src/backend/base/langflow/components/prototypes/Pass.py +++ b/src/backend/base/langflow/components/prototypes/Pass.py @@ -1,31 +1,32 @@ -from typing import Union - -from langflow.custom import CustomComponent -from langflow.field_typing import Text -from langflow.schema import Data +from langflow.custom import Component +from langflow.io import MessageInput +from langflow.schema.message import Message +from langflow.template import Output -class PassComponent(CustomComponent): +class PassMessageComponent(Component): display_name = "Pass" - description = "A pass-through component that forwards the second input while ignoring the first, used for controlling workflow direction." - field_order = ["ignored_input", "forwarded_input"] - beta = True + description = "Forwards the input message, unchanged." + icon = "arrow-right" - def build_config(self) -> dict: - return { - "ignored_input": { - "display_name": "Ignored Input", - "info": "This input is ignored. It's used to control the flow in the graph.", - "input_types": ["Text", "Data"], - }, - "forwarded_input": { - "display_name": "Input", - "info": "This input is forwarded by the component.", - "input_types": ["Text", "Data"], - }, - } + inputs = [ + MessageInput( + name="input_message", + display_name="Input Message", + info="The message to be passed forward.", + ), + MessageInput( + name="ignored_message", + display_name="Ignored Message", + info="A second message to be ignored. Used as a workaround for continuity.", + advanced=True, + ), + ] - def build(self, ignored_input: Text, forwarded_input: Text) -> Union[Text, Data]: - # The ignored_input is not used in the logic, it's just there for graph flow control - self.status = forwarded_input - return forwarded_input + outputs = [ + Output(display_name="Output Message", name="output_message", method="pass_message"), + ] + + def pass_message(self) -> Message: + self.status = self.input_message + return self.input_message diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 18c2bbb6a..c71313b3b 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -26,7 +26,7 @@ import useFlowStore from "../../stores/flowStore"; import useFlowsManagerStore from "../../stores/flowsManagerStore"; import { useShortcutsStore } from "../../stores/shortcuts"; import { useTypesStore } from "../../stores/typesStore"; -import { VertexBuildTypeAPI } from "../../types/api"; +import { OutputFieldType, VertexBuildTypeAPI } from "../../types/api"; import { NodeDataType } from "../../types/flow"; import { handleKeyDown, scapedJSONStringfy } from "../../utils/reactflowUtils"; import { nodeColors, nodeIconsLucide } from "../../utils/styleUtils"; @@ -279,7 +279,7 @@ export default function GenericNode({ const shortcuts = useShortcutsStore((state) => state.shortcuts); - const renderOutputParameter = (output, idx) => { + const renderOutputParameter = (output: OutputFieldType, idx: number) => { return ( nodeColors[type] ?? nodeColors.unknown; diff --git a/src/frontend/src/CustomNodes/helpers/get-node-output-colors.ts b/src/frontend/src/CustomNodes/helpers/get-node-output-colors.ts index d846a304e..dcac3d5cc 100644 --- a/src/frontend/src/CustomNodes/helpers/get-node-output-colors.ts +++ b/src/frontend/src/CustomNodes/helpers/get-node-output-colors.ts @@ -1,6 +1,12 @@ +import { OutputFieldType } from "../../types/api"; +import { NodeDataType } from "../../types/flow"; import { nodeColors } from "../../utils/styleUtils"; -export function getNodeOutputColors(output, data, types): string[] { +export function getNodeOutputColors( + output: OutputFieldType, + data: NodeDataType, + types: { [char: string]: string }, +): string[] { // Helper function to get the color based on type const getColorByType = (type) => nodeColors[type] ?? nodeColors.unknown; diff --git a/src/frontend/src/CustomNodes/hooks/use-icons-status.tsx b/src/frontend/src/CustomNodes/hooks/use-icons-status.tsx index 155592b78..7a969b965 100644 --- a/src/frontend/src/CustomNodes/hooks/use-icons-status.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-icons-status.tsx @@ -9,7 +9,10 @@ const useIconStatus = ( buildStatus: BuildStatus | undefined, validationStatus: VertexBuildTypeAPI | null, ) => { - const conditionSuccess = validationStatus && validationStatus.valid; + const conditionSuccess = + !(!buildStatus || buildStatus === BuildStatus.TO_BUILD) && + validationStatus && + validationStatus.valid; const conditionError = buildStatus === BuildStatus.ERROR; const conditionInactive = buildStatus === BuildStatus.INACTIVE; diff --git a/src/frontend/src/components/ImageViewer/index.tsx b/src/frontend/src/components/ImageViewer/index.tsx index 9e7f091d2..6e2c340fd 100644 --- a/src/frontend/src/components/ImageViewer/index.tsx +++ b/src/frontend/src/components/ImageViewer/index.tsx @@ -6,7 +6,7 @@ import useAlertStore from "../../stores/alertStore"; import ForwardedIconComponent from "../genericIconComponent"; import { Separator } from "../ui/separator"; -export default function ImageViewer({ image }) { +export default function ImageViewer({ image }: { image: string }) { const viewerRef = useRef(null); const [errorDownloading, setErrordownloading] = useState(false); const setErrorList = useAlertStore((state) => state.setErrorData); diff --git a/src/frontend/src/components/horizontalScrollFadeComponent/index.tsx b/src/frontend/src/components/horizontalScrollFadeComponent/index.tsx index e0bf48917..db78daaaf 100644 --- a/src/frontend/src/components/horizontalScrollFadeComponent/index.tsx +++ b/src/frontend/src/components/horizontalScrollFadeComponent/index.tsx @@ -3,6 +3,9 @@ import { useEffect, useRef, useState } from "react"; export default function HorizontalScrollFadeComponent({ children, isFolder = true, +}: { + children: JSX.Element | JSX.Element[]; + isFolder?: boolean; }) { const scrollContainerRef = useRef(null); const fadeContainerRef = useRef(null); diff --git a/src/frontend/src/modals/BundleModal/hooks/submit-folder.tsx b/src/frontend/src/modals/BundleModal/hooks/submit-folder.tsx index cb12ca7cb..cd3c3a1e1 100644 --- a/src/frontend/src/modals/BundleModal/hooks/submit-folder.tsx +++ b/src/frontend/src/modals/BundleModal/hooks/submit-folder.tsx @@ -1,8 +1,12 @@ +import { FolderType } from "../../../pages/MainPage/entities"; import { addFolder, updateFolder } from "../../../pages/MainPage/services"; import useAlertStore from "../../../stores/alertStore"; import { useFolderStore } from "../../../stores/foldersStore"; -const useFolderSubmit = (setOpen, folderToEdit) => { +const useFolderSubmit = ( + setOpen: (a: boolean) => void, + folderToEdit: FolderType | null, +) => { const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const getFoldersApi = useFolderStore((state) => state.getFoldersApi); diff --git a/src/frontend/src/modals/foldersModal/hooks/submit-folder.tsx b/src/frontend/src/modals/foldersModal/hooks/submit-folder.tsx index 71a8ac944..a60f2a419 100644 --- a/src/frontend/src/modals/foldersModal/hooks/submit-folder.tsx +++ b/src/frontend/src/modals/foldersModal/hooks/submit-folder.tsx @@ -1,9 +1,13 @@ import { useNavigate } from "react-router-dom"; +import { FolderType } from "../../../pages/MainPage/entities"; import { addFolder, updateFolder } from "../../../pages/MainPage/services"; import useAlertStore from "../../../stores/alertStore"; import { useFolderStore } from "../../../stores/foldersStore"; -const useFolderSubmit = (setOpen, folderToEdit) => { +const useFolderSubmit = ( + setOpen: (a: boolean) => void, + folderToEdit: FolderType | null, +) => { const setSuccessData = useAlertStore((state) => state.setSuccessData); const setErrorData = useAlertStore((state) => state.setErrorData); const getFoldersApi = useFolderStore((state) => state.getFoldersApi); diff --git a/src/frontend/src/utils/buildUtils.ts b/src/frontend/src/utils/buildUtils.ts index f3ae642bc..4d0a81594 100644 --- a/src/frontend/src/utils/buildUtils.ts +++ b/src/frontend/src/utils/buildUtils.ts @@ -99,6 +99,10 @@ export async function updateVerticesOrder( const runId = orderResponse.data.run_id; const verticesToRun = orderResponse.data.vertices_to_run; + useFlowStore + .getState() + .updateBuildStatus(verticesToRun, BuildStatus.TO_BUILD); + const verticesIds = orderResponse.data.ids; useFlowStore.getState().updateVerticesBuild({ verticesLayers, diff --git a/tests/unit/test_helper_components.py b/tests/unit/test_helper_components.py index 9e9c4f63a..dc07c5835 100644 --- a/tests/unit/test_helper_components.py +++ b/tests/unit/test_helper_components.py @@ -31,7 +31,7 @@ from langflow.schema import Data def test_uuid_generator_component(): # Arrange - uuid_generator_component = helpers.UUIDGeneratorComponent() + uuid_generator_component = helpers.IDGeneratorComponent() uuid_generator_component.code = open(helpers.IDGenerator.__file__, "r").read() frontend_node, _ = build_custom_component_template(uuid_generator_component)