From 25a0d7674a4af17bbb9207f819493f364c8a801a Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 19 Nov 2024 15:17:32 -0300 Subject: [PATCH 01/45] fix: update import for compatibility, format starter projects, fix coroutine call (#4708) * Use `typing_extensions.override` for compatibility with older Python versions * Format starter projects * Fix async call in get_lf_version_from_pypi by adding 'await' --- .../base/langflow/components/models/aiml.py | 3 +- .../Custom Component Maker.json | 351 +++++++++++---- .../Instagram Copywriter.json | 426 +++++++++++++----- .../starter_projects/Market Research.json | 306 ++++++++++--- .../starter_projects/Research Agent.json | 404 +++++++++++++---- .../starter_projects/SaaS Pricing.json | 199 ++++++-- .../Sequential Tasks Agents .json | 10 +- .../starter_projects/Simple Agent .json | 199 ++++++-- .../Travel Planning Agents.json | 421 +++++++++++++---- .../starter_projects/Vector Store RAG.json | 12 +- .../YouTube Transcript Q&A.json | 204 +++++++-- .../base/langflow/services/store/utils.py | 2 +- 12 files changed, 1926 insertions(+), 611 deletions(-) diff --git a/src/backend/base/langflow/components/models/aiml.py b/src/backend/base/langflow/components/models/aiml.py index bce906a12539..c974f54011d9 100644 --- a/src/backend/base/langflow/components/models/aiml.py +++ b/src/backend/base/langflow/components/models/aiml.py @@ -1,7 +1,6 @@ -from typing import override - from langchain_openai import ChatOpenAI from pydantic.v1 import SecretStr +from typing_extensions import override from langflow.base.models.aiml_constants import AimlModels from langflow.base.models.model import LCModelComponent diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Custom Component Maker.json b/src/backend/base/langflow/initial_setup/starter_projects/Custom Component Maker.json index 8a38d1efe9cb..ec70e6a3303a 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Custom Component Maker.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Custom Component Maker.json @@ -9,12 +9,17 @@ "dataType": "Memory", "id": "Memory-tBe70", "name": "messages_text", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "CHAT_HISTORY", "id": "Prompt-WSv03", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -33,12 +38,17 @@ "dataType": "ChatInput", "id": "ChatInput-VUqPC", "name": "message", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "USER_INPUT", "id": "Prompt-WSv03", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -57,12 +67,16 @@ "dataType": "Prompt", "id": "Prompt-WSv03", "name": "prompt", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "AnthropicModel-laWKJ", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -80,12 +94,16 @@ "dataType": "AnthropicModel", "id": "AnthropicModel-laWKJ", "name": "text_output", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-XNaWv", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -103,12 +121,17 @@ "dataType": "URL", "id": "URL-9b1oo", "name": "text", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "EXAMPLE_COMPONENTS", "id": "Prompt-WSv03", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -126,12 +149,17 @@ "dataType": "URL", "id": "URL-HTi1a", "name": "text", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "BASE_COMPONENT_CODE", "id": "Prompt-WSv03", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -149,12 +177,17 @@ "dataType": "URL", "id": "URL-bqkBy", "name": "text", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "CUSTOM_COMPONENT_CODE", "id": "Prompt-WSv03", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -172,7 +205,9 @@ "display_name": "Chat Input", "id": "ChatInput-VUqPC", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -204,7 +239,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -217,7 +254,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -237,7 +276,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -317,7 +358,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as input.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -340,7 +383,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -356,7 +402,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -376,7 +424,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -412,7 +462,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -452,7 +504,10 @@ "display_name": "Chat Memory", "id": "Memory-tBe70", "node": { - "base_classes": ["Data", "Message"], + "base_classes": [ + "Data", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -482,7 +537,9 @@ "method": "retrieve_messages", "name": "messages", "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -491,7 +548,9 @@ "method": "retrieve_messages_as_text", "name": "messages_text", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -522,7 +581,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -557,7 +618,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -575,7 +639,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -591,7 +659,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -611,7 +681,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -631,7 +703,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -672,7 +746,9 @@ "display_name": "Prompt", "id": "Prompt-WSv03", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": { @@ -688,7 +764,9 @@ "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": ["template"], + "field_order": [ + "template" + ], "frozen": false, "icon": "prompts", "legacy": false, @@ -702,7 +780,9 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -716,7 +796,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -736,7 +819,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -756,7 +842,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -776,7 +865,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -796,7 +888,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -871,7 +966,9 @@ "display_name": "Chat Output", "id": "ChatOutput-XNaWv", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -902,7 +999,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -915,7 +1014,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -935,7 +1036,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -973,7 +1076,9 @@ "display_name": "Data Template", "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "data_template", @@ -993,7 +1098,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as output.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1014,7 +1121,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -1030,7 +1140,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -1050,7 +1162,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -1086,7 +1200,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -1181,7 +1297,10 @@ "data": { "id": "AnthropicModel-laWKJ", "node": { - "base_classes": ["LanguageModel", "Message"], + "base_classes": [ + "LanguageModel", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1215,7 +1334,9 @@ "name": "text_output", "required_inputs": [], "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" }, { @@ -1225,7 +1346,9 @@ "name": "model_output", "required_inputs": [], "selected": "LanguageModel", - "types": ["LanguageModel"], + "types": [ + "LanguageModel" + ], "value": "__UNDEFINED__" } ], @@ -1238,7 +1361,9 @@ "display_name": "Anthropic API Key", "dynamic": false, "info": "Your Anthropic API key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "anthropic_api_key", "password": true, @@ -1255,7 +1380,9 @@ "display_name": "Anthropic API URL", "dynamic": false, "info": "Endpoint of the Anthropic API. Defaults to 'https://api.anthropic.com' if not specified.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "anthropic_api_url", @@ -1284,7 +1411,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import LanguageModel\nfrom langflow.inputs.inputs import HandleInput\nfrom langflow.io import DropdownInput, FloatInput, IntInput, MessageTextInput, SecretStrInput\n\n\nclass AnthropicModelComponent(LCModelComponent):\n display_name = \"Anthropic\"\n description = \"Generate text using Anthropic Chat&Completion LLMs with prefill support.\"\n icon = \"Anthropic\"\n name = \"AnthropicModel\"\n\n inputs = [\n *LCModelComponent._base_inputs,\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n value=4096,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DropdownInput(\n name=\"model\",\n display_name=\"Model Name\",\n options=[\n \"claude-3-5-sonnet-20240620\",\n \"claude-3-opus-20240229\",\n \"claude-3-sonnet-20240229\",\n \"claude-3-haiku-20240307\",\n ],\n info=\"https://python.langchain.com/docs/integrations/chat/anthropic\",\n value=\"claude-3-5-sonnet-20240620\",\n ),\n SecretStrInput(name=\"anthropic_api_key\", display_name=\"Anthropic API Key\", info=\"Your Anthropic API key.\"),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n MessageTextInput(\n name=\"anthropic_api_url\",\n display_name=\"Anthropic API URL\",\n advanced=True,\n info=\"Endpoint of the Anthropic API. Defaults to 'https://api.anthropic.com' if not specified.\",\n ),\n MessageTextInput(\n name=\"prefill\", display_name=\"Prefill\", info=\"Prefill text to guide the model's response.\", advanced=True\n ),\n HandleInput(\n name=\"output_parser\",\n display_name=\"Output Parser\",\n info=\"The parser to use to parse the output of the model\",\n advanced=True,\n input_types=[\"OutputParser\"],\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n try:\n from langchain_anthropic.chat_models import ChatAnthropic\n except ImportError as e:\n msg = \"langchain_anthropic is not installed. Please install it with `pip install langchain_anthropic`.\"\n raise ImportError(msg) from e\n model = self.model\n anthropic_api_key = self.anthropic_api_key\n max_tokens = self.max_tokens\n temperature = self.temperature\n anthropic_api_url = self.anthropic_api_url or \"https://api.anthropic.com\"\n\n try:\n output = ChatAnthropic(\n model=model,\n anthropic_api_key=(SecretStr(anthropic_api_key).get_secret_value() if anthropic_api_key else None),\n max_tokens_to_sample=max_tokens,\n temperature=temperature,\n anthropic_api_url=anthropic_api_url,\n streaming=self.stream,\n )\n except Exception as e:\n msg = \"Could not connect to Anthropic API.\"\n raise ValueError(msg) from e\n\n return output\n\n def _get_exception_message(self, exception: Exception) -> str | None:\n \"\"\"Get a message from an Anthropic exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n try:\n from anthropic import BadRequestError\n except ImportError:\n return None\n if isinstance(exception, BadRequestError):\n message = exception.body.get(\"error\", {}).get(\"message\")\n if message:\n return message\n return None\n" + "value": "from pydantic.v1 import SecretStr\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.field_typing import LanguageModel\nfrom langflow.inputs.inputs import HandleInput\nfrom langflow.io import DropdownInput, FloatInput, IntInput, MessageTextInput, SecretStrInput\n\n\nclass AnthropicModelComponent(LCModelComponent):\n display_name = \"Anthropic\"\n description = \"Generate text using Anthropic Chat&Completion LLMs with prefill support.\"\n icon = \"Anthropic\"\n name = \"AnthropicModel\"\n\n inputs = [\n *LCModelComponent._base_inputs,\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n value=4096,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DropdownInput(\n name=\"model\",\n display_name=\"Model Name\",\n options=ANTHROPIC_MODELS,\n info=\"https://python.langchain.com/docs/integrations/chat/anthropic\",\n value=\"claude-3-5-sonnet-latest\",\n ),\n SecretStrInput(name=\"anthropic_api_key\", display_name=\"Anthropic API Key\", info=\"Your Anthropic API key.\"),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n MessageTextInput(\n name=\"anthropic_api_url\",\n display_name=\"Anthropic API URL\",\n advanced=True,\n info=\"Endpoint of the Anthropic API. Defaults to 'https://api.anthropic.com' if not specified.\",\n ),\n MessageTextInput(\n name=\"prefill\", display_name=\"Prefill\", info=\"Prefill text to guide the model's response.\", advanced=True\n ),\n HandleInput(\n name=\"output_parser\",\n display_name=\"Output Parser\",\n info=\"The parser to use to parse the output of the model\",\n advanced=True,\n input_types=[\"OutputParser\"],\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n try:\n from langchain_anthropic.chat_models import ChatAnthropic\n except ImportError as e:\n msg = \"langchain_anthropic is not installed. Please install it with `pip install langchain_anthropic`.\"\n raise ImportError(msg) from e\n model = self.model\n anthropic_api_key = self.anthropic_api_key\n max_tokens = self.max_tokens\n temperature = self.temperature\n anthropic_api_url = self.anthropic_api_url or \"https://api.anthropic.com\"\n\n try:\n output = ChatAnthropic(\n model=model,\n anthropic_api_key=(SecretStr(anthropic_api_key).get_secret_value() if anthropic_api_key else None),\n max_tokens_to_sample=max_tokens,\n temperature=temperature,\n anthropic_api_url=anthropic_api_url,\n streaming=self.stream,\n )\n except Exception as e:\n msg = \"Could not connect to Anthropic API.\"\n raise ValueError(msg) from e\n\n return output\n\n def _get_exception_message(self, exception: Exception) -> str | None:\n \"\"\"Get a message from an Anthropic exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n try:\n from anthropic import BadRequestError\n except ImportError:\n return None\n if isinstance(exception, BadRequestError):\n message = exception.body.get(\"error\", {}).get(\"message\")\n if message:\n return message\n return None\n" }, "input_value": { "_input_type": "MessageInput", @@ -1292,7 +1419,9 @@ "display_name": "Input", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1330,8 +1459,12 @@ "info": "https://python.langchain.com/docs/integrations/chat/anthropic", "name": "model", "options": [ + "claude-3-5-sonnet-latest", + "claude-3-5-haiku-latest", + "claude-3-opus-latest", "claude-3-5-sonnet-20240620", - "claude-3-opus-20240229", + "claude-3-5-sonnet-20241022", + "claude-3-5-haiku-20241022", "claude-3-sonnet-20240229", "claude-3-haiku-20240307" ], @@ -1349,7 +1482,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -1366,7 +1501,9 @@ "display_name": "Prefill", "dynamic": false, "info": "Prefill text to guide the model's response.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "prefill", @@ -1401,7 +1538,9 @@ "display_name": "System Message", "dynamic": false, "info": "System message to pass to the model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "system_message", @@ -1453,7 +1592,10 @@ "data": { "id": "URL-HTi1a", "node": { - "base_classes": ["Data", "Message"], + "base_classes": [ + "Data", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1461,7 +1603,10 @@ "display_name": "URL", "documentation": "", "edited": false, - "field_order": ["urls", "format"], + "field_order": [ + "urls", + "format" + ], "frozen": false, "icon": "layout-template", "legacy": false, @@ -1475,7 +1620,9 @@ "method": "fetch_content", "name": "data", "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -1484,7 +1631,9 @@ "method": "fetch_content_text", "name": "text", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1517,7 +1666,10 @@ "dynamic": false, "info": "Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.", "name": "format", - "options": ["Text", "Raw HTML"], + "options": [ + "Text", + "Raw HTML" + ], "placeholder": "", "required": false, "show": true, @@ -1533,7 +1685,9 @@ "display_name": "URLs", "dynamic": false, "info": "Enter one or more URLs, by clicking the '+' button.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": true, "load_from_db": false, "name": "urls", @@ -1573,7 +1727,10 @@ "data": { "id": "URL-9b1oo", "node": { - "base_classes": ["Data", "Message"], + "base_classes": [ + "Data", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1581,7 +1738,10 @@ "display_name": "URL", "documentation": "", "edited": false, - "field_order": ["urls", "format"], + "field_order": [ + "urls", + "format" + ], "frozen": false, "icon": "layout-template", "legacy": false, @@ -1595,7 +1755,9 @@ "method": "fetch_content", "name": "data", "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -1604,7 +1766,9 @@ "method": "fetch_content_text", "name": "text", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1637,7 +1801,10 @@ "dynamic": false, "info": "Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.", "name": "format", - "options": ["Text", "Raw HTML"], + "options": [ + "Text", + "Raw HTML" + ], "placeholder": "", "required": false, "show": true, @@ -1653,7 +1820,9 @@ "display_name": "URLs", "dynamic": false, "info": "Enter one or more URLs, by clicking the '+' button.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": true, "load_from_db": false, "name": "urls", @@ -1697,7 +1866,10 @@ "data": { "id": "URL-bqkBy", "node": { - "base_classes": ["Data", "Message"], + "base_classes": [ + "Data", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1705,7 +1877,10 @@ "display_name": "URL", "documentation": "", "edited": false, - "field_order": ["urls", "format"], + "field_order": [ + "urls", + "format" + ], "frozen": false, "icon": "layout-template", "legacy": false, @@ -1719,7 +1894,9 @@ "method": "fetch_content", "name": "data", "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -1728,7 +1905,9 @@ "method": "fetch_content_text", "name": "text", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1761,7 +1940,10 @@ "dynamic": false, "info": "Output Format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.", "name": "format", - "options": ["Text", "Raw HTML"], + "options": [ + "Text", + "Raw HTML" + ], "placeholder": "", "required": false, "show": true, @@ -1777,7 +1959,9 @@ "display_name": "URLs", "dynamic": false, "info": "Enter one or more URLs, by clicking the '+' button.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": true, "load_from_db": false, "name": "urls", @@ -1822,11 +2006,14 @@ }, "description": "Generates well-structured code for custom components following Langflow's specifications.", "endpoint_name": null, + "gradient": "1", "icon": "SquareCode", "id": "aabe02e5-255f-447a-8ec2-be6f43955a43", - "gradient": "1", "is_component": false, "last_tested_version": "1.0.19.post2", "name": "Custom Component Generator", - "tags": ["coding", "web-scraping"] -} + "tags": [ + "coding", + "web-scraping" + ] +} \ No newline at end of file diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json b/src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json index 194c14838c8f..123ed786b7fc 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json @@ -9,12 +9,17 @@ "dataType": "TextInput", "id": "TextInput-VURDN", "name": "text", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "guidelines", "id": "Prompt-vFWlB", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -32,12 +37,17 @@ "dataType": "OpenAIModel", "id": "OpenAIModel-e0DLW", "name": "text_output", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "post", "id": "Prompt-0R6oq", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -55,12 +65,16 @@ "dataType": "Prompt", "id": "Prompt-vFWlB", "name": "prompt", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-e0DLW", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -78,12 +92,18 @@ "dataType": "TavilyAISearch", "id": "TavilyAISearch-AN1Hv", "name": "api_build_tool", - "output_types": ["Tool"] + "output_types": [ + "Tool" + ] }, "targetHandle": { "fieldName": "tools", "id": "Agent-9Wf58", - "inputTypes": ["Tool", "BaseTool", "StructuredTool"], + "inputTypes": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "type": "other" } }, @@ -102,12 +122,16 @@ "dataType": "ChatInput", "id": "ChatInput-RN2Gt", "name": "message", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Agent-9Wf58", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -125,12 +149,17 @@ "dataType": "Agent", "id": "Agent-9Wf58", "name": "response", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "context", "id": "Prompt-vFWlB", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -148,12 +177,16 @@ "dataType": "Prompt", "id": "Prompt-0R6oq", "name": "prompt", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-XHfFc", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -171,12 +204,17 @@ "dataType": "OpenAIModel", "id": "OpenAIModel-e0DLW", "name": "text_output", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "post", "id": "Prompt-Z1RBN", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -194,12 +232,17 @@ "dataType": "OpenAIModel", "id": "OpenAIModel-XHfFc", "name": "text_output", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "image_description", "id": "Prompt-Z1RBN", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -217,12 +260,16 @@ "dataType": "Prompt", "id": "Prompt-Z1RBN", "name": "prompt", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-1gPMj", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -238,7 +285,9 @@ "data": { "id": "ChatInput-RN2Gt", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -270,7 +319,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -283,7 +334,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -302,7 +355,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -381,7 +436,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as input.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -403,7 +460,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -418,7 +478,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -437,7 +499,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -472,7 +536,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -510,17 +576,24 @@ "display_name": "Prompt", "id": "Prompt-vFWlB", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": { - "template": ["context", "guidelines"] + "template": [ + "context", + "guidelines" + ] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": ["template"], + "field_order": [ + "template" + ], "frozen": false, "icon": "prompts", "legacy": false, @@ -534,7 +607,9 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -567,7 +642,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -587,7 +665,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -641,7 +722,9 @@ "data": { "id": "TextInput-VURDN", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -649,7 +732,9 @@ "display_name": "Text Input", "documentation": "", "edited": false, - "field_order": ["input_value"], + "field_order": [ + "input_value" + ], "frozen": false, "icon": "type", "legacy": false, @@ -663,7 +748,9 @@ "method": "text_response", "name": "text", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -694,7 +781,9 @@ "display_name": "Text", "dynamic": false, "info": "Text to be passed as input.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -733,7 +822,10 @@ "display_name": "OpenAI", "id": "OpenAIModel-e0DLW", "node": { - "base_classes": ["LanguageModel", "Message"], + "base_classes": [ + "LanguageModel", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -770,7 +862,9 @@ "name": "text_output", "required_inputs": [], "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" }, { @@ -780,7 +874,9 @@ "name": "model_output", "required_inputs": [], "selected": "LanguageModel", - "types": ["LanguageModel"], + "types": [ + "LanguageModel" + ], "value": "__UNDEFINED__" } ], @@ -793,7 +889,9 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "api_key", "password": true, @@ -828,7 +926,9 @@ "display_name": "Input", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -944,7 +1044,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -1009,7 +1111,9 @@ "display_name": "System Message", "dynamic": false, "info": "System message to pass to the model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "system_message", @@ -1065,17 +1169,23 @@ "display_name": "Prompt", "id": "Prompt-0R6oq", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": { - "template": ["post"] + "template": [ + "post" + ] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": ["template"], + "field_order": [ + "template" + ], "frozen": false, "icon": "prompts", "legacy": false, @@ -1089,7 +1199,9 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1122,7 +1234,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1178,7 +1293,9 @@ "display_name": "Chat Output", "id": "ChatOutput-1gPMj", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1209,7 +1326,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1222,7 +1341,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -1242,7 +1363,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -1280,7 +1403,9 @@ "display_name": "Data Template", "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "data_template", @@ -1300,7 +1425,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as output.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1321,7 +1448,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -1337,7 +1467,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -1357,7 +1489,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -1393,7 +1527,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -1433,7 +1569,9 @@ "display_name": "Agent", "id": "Agent-9Wf58", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1481,7 +1619,9 @@ "method": "message_response", "name": "response", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1510,7 +1650,9 @@ "display_name": "Agent Description", "dynamic": false, "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1559,8 +1701,10 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], - "load_from_db": true, + "input_types": [ + "Message" + ], + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -1610,7 +1754,9 @@ "display_name": "Input", "dynamic": false, "info": "The input provided by the user for the agent to process.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1684,7 +1830,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -1778,7 +1926,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -1794,7 +1945,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -1845,7 +1998,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -1861,7 +2018,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -1881,7 +2040,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -1901,7 +2062,9 @@ "display_name": "Agent Instructions", "dynamic": false, "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1938,7 +2101,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1959,7 +2124,11 @@ "display_name": "Tools", "dynamic": false, "info": "These are the tools that the agent can use to help with tasks.", - "input_types": ["Tool", "BaseTool", "StructuredTool"], + "input_types": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "list": true, "name": "tools", "placeholder": "", @@ -2012,7 +2181,10 @@ "display_name": "Tavily AI Search", "id": "TavilyAISearch-AN1Hv", "node": { - "base_classes": ["Data", "Tool"], + "base_classes": [ + "Data", + "Tool" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -2041,9 +2213,13 @@ "display_name": "Data", "method": "run_model", "name": "api_run_model", - "required_inputs": ["api_key"], + "required_inputs": [ + "api_key" + ], "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -2051,9 +2227,13 @@ "display_name": "Tool", "method": "build_tool", "name": "api_build_tool", - "required_inputs": ["api_key"], + "required_inputs": [ + "api_key" + ], "selected": "Tool", - "types": ["Tool"], + "types": [ + "Tool" + ], "value": "__UNDEFINED__" } ], @@ -2066,7 +2246,9 @@ "display_name": "Tavily API Key", "dynamic": false, "info": "Your Tavily API Key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "api_key", "password": true, @@ -2149,7 +2331,9 @@ "display_name": "Search Query", "dynamic": false, "info": "The search query you want to execute with Tavily.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "query", @@ -2172,7 +2356,10 @@ "info": "The depth of the search.", "load_from_db": false, "name": "search_depth", - "options": ["basic", "advanced"], + "options": [ + "basic", + "advanced" + ], "placeholder": "", "required": false, "show": true, @@ -2191,7 +2378,10 @@ "info": "The category of the search.", "load_from_db": false, "name": "topic", - "options": ["general", "news"], + "options": [ + "general", + "news" + ], "placeholder": "", "required": false, "show": true, @@ -2227,7 +2417,10 @@ "display_name": "OpenAI", "id": "OpenAIModel-XHfFc", "node": { - "base_classes": ["LanguageModel", "Message"], + "base_classes": [ + "LanguageModel", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -2264,7 +2457,9 @@ "name": "text_output", "required_inputs": [], "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" }, { @@ -2274,7 +2469,9 @@ "name": "model_output", "required_inputs": [], "selected": "LanguageModel", - "types": ["LanguageModel"], + "types": [ + "LanguageModel" + ], "value": "__UNDEFINED__" } ], @@ -2287,7 +2484,9 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "api_key", "password": true, @@ -2322,7 +2521,9 @@ "display_name": "Input", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -2438,7 +2639,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -2503,7 +2706,9 @@ "display_name": "System Message", "dynamic": false, "info": "System message to pass to the model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "system_message", @@ -2559,17 +2764,24 @@ "display_name": "Prompt", "id": "Prompt-Z1RBN", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": { - "template": ["post", "image_description"] + "template": [ + "post", + "image_description" + ] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": ["template"], + "field_order": [ + "template" + ], "frozen": false, "icon": "prompts", "legacy": false, @@ -2583,7 +2795,9 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -2616,7 +2830,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2636,7 +2853,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2841,11 +3061,15 @@ }, "description": " Create engaging Instagram posts with AI-generated content and image prompts, streamlining social media content creation.", "endpoint_name": null, + "gradient": "0", "icon": "InstagramIcon", "id": "4bb309e6-42b4-4565-b960-8bd0f7e431f2", - "gradient": "0", "is_component": false, "last_tested_version": "1.0.19.post2", "name": "Instagram Copywriter", - "tags": ["content-generation", "chatbots", "agents"] -} + "tags": [ + "content-generation", + "chatbots", + "agents" + ] +} \ No newline at end of file diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json b/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json index 01d8fa9e722c..7a709987328e 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Market Research.json @@ -9,12 +9,16 @@ "dataType": "OpenAIModel", "id": "OpenAIModel-1WzgM", "name": "model_output", - "output_types": ["LanguageModel"] + "output_types": [ + "LanguageModel" + ] }, "targetHandle": { "fieldName": "llm", "id": "StructuredOutputComponent-421WY", - "inputTypes": ["LanguageModel"], + "inputTypes": [ + "LanguageModel" + ], "type": "other" } }, @@ -33,12 +37,16 @@ "dataType": "StructuredOutputComponent", "id": "StructuredOutputComponent-421WY", "name": "structured_output", - "output_types": ["Data"] + "output_types": [ + "Data" + ] }, "targetHandle": { "fieldName": "data", "id": "ParseData-rO6Qs", - "inputTypes": ["Data"], + "inputTypes": [ + "Data" + ], "type": "other" } }, @@ -57,12 +65,16 @@ "dataType": "ParseData", "id": "ParseData-rO6Qs", "name": "text", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-hBRXA", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -81,12 +93,18 @@ "dataType": "TavilyAISearch", "id": "TavilyAISearch-ghguc", "name": "api_build_tool", - "output_types": ["Tool"] + "output_types": [ + "Tool" + ] }, "targetHandle": { "fieldName": "tools", "id": "Agent-QSS16", - "inputTypes": ["Tool", "BaseTool", "StructuredTool"], + "inputTypes": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "type": "other" } }, @@ -105,12 +123,16 @@ "dataType": "ChatInput", "id": "ChatInput-1iaFN", "name": "message", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Agent-QSS16", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -129,12 +151,16 @@ "dataType": "Agent", "id": "Agent-QSS16", "name": "response", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "StructuredOutputComponent-421WY", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -153,7 +179,9 @@ "display_name": "Chat Input", "id": "ChatInput-1iaFN", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -185,7 +213,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -198,7 +228,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -217,7 +249,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -296,7 +330,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as input.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -318,7 +354,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -333,7 +372,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -352,7 +393,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -387,7 +430,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -425,7 +470,9 @@ "display_name": "Chat Output", "id": "ChatOutput-hBRXA", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -457,7 +504,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -470,7 +519,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -490,7 +541,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -528,7 +581,9 @@ "display_name": "Data Template", "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "data_template", @@ -548,7 +603,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as output.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -569,7 +626,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -585,7 +645,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -605,7 +667,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -641,7 +705,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -681,7 +747,10 @@ "display_name": "Tavily AI Search", "id": "TavilyAISearch-ghguc", "node": { - "base_classes": ["Data", "Tool"], + "base_classes": [ + "Data", + "Tool" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -710,9 +779,13 @@ "display_name": "Data", "method": "run_model", "name": "api_run_model", - "required_inputs": ["api_key"], + "required_inputs": [ + "api_key" + ], "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -720,9 +793,13 @@ "display_name": "Tool", "method": "build_tool", "name": "api_build_tool", - "required_inputs": ["api_key"], + "required_inputs": [ + "api_key" + ], "selected": "Tool", - "types": ["Tool"], + "types": [ + "Tool" + ], "value": "__UNDEFINED__" } ], @@ -735,7 +812,9 @@ "display_name": "Tavily API Key", "dynamic": false, "info": "Your Tavily API Key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "api_key", "password": true, @@ -818,7 +897,9 @@ "display_name": "Search Query", "dynamic": false, "info": "The search query you want to execute with Tavily.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "query", @@ -841,7 +922,10 @@ "info": "The depth of the search.", "load_from_db": false, "name": "search_depth", - "options": ["basic", "advanced"], + "options": [ + "basic", + "advanced" + ], "placeholder": "", "required": false, "show": true, @@ -860,7 +944,10 @@ "info": "The category of the search.", "load_from_db": false, "name": "topic", - "options": ["general", "news"], + "options": [ + "general", + "news" + ], "placeholder": "", "required": false, "show": true, @@ -995,7 +1082,9 @@ "display_name": "Structured Output", "id": "StructuredOutputComponent-421WY", "node": { - "base_classes": ["Data"], + "base_classes": [ + "Data" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1023,7 +1112,9 @@ "method": "build_structured_output", "name": "structured_output", "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" } ], @@ -1054,7 +1145,9 @@ "display_name": "Input message", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1074,7 +1167,9 @@ "display_name": "Language Model", "dynamic": false, "info": "The language model to use to generate the structured output.", - "input_types": ["LanguageModel"], + "input_types": [ + "LanguageModel" + ], "list": false, "name": "llm", "placeholder": "", @@ -1264,7 +1359,10 @@ "display_name": "OpenAI", "id": "OpenAIModel-1WzgM", "node": { - "base_classes": ["LanguageModel", "Message"], + "base_classes": [ + "LanguageModel", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1301,7 +1399,9 @@ "name": "text_output", "required_inputs": [], "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" }, { @@ -1311,7 +1411,9 @@ "name": "model_output", "required_inputs": [], "selected": "LanguageModel", - "types": ["LanguageModel"], + "types": [ + "LanguageModel" + ], "value": "__UNDEFINED__" } ], @@ -1324,7 +1426,9 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "api_key", "password": true, @@ -1359,7 +1463,9 @@ "display_name": "Input", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1475,7 +1581,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -1540,7 +1648,9 @@ "display_name": "System Message", "dynamic": false, "info": "System message to pass to the model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "system_message", @@ -1594,7 +1704,9 @@ "data": { "id": "ParseData-rO6Qs", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "category": "helpers", "conditional_paths": [], @@ -1603,7 +1715,11 @@ "display_name": "Parse Data", "documentation": "", "edited": false, - "field_order": ["data", "template", "sep"], + "field_order": [ + "data", + "template", + "sep" + ], "frozen": false, "icon": "braces", "key": "ParseData", @@ -1618,7 +1734,9 @@ "method": "parse_data", "name": "text", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1649,7 +1767,9 @@ "display_name": "Data", "dynamic": false, "info": "The data to convert to text.", - "input_types": ["Data"], + "input_types": [ + "Data" + ], "list": false, "name": "data", "placeholder": "", @@ -1684,7 +1804,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {data} or any other key in the Data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1723,7 +1845,9 @@ "display_name": "Agent", "id": "Agent-QSS16", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1772,7 +1896,9 @@ "method": "message_response", "name": "response", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1801,7 +1927,9 @@ "display_name": "Agent Description", "dynamic": false, "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1850,8 +1978,10 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], - "load_from_db": true, + "input_types": [ + "Message" + ], + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -1901,7 +2031,9 @@ "display_name": "Input", "dynamic": false, "info": "The input provided by the user for the agent to process.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1975,7 +2107,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -2069,7 +2203,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -2085,7 +2222,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -2136,7 +2275,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -2152,7 +2295,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -2172,7 +2317,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -2192,7 +2339,9 @@ "display_name": "Agent Instructions", "dynamic": false, "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2229,7 +2378,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2250,7 +2401,11 @@ "display_name": "Tools", "dynamic": false, "info": "These are the tools that the agent can use to help with tasks.", - "input_types": ["Tool", "BaseTool", "StructuredTool"], + "input_types": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "list": true, "name": "tools", "placeholder": "", @@ -2330,11 +2485,14 @@ }, "description": "Researches companies, extracts key business data, and presents structured information for efficient analysis. ", "endpoint_name": null, + "gradient": "1", "icon": "PieChart", "id": "153a05e5-86bd-4de8-b159-2cb4f9f94de5", "is_component": false, - "gradient": "1", "last_tested_version": "1.0.19.post2", "name": "Market Research", - "tags": ["assistants", "agents"] -} + "tags": [ + "assistants", + "agents" + ] +} \ No newline at end of file diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json index 10a33260dcb7..c64653b2de04 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json @@ -9,12 +9,17 @@ "dataType": "OpenAIModel", "id": "OpenAIModel-Rc3MO", "name": "text_output", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "previous_response", "id": "Prompt-u7GZR", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -33,12 +38,16 @@ "dataType": "Prompt", "id": "Prompt-yDDjW", "name": "prompt", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-zhgF5", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -57,12 +66,17 @@ "dataType": "ChatInput", "id": "ChatInput-Mzp4f", "name": "message", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Prompt-yDDjW", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -81,12 +95,16 @@ "dataType": "OpenAIModel", "id": "OpenAIModel-zhgF5", "name": "text_output", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-mWv8X", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -105,12 +123,18 @@ "dataType": "TavilyAISearch", "id": "TavilyAISearch-rI4aD", "name": "api_build_tool", - "output_types": ["Tool"] + "output_types": [ + "Tool" + ] }, "targetHandle": { "fieldName": "tools", "id": "Agent-9E8IU", - "inputTypes": ["Tool", "BaseTool", "StructuredTool"], + "inputTypes": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "type": "other" } }, @@ -129,12 +153,16 @@ "dataType": "Prompt", "id": "Prompt-u7GZR", "name": "prompt", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Agent-9E8IU", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -153,12 +181,17 @@ "dataType": "Agent", "id": "Agent-9E8IU", "name": "response", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "search_results", "id": "Prompt-yDDjW", - "inputTypes": ["Message", "Text"], + "inputTypes": [ + "Message", + "Text" + ], "type": "str" } }, @@ -177,12 +210,16 @@ "dataType": "Prompt", "id": "Prompt-T4lL6", "name": "prompt", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "system_message", "id": "OpenAIModel-Rc3MO", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -200,12 +237,16 @@ "dataType": "ChatInput", "id": "ChatInput-Mzp4f", "name": "message", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "OpenAIModel-Rc3MO", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -223,12 +264,16 @@ "dataType": "Prompt", "id": "Prompt-f4xQ5", "name": "prompt", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "system_message", "id": "OpenAIModel-zhgF5", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -246,17 +291,23 @@ "display_name": "Prompt", "id": "Prompt-u7GZR", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": { - "template": ["previous_response"] + "template": [ + "previous_response" + ] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": ["template"], + "field_order": [ + "template" + ], "frozen": false, "icon": "prompts", "legacy": false, @@ -270,7 +321,9 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -303,7 +356,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -357,7 +413,9 @@ "data": { "id": "ChatInput-Mzp4f", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "category": "inputs", "conditional_paths": [], @@ -391,7 +449,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -404,7 +464,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -423,7 +485,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -502,7 +566,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as input.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -524,7 +590,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -539,7 +608,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -558,7 +629,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -593,7 +666,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -631,7 +706,9 @@ "display_name": "Chat Output", "id": "ChatOutput-mWv8X", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -663,7 +740,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -676,7 +755,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -696,7 +777,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -734,7 +817,9 @@ "display_name": "Data Template", "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "data_template", @@ -754,7 +839,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as output.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -775,7 +862,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -791,7 +881,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -811,7 +903,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -847,7 +941,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -887,17 +983,24 @@ "display_name": "Prompt", "id": "Prompt-yDDjW", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": { - "template": ["search_results", "input_value"] + "template": [ + "search_results", + "input_value" + ] }, "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": ["template"], + "field_order": [ + "template" + ], "frozen": false, "icon": "prompts", "legacy": false, @@ -911,7 +1014,9 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -944,7 +1049,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -964,7 +1072,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1020,7 +1131,10 @@ "display_name": "Tavily AI Search", "id": "TavilyAISearch-rI4aD", "node": { - "base_classes": ["Data", "Tool"], + "base_classes": [ + "Data", + "Tool" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1049,9 +1163,13 @@ "display_name": "Data", "method": "run_model", "name": "api_run_model", - "required_inputs": ["api_key"], + "required_inputs": [ + "api_key" + ], "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -1059,9 +1177,13 @@ "display_name": "Tool", "method": "build_tool", "name": "api_build_tool", - "required_inputs": ["api_key"], + "required_inputs": [ + "api_key" + ], "selected": "Tool", - "types": ["Tool"], + "types": [ + "Tool" + ], "value": "__UNDEFINED__" } ], @@ -1074,7 +1196,9 @@ "display_name": "Tavily API Key", "dynamic": false, "info": "Your Tavily API Key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "api_key", "password": true, @@ -1157,7 +1281,9 @@ "display_name": "Search Query", "dynamic": false, "info": "The search query you want to execute with Tavily.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "query", @@ -1180,7 +1306,10 @@ "info": "The depth of the search.", "load_from_db": false, "name": "search_depth", - "options": ["basic", "advanced"], + "options": [ + "basic", + "advanced" + ], "placeholder": "", "required": false, "show": true, @@ -1199,7 +1328,10 @@ "info": "The category of the search.", "load_from_db": false, "name": "topic", - "options": ["general", "news"], + "options": [ + "general", + "news" + ], "placeholder": "", "required": false, "show": true, @@ -1235,7 +1367,10 @@ "display_name": "OpenAI", "id": "OpenAIModel-Rc3MO", "node": { - "base_classes": ["LanguageModel", "Message"], + "base_classes": [ + "LanguageModel", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1272,7 +1407,9 @@ "name": "text_output", "required_inputs": [], "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" }, { @@ -1282,7 +1419,9 @@ "name": "model_output", "required_inputs": [], "selected": "LanguageModel", - "types": ["LanguageModel"], + "types": [ + "LanguageModel" + ], "value": "__UNDEFINED__" } ], @@ -1295,7 +1434,9 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "api_key", "password": true, @@ -1330,7 +1471,9 @@ "display_name": "Input", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1446,7 +1589,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -1511,7 +1656,9 @@ "display_name": "System Message", "dynamic": false, "info": "System message to pass to the model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "system_message", @@ -1567,7 +1714,10 @@ "display_name": "OpenAI", "id": "OpenAIModel-zhgF5", "node": { - "base_classes": ["LanguageModel", "Message"], + "base_classes": [ + "LanguageModel", + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1604,7 +1754,9 @@ "name": "text_output", "required_inputs": [], "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" }, { @@ -1614,7 +1766,9 @@ "name": "model_output", "required_inputs": [], "selected": "LanguageModel", - "types": ["LanguageModel"], + "types": [ + "LanguageModel" + ], "value": "__UNDEFINED__" } ], @@ -1627,7 +1781,9 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "api_key", "password": true, @@ -1662,7 +1818,9 @@ "display_name": "Input", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1778,7 +1936,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -1843,7 +2003,9 @@ "display_name": "System Message", "dynamic": false, "info": "System message to pass to the model.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "system_message", @@ -1932,7 +2094,9 @@ "display_name": "Agent", "id": "Agent-9E8IU", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1981,7 +2145,9 @@ "method": "message_response", "name": "response", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -2010,7 +2176,9 @@ "display_name": "Agent Description", "dynamic": false, "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2059,8 +2227,10 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], - "load_from_db": true, + "input_types": [ + "Message" + ], + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -2110,7 +2280,9 @@ "display_name": "Input", "dynamic": false, "info": "The input provided by the user for the agent to process.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -2184,7 +2356,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -2278,7 +2452,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -2294,7 +2471,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -2345,7 +2524,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -2361,7 +2544,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -2381,7 +2566,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -2401,7 +2588,9 @@ "display_name": "Agent Instructions", "dynamic": false, "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2438,7 +2627,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2459,7 +2650,11 @@ "display_name": "Tools", "dynamic": false, "info": "These are the tools that the agent can use to help with tasks.", - "input_types": ["Tool", "BaseTool", "StructuredTool"], + "input_types": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "list": true, "name": "tools", "placeholder": "", @@ -2512,7 +2707,9 @@ "display_name": "Prompt", "id": "Prompt-T4lL6", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": { @@ -2522,7 +2719,9 @@ "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": ["template"], + "field_order": [ + "template" + ], "frozen": false, "icon": "prompts", "legacy": false, @@ -2536,7 +2735,9 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -2605,7 +2806,9 @@ "display_name": "Prompt", "id": "Prompt-f4xQ5", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": { @@ -2615,7 +2818,9 @@ "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": ["template"], + "field_order": [ + "template" + ], "frozen": false, "icon": "prompts", "legacy": false, @@ -2629,7 +2834,9 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -2729,11 +2936,14 @@ }, "description": "Agent that generates focused plans, conducts web searches, and synthesizes findings into comprehensive reports.", "endpoint_name": null, + "gradient": "5", "icon": "TextSearchIcon", "id": "67b16861-1344-465b-963a-c1c338623438", - "gradient": "5", "is_component": false, "last_tested_version": "1.0.19.post2", "name": "Research Agent", - "tags": ["assistants", "agents"] -} + "tags": [ + "assistants", + "agents" + ] +} \ No newline at end of file diff --git a/src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json b/src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json index 9ad76c5b2e29..8030dcbd5373 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json @@ -9,12 +9,18 @@ "dataType": "CalculatorTool", "id": "CalculatorTool-DF8xQ", "name": "api_build_tool", - "output_types": ["Tool"] + "output_types": [ + "Tool" + ] }, "targetHandle": { "fieldName": "tools", "id": "Agent-5e01q", - "inputTypes": ["Tool", "BaseTool", "StructuredTool"], + "inputTypes": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "type": "other" } }, @@ -32,12 +38,16 @@ "dataType": "Agent", "id": "Agent-5e01q", "name": "response", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-s1eJK", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -55,12 +65,16 @@ "dataType": "Prompt", "id": "Prompt-KkcsZ", "name": "prompt", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Agent-5e01q", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -78,7 +92,9 @@ "display_name": "Prompt", "id": "Prompt-KkcsZ", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": { @@ -94,7 +110,9 @@ "display_name": "Prompt", "documentation": "", "edited": false, - "field_order": ["template"], + "field_order": [ + "template" + ], "frozen": false, "icon": "prompts", "legacy": false, @@ -108,7 +126,9 @@ "method": "build_prompt", "name": "prompt", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -141,7 +161,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -161,7 +184,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -181,7 +207,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -201,7 +230,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -221,7 +253,10 @@ "fileTypes": [], "file_path": "", "info": "", - "input_types": ["Message", "Text"], + "input_types": [ + "Message", + "Text" + ], "list": false, "load_from_db": false, "multiline": true, @@ -277,7 +312,9 @@ "display_name": "Chat Output", "id": "ChatOutput-s1eJK", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -309,7 +346,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -322,7 +361,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -342,7 +383,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -380,7 +423,9 @@ "display_name": "Data Template", "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "data_template", @@ -400,7 +445,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as output.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -421,7 +468,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -437,7 +487,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -457,7 +509,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -493,7 +547,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -564,7 +620,9 @@ "display_name": "Agent", "id": "Agent-5e01q", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -613,7 +671,9 @@ "method": "message_response", "name": "response", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -642,7 +702,9 @@ "display_name": "Agent Description", "dynamic": false, "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -691,8 +753,10 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], - "load_from_db": true, + "input_types": [ + "Message" + ], + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -742,7 +806,9 @@ "display_name": "Input", "dynamic": false, "info": "The input provided by the user for the agent to process.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -816,7 +882,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -910,7 +978,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -926,7 +997,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -977,7 +1050,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -993,7 +1070,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -1013,7 +1092,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -1033,7 +1114,9 @@ "display_name": "Agent Instructions", "dynamic": false, "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1070,7 +1153,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1091,7 +1176,11 @@ "display_name": "Tools", "dynamic": false, "info": "These are the tools that the agent can use to help with tasks.", - "input_types": ["Tool", "BaseTool", "StructuredTool"], + "input_types": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "list": true, "name": "tools", "placeholder": "", @@ -1142,7 +1231,10 @@ "data": { "id": "CalculatorTool-DF8xQ", "node": { - "base_classes": ["Data", "Tool"], + "base_classes": [ + "Data", + "Tool" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1150,7 +1242,9 @@ "display_name": "Calculator", "documentation": "", "edited": false, - "field_order": ["expression"], + "field_order": [ + "expression" + ], "frozen": false, "icon": "calculator", "legacy": false, @@ -1165,7 +1259,9 @@ "name": "api_run_model", "required_inputs": [], "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -1175,7 +1271,9 @@ "name": "api_build_tool", "required_inputs": [], "selected": "Tool", - "types": ["Tool"], + "types": [ + "Tool" + ], "value": "__UNDEFINED__" } ], @@ -1198,7 +1296,7 @@ "show": true, "title_case": false, "type": "code", - "value": "import ast\nimport operator\n\nfrom langchain.tools import StructuredTool\nfrom langchain_core.tools import ToolException\nfrom loguru import logger\nfrom pydantic import BaseModel, Field\n\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.field_typing import Tool\nfrom langflow.inputs import MessageTextInput\nfrom langflow.schema import Data\n\n\nclass CalculatorToolComponent(LCToolComponent):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n name = \"CalculatorTool\"\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n tool_mode=True,\n ),\n ]\n\n class CalculatorToolSchema(BaseModel):\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\n\n def run_model(self) -> list[Data]:\n return self._evaluate_expression(self.expression)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"calculator\",\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\n func=self._eval_expr_with_error,\n args_schema=self.CalculatorToolSchema,\n )\n\n def _eval_expr(self, node):\n # Define the allowed operators\n operators = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n if isinstance(node, ast.Num):\n return node.n\n if isinstance(node, ast.BinOp):\n return operators[type(node.op)](self._eval_expr(node.left), self._eval_expr(node.right))\n if isinstance(node, ast.UnaryOp):\n return operators[type(node.op)](self._eval_expr(node.operand))\n if isinstance(node, ast.Call):\n msg = (\n \"Function calls like sqrt(), sin(), cos() etc. are not supported. \"\n \"Only basic arithmetic operations (+, -, *, /, **) are allowed.\"\n )\n raise TypeError(msg)\n msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(msg)\n\n def _eval_expr_with_error(self, expression: str) -> list[Data]:\n try:\n return self._evaluate_expression(expression)\n except Exception as e:\n raise ToolException(str(e)) from e\n\n def _evaluate_expression(self, expression: str) -> list[Data]:\n try:\n # Parse the expression and evaluate it\n tree = ast.parse(expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n # Format the result to a reasonable number of decimal places\n formatted_result = f\"{result:.6f}\".rstrip(\"0\").rstrip(\".\")\n\n self.status = formatted_result\n return [Data(data={\"result\": formatted_result})]\n\n except (SyntaxError, TypeError, KeyError) as e:\n error_message = f\"Invalid expression: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except Exception as e: # noqa: BLE001\n logger.opt(exception=True).debug(\"Error evaluating expression\")\n error_message = f\"Error: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n" + "value": "import ast\nimport operator\n\nfrom langchain.tools import StructuredTool\nfrom langchain_core.tools import ToolException\nfrom loguru import logger\nfrom pydantic import BaseModel, Field\n\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.field_typing import Tool\nfrom langflow.inputs import MessageTextInput\nfrom langflow.schema import Data\n\n\nclass CalculatorToolComponent(LCToolComponent):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n name = \"CalculatorTool\"\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n ),\n ]\n\n class CalculatorToolSchema(BaseModel):\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\n\n def run_model(self) -> list[Data]:\n return self._evaluate_expression(self.expression)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"calculator\",\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\n func=self._eval_expr_with_error,\n args_schema=self.CalculatorToolSchema,\n )\n\n def _eval_expr(self, node):\n # Define the allowed operators\n operators = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n if isinstance(node, ast.Num):\n return node.n\n if isinstance(node, ast.BinOp):\n return operators[type(node.op)](self._eval_expr(node.left), self._eval_expr(node.right))\n if isinstance(node, ast.UnaryOp):\n return operators[type(node.op)](self._eval_expr(node.operand))\n if isinstance(node, ast.Call):\n msg = (\n \"Function calls like sqrt(), sin(), cos() etc. are not supported. \"\n \"Only basic arithmetic operations (+, -, *, /, **) are allowed.\"\n )\n raise TypeError(msg)\n msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(msg)\n\n def _eval_expr_with_error(self, expression: str) -> list[Data]:\n try:\n return self._evaluate_expression(expression)\n except Exception as e:\n raise ToolException(str(e)) from e\n\n def _evaluate_expression(self, expression: str) -> list[Data]:\n try:\n # Parse the expression and evaluate it\n tree = ast.parse(expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n # Format the result to a reasonable number of decimal places\n formatted_result = f\"{result:.6f}\".rstrip(\"0\").rstrip(\".\")\n\n self.status = formatted_result\n return [Data(data={\"result\": formatted_result})]\n\n except (SyntaxError, TypeError, KeyError) as e:\n error_message = f\"Invalid expression: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except Exception as e: # noqa: BLE001\n logger.opt(exception=True).debug(\"Error evaluating expression\")\n error_message = f\"Error: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n" }, "expression": { "_input_type": "MessageTextInput", @@ -1206,7 +1304,9 @@ "display_name": "Expression", "dynamic": false, "info": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "expression", @@ -1249,11 +1349,14 @@ }, "description": "Calculate SaaS subscription price based on costs, profit margin, and subscribers using step-by-step method and Chain-of-Thought prompting. ", "endpoint_name": null, + "gradient": "3", "icon": "calculator", "id": "9357f72e-2121-4541-8e7d-74b7ba2ada2b", - "gradient": "3", "is_component": false, "last_tested_version": "1.0.19.post2", "name": "SaaS Pricing", - "tags": ["agents", "assistants"] -} + "tags": [ + "agents", + "assistants" + ] +} \ No newline at end of file diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents .json b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents .json index 83df0a789627..8242fcde9305 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents .json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents .json @@ -727,7 +727,7 @@ "input_types": [ "Message" ], - "load_from_db": true, + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -1340,7 +1340,7 @@ "input_types": [ "Message" ], - "load_from_db": true, + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -2541,7 +2541,7 @@ "show": true, "title_case": false, "type": "code", - "value": "import ast\nimport operator\n\nfrom langchain.tools import StructuredTool\nfrom langchain_core.tools import ToolException\nfrom loguru import logger\nfrom pydantic import BaseModel, Field\n\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.field_typing import Tool\nfrom langflow.inputs import MessageTextInput\nfrom langflow.schema import Data\n\n\nclass CalculatorToolComponent(LCToolComponent):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n name = \"CalculatorTool\"\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n tool_mode=True,\n ),\n ]\n\n class CalculatorToolSchema(BaseModel):\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\n\n def run_model(self) -> list[Data]:\n return self._evaluate_expression(self.expression)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"calculator\",\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\n func=self._eval_expr_with_error,\n args_schema=self.CalculatorToolSchema,\n )\n\n def _eval_expr(self, node):\n # Define the allowed operators\n operators = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n if isinstance(node, ast.Num):\n return node.n\n if isinstance(node, ast.BinOp):\n return operators[type(node.op)](self._eval_expr(node.left), self._eval_expr(node.right))\n if isinstance(node, ast.UnaryOp):\n return operators[type(node.op)](self._eval_expr(node.operand))\n if isinstance(node, ast.Call):\n msg = (\n \"Function calls like sqrt(), sin(), cos() etc. are not supported. \"\n \"Only basic arithmetic operations (+, -, *, /, **) are allowed.\"\n )\n raise TypeError(msg)\n msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(msg)\n\n def _eval_expr_with_error(self, expression: str) -> list[Data]:\n try:\n return self._evaluate_expression(expression)\n except Exception as e:\n raise ToolException(str(e)) from e\n\n def _evaluate_expression(self, expression: str) -> list[Data]:\n try:\n # Parse the expression and evaluate it\n tree = ast.parse(expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n # Format the result to a reasonable number of decimal places\n formatted_result = f\"{result:.6f}\".rstrip(\"0\").rstrip(\".\")\n\n self.status = formatted_result\n return [Data(data={\"result\": formatted_result})]\n\n except (SyntaxError, TypeError, KeyError) as e:\n error_message = f\"Invalid expression: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except Exception as e: # noqa: BLE001\n logger.opt(exception=True).debug(\"Error evaluating expression\")\n error_message = f\"Error: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n" + "value": "import ast\nimport operator\n\nfrom langchain.tools import StructuredTool\nfrom langchain_core.tools import ToolException\nfrom loguru import logger\nfrom pydantic import BaseModel, Field\n\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.field_typing import Tool\nfrom langflow.inputs import MessageTextInput\nfrom langflow.schema import Data\n\n\nclass CalculatorToolComponent(LCToolComponent):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n name = \"CalculatorTool\"\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n ),\n ]\n\n class CalculatorToolSchema(BaseModel):\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\n\n def run_model(self) -> list[Data]:\n return self._evaluate_expression(self.expression)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"calculator\",\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\n func=self._eval_expr_with_error,\n args_schema=self.CalculatorToolSchema,\n )\n\n def _eval_expr(self, node):\n # Define the allowed operators\n operators = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n if isinstance(node, ast.Num):\n return node.n\n if isinstance(node, ast.BinOp):\n return operators[type(node.op)](self._eval_expr(node.left), self._eval_expr(node.right))\n if isinstance(node, ast.UnaryOp):\n return operators[type(node.op)](self._eval_expr(node.operand))\n if isinstance(node, ast.Call):\n msg = (\n \"Function calls like sqrt(), sin(), cos() etc. are not supported. \"\n \"Only basic arithmetic operations (+, -, *, /, **) are allowed.\"\n )\n raise TypeError(msg)\n msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(msg)\n\n def _eval_expr_with_error(self, expression: str) -> list[Data]:\n try:\n return self._evaluate_expression(expression)\n except Exception as e:\n raise ToolException(str(e)) from e\n\n def _evaluate_expression(self, expression: str) -> list[Data]:\n try:\n # Parse the expression and evaluate it\n tree = ast.parse(expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n # Format the result to a reasonable number of decimal places\n formatted_result = f\"{result:.6f}\".rstrip(\"0\").rstrip(\".\")\n\n self.status = formatted_result\n return [Data(data={\"result\": formatted_result})]\n\n except (SyntaxError, TypeError, KeyError) as e:\n error_message = f\"Invalid expression: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except Exception as e: # noqa: BLE001\n logger.opt(exception=True).debug(\"Error evaluating expression\")\n error_message = f\"Error: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n" }, "expression": { "_input_type": "MessageTextInput", @@ -3202,7 +3202,7 @@ "input_types": [ "Message" ], - "load_from_db": true, + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -3748,9 +3748,9 @@ }, "description": "This Agent is designed to systematically execute a series of tasks following a meticulously predefined sequence. By adhering to this structured order, the Agent ensures that each task is completed efficiently and effectively, optimizing overall performance and maintaining a high level of accuracy.", "endpoint_name": null, + "gradient": "1", "icon": "ListChecks", "id": "673f26a7-66f4-410a-8ccb-3e635c022023", - "gradient": "1", "is_component": false, "last_tested_version": "1.0.19.post2", "name": "Sequential Tasks Agents", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Simple Agent .json b/src/backend/base/langflow/initial_setup/starter_projects/Simple Agent .json index 6fd8cdf5d01b..9bf3e43f34eb 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Simple Agent .json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Simple Agent .json @@ -9,12 +9,18 @@ "dataType": "YahooFinanceTool", "id": "YahooFinanceTool-PzHUy", "name": "api_build_tool", - "output_types": ["Tool"] + "output_types": [ + "Tool" + ] }, "targetHandle": { "fieldName": "tools", "id": "Agent-KhAae", - "inputTypes": ["Tool", "BaseTool", "StructuredTool"], + "inputTypes": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "type": "other" } }, @@ -32,12 +38,16 @@ "dataType": "ChatInput", "id": "ChatInput-dBek4", "name": "message", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Agent-KhAae", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -55,12 +65,16 @@ "dataType": "Agent", "id": "Agent-KhAae", "name": "response", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-ULcvr", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -78,7 +92,9 @@ "display_name": "Agent", "id": "Agent-KhAae", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -126,7 +142,9 @@ "method": "message_response", "name": "response", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -155,7 +173,9 @@ "display_name": "Agent Description", "dynamic": false, "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -204,8 +224,10 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], - "load_from_db": true, + "input_types": [ + "Message" + ], + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -255,7 +277,9 @@ "display_name": "Input", "dynamic": false, "info": "The input provided by the user for the agent to process.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -329,7 +353,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -423,7 +449,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -439,7 +468,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -490,7 +521,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -506,7 +541,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -526,7 +563,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -546,7 +585,9 @@ "display_name": "Agent Instructions", "dynamic": false, "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -583,7 +624,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -604,7 +647,11 @@ "display_name": "Tools", "dynamic": false, "info": "These are the tools that the agent can use to help with tasks.", - "input_types": ["Tool", "BaseTool", "StructuredTool"], + "input_types": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "list": true, "name": "tools", "placeholder": "", @@ -657,7 +704,10 @@ "display_name": "Yahoo Finance Tool", "id": "YahooFinanceTool-PzHUy", "node": { - "base_classes": ["Data", "Tool"], + "base_classes": [ + "Data", + "Tool" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -665,7 +715,11 @@ "display_name": "Yahoo Finance Tool", "documentation": "", "edited": false, - "field_order": ["symbol", "method", "num_news"], + "field_order": [ + "symbol", + "method", + "num_news" + ], "frozen": false, "icon": "trending-up", "legacy": false, @@ -680,7 +734,9 @@ "name": "api_run_model", "required_inputs": [], "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -690,7 +746,9 @@ "name": "api_build_tool", "required_inputs": [], "selected": "Tool", - "types": ["Tool"], + "types": [ + "Tool" + ], "value": "__UNDEFINED__" } ], @@ -781,7 +839,9 @@ "display_name": "Stock Symbol", "dynamic": false, "info": "The stock symbol to retrieve data for (e.g., AAPL, GOOG).", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "symbol", @@ -819,7 +879,9 @@ "data": { "id": "ChatInput-dBek4", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -851,7 +913,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -864,7 +928,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -884,7 +950,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -964,7 +1032,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as input.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -987,7 +1057,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -1003,7 +1076,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -1023,7 +1098,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -1059,7 +1136,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -1099,7 +1178,9 @@ "display_name": "Chat Output", "id": "ChatOutput-ULcvr", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1130,7 +1211,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1143,7 +1226,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -1163,7 +1248,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -1201,7 +1288,9 @@ "display_name": "Data Template", "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "data_template", @@ -1221,7 +1310,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as output.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1242,7 +1333,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -1258,7 +1352,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -1278,7 +1374,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -1314,7 +1412,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -1388,11 +1488,14 @@ }, "description": "Get started with an agent that calls the Yahoo Finance tool for quick access to stock prices, market trends, and financial data.", "endpoint_name": null, + "gradient": "5", "icon": "Bot", "id": "a774332d-6fb5-43b6-96a4-d3eb8e62ddc0", - "gradient": "5", "is_component": false, "last_tested_version": "1.0.19.post2", "name": "Simple Agent", - "tags": ["assistants", "agents"] -} + "tags": [ + "assistants", + "agents" + ] +} \ No newline at end of file diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json b/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json index efb9a9859d01..826e9afbc20f 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json @@ -9,12 +9,16 @@ "dataType": "Agent", "id": "Agent-ImgzA", "name": "response", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-ZNoa2", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -32,12 +36,16 @@ "dataType": "Agent", "id": "Agent-cj2PH", "name": "response", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Agent-ImgzA", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -55,12 +63,16 @@ "dataType": "Agent", "id": "Agent-rPh1n", "name": "response", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Agent-cj2PH", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -78,12 +90,18 @@ "dataType": "SearchAPI", "id": "SearchAPI-Aez0t", "name": "api_build_tool", - "output_types": ["Tool"] + "output_types": [ + "Tool" + ] }, "targetHandle": { "fieldName": "tools", "id": "Agent-rPh1n", - "inputTypes": ["Tool", "BaseTool", "StructuredTool"], + "inputTypes": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "type": "other" } }, @@ -101,12 +119,18 @@ "dataType": "url_content_fetcher", "id": "url_content_fetcher-AyGpn", "name": "api_build_tool", - "output_types": ["Tool"] + "output_types": [ + "Tool" + ] }, "targetHandle": { "fieldName": "tools", "id": "Agent-cj2PH", - "inputTypes": ["Tool", "BaseTool", "StructuredTool"], + "inputTypes": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "type": "other" } }, @@ -124,12 +148,18 @@ "dataType": "CalculatorTool", "id": "CalculatorTool-dGfrj", "name": "api_build_tool", - "output_types": ["Tool"] + "output_types": [ + "Tool" + ] }, "targetHandle": { "fieldName": "tools", "id": "Agent-ImgzA", - "inputTypes": ["Tool", "BaseTool", "StructuredTool"], + "inputTypes": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "type": "other" } }, @@ -147,12 +177,16 @@ "dataType": "ChatInput", "id": "ChatInput-CIU0F", "name": "message", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Agent-rPh1n", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -168,7 +202,9 @@ "data": { "id": "ChatInput-CIU0F", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -197,7 +233,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -210,7 +248,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -229,7 +269,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -308,7 +350,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as input.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -330,7 +374,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -345,7 +392,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -364,7 +413,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -399,7 +450,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -437,7 +490,9 @@ "display_name": "Chat Output", "id": "ChatOutput-ZNoa2", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -469,7 +524,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -482,7 +539,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -502,7 +561,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -540,7 +601,9 @@ "display_name": "Data Template", "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "data_template", @@ -560,7 +623,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as output.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -581,7 +646,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -597,7 +665,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -617,7 +687,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -653,7 +725,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -691,7 +765,11 @@ "data": { "id": "SearchAPI-Aez0t", "node": { - "base_classes": ["Data", "list", "Tool"], + "base_classes": [ + "Data", + "list", + "Tool" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -717,9 +795,13 @@ "display_name": "Data", "method": "run_model", "name": "api_run_model", - "required_inputs": ["api_key"], + "required_inputs": [ + "api_key" + ], "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -727,9 +809,13 @@ "display_name": "Tool", "method": "build_tool", "name": "api_build_tool", - "required_inputs": ["api_key"], + "required_inputs": [ + "api_key" + ], "selected": "Tool", - "types": ["Tool"], + "types": [ + "Tool" + ], "value": "__UNDEFINED__" } ], @@ -742,7 +828,9 @@ "display_name": "SearchAPI API Key", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "load_from_db": true, "name": "api_key", "password": true, @@ -777,7 +865,9 @@ "display_name": "Engine", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "engine", @@ -796,7 +886,9 @@ "display_name": "Input", "dynamic": false, "info": "", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -881,7 +973,11 @@ "data": { "id": "url_content_fetcher-AyGpn", "node": { - "base_classes": ["Data", "list", "Tool"], + "base_classes": [ + "Data", + "list", + "Tool" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -889,7 +985,10 @@ "display_name": "URL Content Fetcher", "documentation": "https://python.langchain.com/docs/modules/data_connection/document_loaders/integrations/web_base", "edited": true, - "field_order": ["url", "fetch_params"], + "field_order": [ + "url", + "fetch_params" + ], "frozen": false, "icon": "globe", "lf_version": "1.0.19.post2", @@ -903,7 +1002,10 @@ "method": "run_model", "name": "api_run_model", "selected": "Data", - "types": ["Data", "list"], + "types": [ + "Data", + "list" + ], "value": "__UNDEFINED__" }, { @@ -912,7 +1014,9 @@ "method": "build_tool", "name": "api_build_tool", "selected": "Tool", - "types": ["Tool"], + "types": [ + "Tool" + ], "value": "__UNDEFINED__" } ], @@ -959,7 +1063,9 @@ "display_name": "URL", "dynamic": false, "info": "Enter a single URL to fetch content from.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "url", @@ -995,7 +1101,12 @@ "data": { "id": "CalculatorTool-dGfrj", "node": { - "base_classes": ["Data", "list", "Sequence", "Tool"], + "base_classes": [ + "Data", + "list", + "Sequence", + "Tool" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1003,7 +1114,9 @@ "display_name": "Calculator", "documentation": "", "edited": false, - "field_order": ["expression"], + "field_order": [ + "expression" + ], "frozen": false, "icon": "calculator", "legacy": false, @@ -1019,7 +1132,9 @@ "name": "api_run_model", "required_inputs": [], "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -1029,7 +1144,9 @@ "name": "api_build_tool", "required_inputs": [], "selected": "Tool", - "types": ["Tool"], + "types": [ + "Tool" + ], "value": "__UNDEFINED__" } ], @@ -1052,7 +1169,7 @@ "show": true, "title_case": false, "type": "code", - "value": "import ast\nimport operator\n\nfrom langchain.tools import StructuredTool\nfrom langchain_core.tools import ToolException\nfrom loguru import logger\nfrom pydantic import BaseModel, Field\n\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.field_typing import Tool\nfrom langflow.inputs import MessageTextInput\nfrom langflow.schema import Data\n\n\nclass CalculatorToolComponent(LCToolComponent):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n name = \"CalculatorTool\"\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n tool_mode=True,\n ),\n ]\n\n class CalculatorToolSchema(BaseModel):\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\n\n def run_model(self) -> list[Data]:\n return self._evaluate_expression(self.expression)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"calculator\",\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\n func=self._eval_expr_with_error,\n args_schema=self.CalculatorToolSchema,\n )\n\n def _eval_expr(self, node):\n # Define the allowed operators\n operators = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n if isinstance(node, ast.Num):\n return node.n\n if isinstance(node, ast.BinOp):\n return operators[type(node.op)](self._eval_expr(node.left), self._eval_expr(node.right))\n if isinstance(node, ast.UnaryOp):\n return operators[type(node.op)](self._eval_expr(node.operand))\n if isinstance(node, ast.Call):\n msg = (\n \"Function calls like sqrt(), sin(), cos() etc. are not supported. \"\n \"Only basic arithmetic operations (+, -, *, /, **) are allowed.\"\n )\n raise TypeError(msg)\n msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(msg)\n\n def _eval_expr_with_error(self, expression: str) -> list[Data]:\n try:\n return self._evaluate_expression(expression)\n except Exception as e:\n raise ToolException(str(e)) from e\n\n def _evaluate_expression(self, expression: str) -> list[Data]:\n try:\n # Parse the expression and evaluate it\n tree = ast.parse(expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n # Format the result to a reasonable number of decimal places\n formatted_result = f\"{result:.6f}\".rstrip(\"0\").rstrip(\".\")\n\n self.status = formatted_result\n return [Data(data={\"result\": formatted_result})]\n\n except (SyntaxError, TypeError, KeyError) as e:\n error_message = f\"Invalid expression: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except Exception as e: # noqa: BLE001\n logger.opt(exception=True).debug(\"Error evaluating expression\")\n error_message = f\"Error: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n" + "value": "import ast\nimport operator\n\nfrom langchain.tools import StructuredTool\nfrom langchain_core.tools import ToolException\nfrom loguru import logger\nfrom pydantic import BaseModel, Field\n\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.field_typing import Tool\nfrom langflow.inputs import MessageTextInput\nfrom langflow.schema import Data\n\n\nclass CalculatorToolComponent(LCToolComponent):\n display_name = \"Calculator\"\n description = \"Perform basic arithmetic operations on a given expression.\"\n icon = \"calculator\"\n name = \"CalculatorTool\"\n\n inputs = [\n MessageTextInput(\n name=\"expression\",\n display_name=\"Expression\",\n info=\"The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').\",\n ),\n ]\n\n class CalculatorToolSchema(BaseModel):\n expression: str = Field(..., description=\"The arithmetic expression to evaluate.\")\n\n def run_model(self) -> list[Data]:\n return self._evaluate_expression(self.expression)\n\n def build_tool(self) -> Tool:\n return StructuredTool.from_function(\n name=\"calculator\",\n description=\"Evaluate basic arithmetic expressions. Input should be a string containing the expression.\",\n func=self._eval_expr_with_error,\n args_schema=self.CalculatorToolSchema,\n )\n\n def _eval_expr(self, node):\n # Define the allowed operators\n operators = {\n ast.Add: operator.add,\n ast.Sub: operator.sub,\n ast.Mult: operator.mul,\n ast.Div: operator.truediv,\n ast.Pow: operator.pow,\n }\n if isinstance(node, ast.Num):\n return node.n\n if isinstance(node, ast.BinOp):\n return operators[type(node.op)](self._eval_expr(node.left), self._eval_expr(node.right))\n if isinstance(node, ast.UnaryOp):\n return operators[type(node.op)](self._eval_expr(node.operand))\n if isinstance(node, ast.Call):\n msg = (\n \"Function calls like sqrt(), sin(), cos() etc. are not supported. \"\n \"Only basic arithmetic operations (+, -, *, /, **) are allowed.\"\n )\n raise TypeError(msg)\n msg = f\"Unsupported operation or expression type: {type(node).__name__}\"\n raise TypeError(msg)\n\n def _eval_expr_with_error(self, expression: str) -> list[Data]:\n try:\n return self._evaluate_expression(expression)\n except Exception as e:\n raise ToolException(str(e)) from e\n\n def _evaluate_expression(self, expression: str) -> list[Data]:\n try:\n # Parse the expression and evaluate it\n tree = ast.parse(expression, mode=\"eval\")\n result = self._eval_expr(tree.body)\n\n # Format the result to a reasonable number of decimal places\n formatted_result = f\"{result:.6f}\".rstrip(\"0\").rstrip(\".\")\n\n self.status = formatted_result\n return [Data(data={\"result\": formatted_result})]\n\n except (SyntaxError, TypeError, KeyError) as e:\n error_message = f\"Invalid expression: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except ZeroDivisionError:\n error_message = \"Error: Division by zero\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n except Exception as e: # noqa: BLE001\n logger.opt(exception=True).debug(\"Error evaluating expression\")\n error_message = f\"Error: {e}\"\n self.status = error_message\n return [Data(data={\"error\": error_message, \"input\": expression})]\n" }, "expression": { "_input_type": "MessageTextInput", @@ -1060,7 +1177,9 @@ "display_name": "Expression", "dynamic": false, "info": "The arithmetic expression to evaluate (e.g., '4*4*(33/22)+12-20').", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "expression", @@ -1098,7 +1217,9 @@ "display_name": "City Selection Agent", "id": "Agent-rPh1n", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1147,7 +1268,9 @@ "method": "message_response", "name": "response", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1176,7 +1299,9 @@ "display_name": "Agent Description", "dynamic": false, "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1225,8 +1350,10 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], - "load_from_db": true, + "input_types": [ + "Message" + ], + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -1276,7 +1403,9 @@ "display_name": "Input", "dynamic": false, "info": "The input provided by the user for the agent to process.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1350,7 +1479,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -1444,7 +1575,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -1460,7 +1594,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -1511,7 +1647,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -1527,7 +1667,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -1547,7 +1689,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -1567,7 +1711,9 @@ "display_name": "Agent Instructions", "dynamic": false, "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1604,7 +1750,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1625,7 +1773,11 @@ "display_name": "Tools", "dynamic": false, "info": "These are the tools that the agent can use to help with tasks.", - "input_types": ["Tool", "BaseTool", "StructuredTool"], + "input_types": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "list": true, "name": "tools", "placeholder": "", @@ -1678,7 +1830,9 @@ "display_name": "Local Expert Agent", "id": "Agent-cj2PH", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -1727,7 +1881,9 @@ "method": "message_response", "name": "response", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -1756,7 +1912,9 @@ "display_name": "Agent Description", "dynamic": false, "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1805,8 +1963,10 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], - "load_from_db": true, + "input_types": [ + "Message" + ], + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -1856,7 +2016,9 @@ "display_name": "Input", "dynamic": false, "info": "The input provided by the user for the agent to process.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1930,7 +2092,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -2024,7 +2188,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -2040,7 +2207,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -2091,7 +2260,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -2107,7 +2280,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -2127,7 +2302,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -2147,7 +2324,9 @@ "display_name": "Agent Instructions", "dynamic": false, "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2184,7 +2363,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2205,7 +2386,11 @@ "display_name": "Tools", "dynamic": false, "info": "These are the tools that the agent can use to help with tasks.", - "input_types": ["Tool", "BaseTool", "StructuredTool"], + "input_types": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "list": true, "name": "tools", "placeholder": "", @@ -2258,7 +2443,9 @@ "display_name": "Travel Concierge Agent", "id": "Agent-ImgzA", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -2307,7 +2494,9 @@ "method": "message_response", "name": "response", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -2336,7 +2525,9 @@ "display_name": "Agent Description", "dynamic": false, "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2385,8 +2576,10 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], - "load_from_db": true, + "input_types": [ + "Message" + ], + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -2436,7 +2629,9 @@ "display_name": "Input", "dynamic": false, "info": "The input provided by the user for the agent to process.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -2510,7 +2705,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -2604,7 +2801,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -2620,7 +2820,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -2671,7 +2873,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -2687,7 +2893,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -2707,7 +2915,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -2727,7 +2937,9 @@ "display_name": "Agent Instructions", "dynamic": false, "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2764,7 +2976,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -2785,7 +2999,11 @@ "display_name": "Tools", "dynamic": false, "info": "These are the tools that the agent can use to help with tasks.", - "input_types": ["Tool", "BaseTool", "StructuredTool"], + "input_types": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "list": true, "name": "tools", "placeholder": "", @@ -2999,11 +3217,14 @@ }, "description": "Create a travel planning chatbot that uses specialized agents to craft personalized trip itineraries.", "endpoint_name": null, + "gradient": "0", "icon": "Plane", "id": "d6d33090-44c4-4a4b-8d06-c93fcf426446", - "gradient": "0", "is_component": false, "last_tested_version": "1.0.19.post2", "name": "Travel Planning Agents", - "tags": ["agents", "openai"] -} + "tags": [ + "agents", + "openai" + ] +} \ No newline at end of file diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json index bc3f45113661..ef99db20f4a8 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Vector Store RAG.json @@ -135,7 +135,7 @@ "source": "OpenAIEmbeddings-CeoV9", "sourceHandle": "{œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-CeoV9œ, œnameœ: œembeddingsœ, œoutput_typesœ: [œEmbeddingsœ]}", "target": "AstraDB-3buPx", - "targetHandle": "{œfieldNameœ: œembeddingœ, œidœ: œAstraDB-3buPxœ, œinputTypesœ: [œEmbeddingsœ], œtypeœ: œotherœ}" + "targetHandle": "{œfieldNameœ: œembedding_modelœ, œidœ: œAstraDB-3buPxœ, œinputTypesœ: [œEmbeddingsœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -244,7 +244,7 @@ "source": "OpenAIEmbeddings-ANgku", "sourceHandle": "{œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-ANgkuœ, œnameœ: œembeddingsœ, œoutput_typesœ: [œEmbeddingsœ]}", "target": "AstraDB-laybz", - "targetHandle": "{œfieldNameœ: œembeddingœ, œidœ: œAstraDB-laybzœ, œinputTypesœ: [œEmbeddingsœ], œtypeœ: œotherœ}" + "targetHandle": "{œfieldNameœ: œembedding_modelœ, œidœ: œAstraDB-laybzœ, œinputTypesœ: [œEmbeddingsœ], œtypeœ: œotherœ}" }, { "className": "", @@ -1956,7 +1956,9 @@ "display_name": "Embedding Model", "dynamic": false, "info": "Allows an embedding model configuration.", - "input_types": ["Embeddings"], + "input_types": [ + "Embeddings" + ], "list": false, "name": "embedding_model", "placeholder": "", @@ -2998,7 +3000,9 @@ "display_name": "Embedding Model", "dynamic": false, "info": "Allows an embedding model configuration.", - "input_types": ["Embeddings"], + "input_types": [ + "Embeddings" + ], "list": false, "name": "embedding_model", "placeholder": "", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/YouTube Transcript Q&A.json b/src/backend/base/langflow/initial_setup/starter_projects/YouTube Transcript Q&A.json index 5aa0bf98a9d6..3210a48d745f 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/YouTube Transcript Q&A.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/YouTube Transcript Q&A.json @@ -9,12 +9,16 @@ "dataType": "ChatInput", "id": "ChatInput-B1nYa", "name": "message", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "Agent-EGSx3", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -33,12 +37,16 @@ "dataType": "Agent", "id": "Agent-EGSx3", "name": "response", - "output_types": ["Message"] + "output_types": [ + "Message" + ] }, "targetHandle": { "fieldName": "input_value", "id": "ChatOutput-zUzVK", - "inputTypes": ["Message"], + "inputTypes": [ + "Message" + ], "type": "str" } }, @@ -56,12 +64,18 @@ "dataType": "YouTubeTranscriptsComponent", "id": "YouTubeTranscriptsComponent-n8Z9Y", "name": "transcripts_tool", - "output_types": ["Tool"] + "output_types": [ + "Tool" + ] }, "targetHandle": { "fieldName": "tools", "id": "Agent-EGSx3", - "inputTypes": ["Tool", "BaseTool", "StructuredTool"], + "inputTypes": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "type": "other" } }, @@ -79,7 +93,9 @@ "display_name": "Agent", "id": "Agent-EGSx3", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -128,7 +144,9 @@ "method": "message_response", "name": "response", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -157,7 +175,9 @@ "display_name": "Agent Description", "dynamic": false, "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -206,8 +226,10 @@ "display_name": "OpenAI API Key", "dynamic": false, "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": ["Message"], - "load_from_db": true, + "input_types": [ + "Message" + ], + "load_from_db": false, "name": "api_key", "password": true, "placeholder": "", @@ -257,7 +279,9 @@ "display_name": "Input", "dynamic": false, "info": "The input provided by the user for the agent to process.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -331,7 +355,9 @@ "display_name": "External Memory", "dynamic": false, "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", - "input_types": ["BaseChatMessageHistory"], + "input_types": [ + "BaseChatMessageHistory" + ], "list": false, "name": "memory", "placeholder": "", @@ -425,7 +451,10 @@ "dynamic": false, "info": "Order of the messages.", "name": "order", - "options": ["Ascending", "Descending"], + "options": [ + "Ascending", + "Descending" + ], "placeholder": "", "required": false, "show": true, @@ -441,7 +470,9 @@ "display_name": "Output Parser", "dynamic": false, "info": "The parser to use to parse the output of the model", - "input_types": ["OutputParser"], + "input_types": [ + "OutputParser" + ], "list": false, "name": "output_parser", "placeholder": "", @@ -492,7 +523,11 @@ "dynamic": false, "info": "Filter by sender type.", "name": "sender", - "options": ["Machine", "User", "Machine and User"], + "options": [ + "Machine", + "User", + "Machine and User" + ], "placeholder": "", "required": false, "show": true, @@ -508,7 +543,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Filter by sender name.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -528,7 +565,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -548,7 +587,9 @@ "display_name": "Agent Instructions", "dynamic": false, "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -585,7 +626,9 @@ "display_name": "Template", "dynamic": false, "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -606,7 +649,11 @@ "display_name": "Tools", "dynamic": false, "info": "These are the tools that the agent can use to help with tasks.", - "input_types": ["Tool", "BaseTool", "StructuredTool"], + "input_types": [ + "Tool", + "BaseTool", + "StructuredTool" + ], "list": true, "name": "tools", "placeholder": "", @@ -657,7 +704,9 @@ "data": { "id": "ChatInput-B1nYa", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -689,7 +738,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -702,7 +753,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -722,7 +775,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -802,7 +857,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as input.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -825,7 +882,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -841,7 +901,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -861,7 +923,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -897,7 +961,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -937,7 +1003,9 @@ "display_name": "Chat Output", "id": "ChatOutput-zUzVK", "node": { - "base_classes": ["Message"], + "base_classes": [ + "Message" + ], "beta": false, "conditional_paths": [], "custom_fields": {}, @@ -969,7 +1037,9 @@ "method": "message_response", "name": "message", "selected": "Message", - "types": ["Message"], + "types": [ + "Message" + ], "value": "__UNDEFINED__" } ], @@ -982,7 +1052,9 @@ "display_name": "Background Color", "dynamic": false, "info": "The background color of the icon.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "background_color", @@ -1002,7 +1074,9 @@ "display_name": "Icon", "dynamic": false, "info": "The icon of the message.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "chat_icon", @@ -1040,7 +1114,9 @@ "display_name": "Data Template", "dynamic": false, "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "data_template", @@ -1060,7 +1136,9 @@ "display_name": "Text", "dynamic": false, "info": "Message to be passed as output.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "input_value", @@ -1081,7 +1159,10 @@ "dynamic": false, "info": "Type of sender.", "name": "sender", - "options": ["Machine", "User"], + "options": [ + "Machine", + "User" + ], "placeholder": "", "required": false, "show": true, @@ -1097,7 +1178,9 @@ "display_name": "Sender Name", "dynamic": false, "info": "Name of the sender.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "sender_name", @@ -1117,7 +1200,9 @@ "display_name": "Session ID", "dynamic": false, "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "session_id", @@ -1153,7 +1238,9 @@ "display_name": "Text Color", "dynamic": false, "info": "The text color of the name", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "text_color", @@ -1222,7 +1309,10 @@ "data": { "id": "YouTubeTranscriptsComponent-n8Z9Y", "node": { - "base_classes": ["Data", "Tool"], + "base_classes": [ + "Data", + "Tool" + ], "beta": false, "category": "tools", "conditional_paths": [], @@ -1252,7 +1342,9 @@ "method": "build_youtube_transcripts", "name": "transcripts", "selected": "Data", - "types": ["Data"], + "types": [ + "Data" + ], "value": "__UNDEFINED__" }, { @@ -1261,7 +1353,9 @@ "method": "build_youtube_tool", "name": "transcripts_tool", "selected": "Tool", - "types": ["Tool"], + "types": [ + "Tool" + ], "value": "__UNDEFINED__" } ], @@ -1308,7 +1402,9 @@ "display_name": "Language", "dynamic": false, "info": "A comma-separated list of language codes in descending priority. Leave empty for default.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1331,7 +1427,10 @@ "dynamic": false, "info": "The format of the transcripts. Either 'text' for a single output or 'chunks' for timestamped chunks.", "name": "transcript_format", - "options": ["text", "chunks"], + "options": [ + "text", + "chunks" + ], "placeholder": "", "required": false, "show": true, @@ -1379,7 +1478,9 @@ "display_name": "Video URL", "dynamic": false, "info": "Enter the YouTube video URL to get transcripts from.", - "input_types": ["Message"], + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "multiline": true, @@ -1423,11 +1524,16 @@ }, "description": "Quickly get detailed answers to questions about YouTube videos by analyzing their transcripts.", "endpoint_name": null, + "gradient": "3", "icon": "Youtube", "id": "3b33c431-9b8b-4ba1-9372-04b785e590d3", - "gradient": "3", "is_component": false, "last_tested_version": "1.0.19.post2", "name": "YouTube Transcript Q&A", - "tags": ["agents", "content-generation", "rag", "q-a"] -} + "tags": [ + "agents", + "content-generation", + "rag", + "q-a" + ] +} \ No newline at end of file diff --git a/src/backend/base/langflow/services/store/utils.py b/src/backend/base/langflow/services/store/utils.py index a5ef715465ef..b06ce8e260e3 100644 --- a/src/backend/base/langflow/services/store/utils.py +++ b/src/backend/base/langflow/services/store/utils.py @@ -44,7 +44,7 @@ async def update_components_with_user_data( async def get_lf_version_from_pypi(): try: async with httpx.AsyncClient() as client: - response = client.get("https://pypi.org/pypi/langflow/json") + response = await client.get("https://pypi.org/pypi/langflow/json") if response.status_code != httpx.codes.OK: return None return response.json()["info"]["version"] From 9e8715cf4c0b8a216d9c195441f5a71ffc8cad78 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Tue, 19 Nov 2024 15:41:37 -0300 Subject: [PATCH 02/45] fix: enhance folder download endpoint with zip file support (#4706) * expanding download folder to zip file * run formatter * Update src/backend/base/langflow/api/v1/folders.py Co-authored-by: Gabriel Luiz Freitas Almeida * changing model to flowRead * run formatter --------- Co-authored-by: Gabriel Luiz Freitas Almeida --- src/backend/base/langflow/api/v1/folders.py | 55 +++++++++++++++---- src/backend/base/langflow/api/v1/schemas.py | 2 +- .../components/sideBarFolderButtons/index.tsx | 31 +++++++---- .../folders/use-get-download-folders.ts | 22 ++++++-- 4 files changed, 80 insertions(+), 30 deletions(-) diff --git a/src/backend/base/langflow/api/v1/folders.py b/src/backend/base/langflow/api/v1/folders.py index 910cfa19bbd4..4b9a6b27954f 100644 --- a/src/backend/base/langflow/api/v1/folders.py +++ b/src/backend/base/langflow/api/v1/folders.py @@ -1,16 +1,22 @@ +import io +import json +import zipfile +from datetime import datetime, timezone from typing import Annotated import orjson from fastapi import APIRouter, Depends, File, HTTPException, Response, UploadFile, status +from fastapi.encoders import jsonable_encoder +from fastapi.responses import StreamingResponse from fastapi_pagination import Params from fastapi_pagination.ext.sqlmodel import paginate from sqlalchemy import or_, update from sqlalchemy.orm import selectinload from sqlmodel import select -from langflow.api.utils import AsyncDbSession, CurrentActiveUser, cascade_delete_flow, custom_params +from langflow.api.utils import AsyncDbSession, CurrentActiveUser, cascade_delete_flow, custom_params, remove_api_keys from langflow.api.v1.flows import create_flows -from langflow.api.v1.schemas import FlowListCreate, FlowListReadWithFolderName +from langflow.api.v1.schemas import FlowListCreate from langflow.helpers.flow import generate_unique_flow_name from langflow.helpers.folders import generate_unique_folder_name from langflow.initial_setup.setup import STARTER_FOLDER_NAME @@ -248,28 +254,53 @@ async def delete_folder( raise HTTPException(status_code=500, detail=str(e)) from e -@router.get("/download/{folder_id}", response_model=FlowListReadWithFolderName, status_code=200) +@router.get("/download/{folder_id}", status_code=200) async def download_file( *, session: AsyncDbSession, folder_id: str, current_user: CurrentActiveUser, ): - """Download all flows from folder.""" + """Download all flows from folder as a zip file.""" try: - folder = ( - await session.exec(select(Folder).where(Folder.id == folder_id, Folder.user_id == current_user.id)) - ).first() + query = select(Folder).where(Folder.id == folder_id, Folder.user_id == current_user.id) + result = await session.exec(query) + folder = result.first() + + if not folder: + raise HTTPException(status_code=404, detail="Folder not found") + + flows_query = select(Flow).where(Flow.folder_id == folder_id) + flows_result = await session.exec(flows_query) + flows = [FlowRead.model_validate(flow, from_attributes=True) for flow in flows_result.all()] + + if not flows: + raise HTTPException(status_code=404, detail="No flows found in folder") + + flows_without_api_keys = [remove_api_keys(flow.model_dump()) for flow in flows] + zip_stream = io.BytesIO() + + with zipfile.ZipFile(zip_stream, "w") as zip_file: + for flow in flows_without_api_keys: + flow_json = json.dumps(jsonable_encoder(flow)) + zip_file.writestr(f"{flow['name']}.json", flow_json) + + zip_stream.seek(0) + + current_time = datetime.now(tz=timezone.utc).astimezone().strftime("%Y%m%d_%H%M%S") + filename = f"{current_time}_{folder.name}_flows.zip" + + return StreamingResponse( + zip_stream, + media_type="application/x-zip-compressed", + headers={"Content-Disposition": f"attachment; filename={filename}"}, + ) + except Exception as e: if "No result found" in str(e): raise HTTPException(status_code=404, detail="Folder not found") from e raise HTTPException(status_code=500, detail=str(e)) from e - if not folder: - raise HTTPException(status_code=404, detail="Folder not found") - - return folder - @router.post("/upload/", response_model=list[FlowRead], status_code=201) async def upload_file( diff --git a/src/backend/base/langflow/api/v1/schemas.py b/src/backend/base/langflow/api/v1/schemas.py index 9d9b21018ffd..87b137bfda32 100644 --- a/src/backend/base/langflow/api/v1/schemas.py +++ b/src/backend/base/langflow/api/v1/schemas.py @@ -162,7 +162,7 @@ class FlowListRead(BaseModel): class FlowListReadWithFolderName(BaseModel): flows: list[FlowRead] - name: str + folder_name: str description: str diff --git a/src/frontend/src/components/folderSidebarComponent/components/sideBarFolderButtons/index.tsx b/src/frontend/src/components/folderSidebarComponent/components/sideBarFolderButtons/index.tsx index f7031f8bcb2e..c2aa99bccb1e 100644 --- a/src/frontend/src/components/folderSidebarComponent/components/sideBarFolderButtons/index.tsx +++ b/src/frontend/src/components/folderSidebarComponent/components/sideBarFolderButtons/index.tsx @@ -125,7 +125,7 @@ const SideBarFoldersButtonsComponent = ({ }); }; - const { mutate: mutateDownloadFolder } = useGetDownloadFolders(); + const { mutate: mutateDownloadFolder } = useGetDownloadFolders({}); const handleDownloadFolder = (id: string) => { mutateDownloadFolder( @@ -133,20 +133,29 @@ const SideBarFoldersButtonsComponent = ({ folderId: id, }, { - onSuccess: (data) => { - data.folder_name = data?.name || "folder"; - data.folder_description = data?.description || ""; - - const jsonString = `data:text/json;charset=utf-8,${encodeURIComponent( - JSON.stringify(data), - )}`; + onSuccess: (response) => { + // Create a blob from the response data + const blob = new Blob([response.data], { + type: "application/x-zip-compressed", + }); + const url = window.URL.createObjectURL(blob); const link = document.createElement("a"); - link.href = jsonString; - link.download = `${data?.name}.json`; + link.href = url; + // Get filename from header or use default + const filename = + response.headers?.["content-disposition"] + ?.split("filename=")[1] + ?.replace(/['"]/g, "") ?? "flows.zip"; + + link.setAttribute("download", filename); + document.body.appendChild(link); link.click(); - track("Folder Exported", { folderId: id! }); + link.remove(); + window.URL.revokeObjectURL(url); + + track("Folder Exported", { folderId: id }); }, onError: () => { setErrorData({ diff --git a/src/frontend/src/controllers/API/queries/folders/use-get-download-folders.ts b/src/frontend/src/controllers/API/queries/folders/use-get-download-folders.ts index 8d4dec403ddf..36a2cc868145 100644 --- a/src/frontend/src/controllers/API/queries/folders/use-get-download-folders.ts +++ b/src/frontend/src/controllers/API/queries/folders/use-get-download-folders.ts @@ -1,4 +1,5 @@ import { useMutationFunctionType } from "@/types/api"; +import { UseMutationResult } from "@tanstack/react-query"; import { api } from "../../api"; import { getURL } from "../../helpers/constants"; import { UseRequestProcessor } from "../../services/request-processor"; @@ -8,22 +9,31 @@ interface IGetDownloadFolders { } export const useGetDownloadFolders: useMutationFunctionType< - undefined, + any, // Changed to any since we're getting the full response IGetDownloadFolders > = (options?) => { const { mutate } = UseRequestProcessor(); const downloadFoldersFn = async ( - data: IGetDownloadFolders, - ): Promise => { - const res = await api.get(`${getURL("FOLDERS")}/download/${data.folderId}`); - return res.data; + payload: IGetDownloadFolders, + ): Promise => { + const response = await api.get( + `${getURL("FOLDERS")}/download/${payload.folderId}`, + { + responseType: "blob", + headers: { + Accept: "application/x-zip-compressed", + }, + }, + ); + return response; }; - const mutation = mutate( + const mutation: UseMutationResult = mutate( ["useGetDownloadFolders"], downloadFoldersFn, options, ); + return mutation; }; From fb83af224aae6ecd69176cbd2b9eea24fb51e11f Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 19 Nov 2024 16:06:01 -0300 Subject: [PATCH 03/45] chore: Add pyproject.toml configuration to ruff format command in Makefile (#4714) * Add pyproject.toml config flag to ruff commands in Makefile * Update ruff exclude path in pyproject.toml * Remove unused TYPE_CHECKING import from api_key.py * Remove redundant config flag from 'ruff check' command in Makefile --- Makefile | 2 +- pyproject.toml | 2 +- src/backend/base/langflow/api/v1/api_key.py | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Makefile b/Makefile index 371ab33e464c..b928ccc0a44c 100644 --- a/Makefile +++ b/Makefile @@ -181,7 +181,7 @@ fix_codespell: ## run codespell to fix spelling errors format: ## run code formatters @uv run ruff check . --fix - @uv run ruff format . + @uv run ruff format . --config pyproject.toml @cd src/frontend && npm run format unsafe_fix: diff --git a/pyproject.toml b/pyproject.toml index a5b5b548ad6f..0a3b8261e3f2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -224,7 +224,7 @@ directory = "coverage" [tool.ruff] -exclude = ["src/backend/langflow/alembic/*"] +exclude = ["src/backend/base/langflow/alembic/*"] line-length = 120 [tool.ruff.lint] diff --git a/src/backend/base/langflow/api/v1/api_key.py b/src/backend/base/langflow/api/v1/api_key.py index 5fa0d117ea74..f3a19d9ae37f 100644 --- a/src/backend/base/langflow/api/v1/api_key.py +++ b/src/backend/base/langflow/api/v1/api_key.py @@ -1,4 +1,3 @@ -from typing import TYPE_CHECKING from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, Response @@ -12,9 +11,6 @@ from langflow.services.database.models.api_key.model import ApiKeyCreate, UnmaskedApiKeyRead from langflow.services.deps import get_settings_service -if TYPE_CHECKING: - pass - router = APIRouter(tags=["APIKey"], prefix="/api_key") From e7e364eb0dfd65b5ca07f0ba49f411599d912eb8 Mon Sep 17 00:00:00 2001 From: Jordan Frazier <122494242+jordanrfrazier@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:05:23 -0800 Subject: [PATCH 04/45] chore: add deprecation flag on base upload flow route (#4717) * add deprecation flag on base upload flow route * add deprecation flag on base upload flow route * [autofix.ci] apply automated fixes --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/backend/base/langflow/api/v1/endpoints.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/base/langflow/api/v1/endpoints.py b/src/backend/base/langflow/api/v1/endpoints.py index 000618fb6280..3514d23aa624 100644 --- a/src/backend/base/langflow/api/v1/endpoints.py +++ b/src/backend/base/langflow/api/v1/endpoints.py @@ -544,11 +544,16 @@ async def get_task_status(task_id: str) -> TaskStatusResponse: @router.post( "/upload/{flow_id}", status_code=HTTPStatus.CREATED, + deprecated=True, ) async def create_upload_file( file: UploadFile, flow_id: UUID, ) -> UploadFileResponse: + """Upload a file for a specific flow (Deprecated). + + This endpoint is deprecated and will be removed in a future version. + """ try: flow_id_str = str(flow_id) file_path = save_uploaded_file(file, folder_name=flow_id_str) From f1c00750aaffec970758f4d90c93429fd88b9037 Mon Sep 17 00:00:00 2001 From: "Junxi (Eric) Li" <68884486+ericljx2020-gmail@users.noreply.github.com> Date: Tue, 19 Nov 2024 12:10:39 -0800 Subject: [PATCH 05/45] fix: correct issue with Milvus field name, renamed connection password to Token (#4453) rename connectino password to Token --- src/backend/base/langflow/components/vectorstores/milvus.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/components/vectorstores/milvus.py b/src/backend/base/langflow/components/vectorstores/milvus.py index ff83d961d64d..994604f2f589 100644 --- a/src/backend/base/langflow/components/vectorstores/milvus.py +++ b/src/backend/base/langflow/components/vectorstores/milvus.py @@ -34,9 +34,9 @@ class MilvusVectorStoreComponent(LCVectorStoreComponent): ), SecretStrInput( name="password", - display_name="Connection Password", + display_name="Token", value="", - info="Ignore this field if no password is required to make connection.", + info="Ignore this field if no token is required to make connection.", ), DictInput(name="connection_args", display_name="Other Connection Arguments", advanced=True), StrInput(name="primary_field", display_name="Primary Field Name", value="pk"), From 39cec36641fbc5f24a488348e3573c78f052d731 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 19 Nov 2024 18:28:39 -0300 Subject: [PATCH 06/45] fix: fix overflow for lists in markdown render (#4720) * feat: Add support for ordered and unordered lists in ContentDisplay and ChatMessage components to prevent overflow --- .../components/chatComponents/ContentDisplay.tsx | 6 ++++++ .../chatView/chatMessage/newChatMessage.tsx | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/src/frontend/src/components/chatComponents/ContentDisplay.tsx b/src/frontend/src/components/chatComponents/ContentDisplay.tsx index fbce64044c43..0870ae453d2b 100644 --- a/src/frontend/src/components/chatComponents/ContentDisplay.tsx +++ b/src/frontend/src/components/chatComponents/ContentDisplay.tsx @@ -155,6 +155,12 @@ export default function ContentDisplay({ pre({ node, ...props }) { return <>{props.children}; }, + ol({ node, ...props }) { + return
    {props.children}
; + }, + ul({ node, ...props }) { + return
    {props.children}
; + }, code: ({ node, inline, className, children, ...props }) => { const match = /language-(\w+)/.exec(className || ""); return !inline ? ( diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/newChatMessage.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/newChatMessage.tsx index 5d7e4e2f173e..07cd37f9a855 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/newChatMessage.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/newChatMessage.tsx @@ -552,6 +552,20 @@ export default function ChatMessage({ ); }, + ol({ node, ...props }) { + return ( +
    + {props.children} +
+ ); + }, + ul({ node, ...props }) { + return ( +
    + {props.children} +
+ ); + }, pre({ node, ...props }) { return <>{props.children}; }, From ffd9eaae9fd909cc540efad6f095a0b109effca0 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 19 Nov 2024 18:48:37 -0300 Subject: [PATCH 07/45] fix: update Test Docker Images workflow to use get_version_info (#4721) Update Docker test workflow to use get_version_info function for version retrieval --- .github/workflows/docker_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker_test.yml b/.github/workflows/docker_test.yml index 9564ccd271b1..ea4824ef3fb9 100644 --- a/.github/workflows/docker_test.yml +++ b/.github/workflows/docker_test.yml @@ -35,7 +35,7 @@ jobs: - name: Test image run: | expected_version=$(cat pyproject.toml | grep version | head -n 1 | cut -d '"' -f 2) - version=$(docker run --rm --entrypoint bash langflowai/langflow:latest-dev -c 'python -c "from langflow.version.version import get_version; print(get_version())"') + version=$(docker run --rm --entrypoint bash langflowai/langflow:latest-dev -c "python -c 'from langflow.utils.version import get_version_info; print(get_version_info()[\"version\"])'") if [ "$expected_version" != "$version" ]; then echo "Expected version: $expected_version" echo "Actual version: $version" @@ -51,7 +51,7 @@ jobs: - name: Test backend image run: | expected_version=$(cat pyproject.toml | grep version | head -n 1 | cut -d '"' -f 2) - version=$(docker run --rm --entrypoint bash langflowai/langflow-backend:latest-dev -c 'python -c "from langflow.version.version import get_version; print(get_version())"') + version=$(docker run --rm --entrypoint bash langflowai/langflow-backend:latest-dev -c "python -c 'from langflow.utils.version import get_version_info; print(get_version_info()[\"version\"])'") if [ "$expected_version" != "$version" ]; then echo "Expected version: $expected_version" echo "Actual version: $version" From c9e0d5e4ba086391013e2775dcd0e939547b9aac Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Tue, 19 Nov 2024 18:54:49 -0300 Subject: [PATCH 08/45] fix: updated node toolbar to be more stable on zoom, fix success and error component tooltips, unselect nodes on flow opening (#4610) * Unselect nodes and edges on enter * Start with viewport fit * Fix run component tooltip to match error and success * Update scrollbar design * Added node toolbar without portal to match the width of the node * Updated color of scrollbar * Updated NoteNode to have same behavior as GenericNode * Updated logic for tests to pass * Fix fitview to only occur when nodes are present * Update z index of resize handle * Sticky notes test fix * [autofix.ci] apply automated fixes * freeze test fixed --------- Co-authored-by: Mike Fortman Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: anovazzi1 Co-authored-by: Gabriel Luiz Freitas Almeida Co-authored-by: Cristhian Zanforlin Lousa --- src/frontend/src/App.css | 6 +- .../components/NodeStatus/index.tsx | 27 +- .../src/CustomNodes/GenericNode/index.tsx | 13 +- .../NoteNode/NoteToolbarComponent/index.tsx | 16 +- .../src/CustomNodes/NoteNode/index.tsx | 22 +- .../src/components/ui/select-custom.tsx | 29 ++ .../components/PageComponent/index.tsx | 2 + .../components/nodeToolbarComponent/index.tsx | 392 ++++++++---------- src/frontend/src/stores/flowStore.ts | 3 +- src/frontend/src/style/ag-theme-shadcn.css | 2 +- src/frontend/src/style/classes.css | 3 +- .../src/types/utils/reactflowUtils.ts | 7 +- src/frontend/src/utils/reactflowUtils.ts | 10 +- src/frontend/tailwind.config.mjs | 2 +- .../tests/core/features/freeze.spec.ts | 2 +- .../extended/features/sticky-notes.spec.ts | 2 +- 16 files changed, 275 insertions(+), 263 deletions(-) diff --git a/src/frontend/src/App.css b/src/frontend/src/App.css index 96907a86503f..1a20e99fb8cf 100644 --- a/src/frontend/src/App.css +++ b/src/frontend/src/App.css @@ -93,7 +93,7 @@ body { } ::-webkit-scrollbar-thumb:hover { - background-color: hsl(var(--ring)) !important; + background-color: hsl(var(--placeholder-foreground)) !important; } .jv-indent::-webkit-scrollbar-track { @@ -107,7 +107,7 @@ body { } .jv-indent::-webkit-scrollbar-thumb:hover { - background-color: hsl(var(--ring)) !important; + background-color: hsl(var(--placeholder-foreground)) !important; } .custom-hover { @@ -170,7 +170,7 @@ body { height: 0.75rem !important; background-color: white !important; border-color: var(--border) !important; - z-index: 1000 !important; + z-index: 50 !important; border-radius: 20% !important; } diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx index 4f5c2676a4b4..579233ccecb9 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeStatus/index.tsx @@ -151,7 +151,6 @@ export default function NodeStatus({ const divRef = useRef(null); const [isHovered, setIsHovered] = useState(false); - const runClass = "justify-left flex font-normal text-muted-foreground"; const stopBuilding = useFlowStore((state) => state.stopBuilding); const handleClickRun = () => { @@ -207,6 +206,12 @@ export default function NodeStatus({
{STATUS_BUILDING} @@ -215,26 +220,26 @@ export default function NodeStatus({ ) : !validationStatus ? ( {STATUS_BUILD} ) : ( -
-
+
+
{validationString && ( -
+
{validationString}
)} {lastRunTime && ( -
+
{RUN_TIMESTAMP_PREFIX}
-
+
{lastRunTime}
)} -
-
-
Duration:
-
- {validationStatus?.data.duration} +
+
Duration:
+
+ {validationStatus?.data.duration} +
diff --git a/src/frontend/src/CustomNodes/GenericNode/index.tsx b/src/frontend/src/CustomNodes/GenericNode/index.tsx index 3e44602454d8..07f92d823517 100644 --- a/src/frontend/src/CustomNodes/GenericNode/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/index.tsx @@ -1,7 +1,7 @@ import { usePostValidateComponentCode } from "@/controllers/API/queries/nodes/use-post-validate-component-code"; import { useEffect, useMemo, useState } from "react"; import { useHotkeys } from "react-hotkeys-hook"; -import { NodeToolbar, useUpdateNodeInternals } from "reactflow"; +import { useUpdateNodeInternals } from "reactflow"; import { ForwardedIconComponent } from "../../components/genericIconComponent"; import ShadTooltip from "../../components/shadTooltipComponent"; import { Button } from "../../components/ui/button"; @@ -214,8 +214,8 @@ export default function GenericNode({ }, [hiddenOutputs]); const memoizedNodeToolbarComponent = useMemo(() => { - return ( - + return selected ? ( +
{ @@ -236,7 +236,9 @@ export default function GenericNode({ isOutdated={isOutdated && isUserEdited} setOpenShowMoreOptions={setOpenShowMoreOptions} /> - +
+ ) : ( + <> ); }, [ data, @@ -336,7 +338,6 @@ export default function GenericNode({ return ( <> - {memoizedNodeToolbarComponent}
+ {memoizedNodeToolbarComponent}
- +
diff --git a/src/frontend/src/CustomNodes/NoteNode/index.tsx b/src/frontend/src/CustomNodes/NoteNode/index.tsx index 0fa8e27d8959..d4a7ee4f7af0 100644 --- a/src/frontend/src/CustomNodes/NoteNode/index.tsx +++ b/src/frontend/src/CustomNodes/NoteNode/index.tsx @@ -8,7 +8,7 @@ import { import { noteDataType } from "@/types/flow"; import { cn } from "@/utils/utils"; import { useEffect, useMemo, useRef, useState } from "react"; -import { NodeResizer, NodeToolbar } from "reactflow"; +import { NodeResizer } from "reactflow"; import NodeDescription from "../GenericNode/components/NodeDescription"; import NoteToolbarComponent from "./NoteToolbarComponent"; function NoteNode({ @@ -33,17 +33,20 @@ function NoteNode({ }); } }, []); + const MemoNoteToolbarComponent = useMemo( - () => ( - - - - ), - [data, bgColor], + () => + selected ? ( +
+ +
+ ) : ( + <> + ), + [data, bgColor, selected], ); return ( <> - {MemoNoteToolbarComponent} + {MemoNoteToolbarComponent}
, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + {children} + + +)); +SelectContentWithoutPortal.displayName = SelectPrimitive.Content.displayName; + const SelectLabel = React.forwardRef< React.ElementRef, React.ComponentPropsWithoutRef @@ -99,6 +127,7 @@ SelectSeparator.displayName = SelectPrimitive.Separator.displayName; export { Select, SelectContent, + SelectContentWithoutPortal, SelectGroup, SelectItem, SelectLabel, diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 6e9f8a9c3bc0..3b336b35ef30 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -91,6 +91,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { ); const nodes = useFlowStore((state) => state.nodes); const edges = useFlowStore((state) => state.edges); + const isEmptyFlow = useRef(nodes.length === 0); const onNodesChange = useFlowStore((state) => state.onNodesChange); const onEdgesChange = useFlowStore((state) => state.onEdgesChange); const setNodes = useFlowStore((state) => state.setNodes); @@ -552,6 +553,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { onDrop={onDrop} onSelectionChange={onSelectionChange} deleteKeyCode={[]} + fitView={isEmptyFlow.current ? false : true} className="theme-attribution" minZoom={0.01} maxZoom={8} diff --git a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx index b4f33ff8b256..f751ac6cf615 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -11,12 +11,12 @@ import CodeAreaModal from "@/modals/codeAreaModal"; import { APIClassType } from "@/types/api"; import _, { cloneDeep } from "lodash"; import { useEffect, useRef, useState } from "react"; -import { useStore, useUpdateNodeInternals } from "reactflow"; +import { useUpdateNodeInternals } from "reactflow"; import IconComponent from "../../../../components/genericIconComponent"; import ShadTooltip from "../../../../components/shadTooltipComponent"; import { Select, - SelectContent, + SelectContentWithoutPortal, SelectItem, SelectTrigger, } from "../../../../components/ui/select-custom"; @@ -370,31 +370,9 @@ export default function NodeToolbarComponent({ parameterId: "tool_mode", }); - // Use ReactFlow's store selector to get zoom updates - const zoom = useStore((state) => state.transform[2]); - const [scale, setScale] = useState(null); - - useEffect(() => { - if (!zoom) return; - if (zoom < 0.65) { - const newScale = Math.max(zoom * 1.2, 0.4); - setScale(newScale); - } else { - setScale(1); - } - }, [zoom]); - - if (scale === null) return <>; - return ( <> -
+
{hasCode && ( } side="top" - styleClasses="relative bottom-2" > - -
- + +
+ {" "} + Delete{" "} + + + +
+
+ {hasToolMode && ( + + obj.name === "Tool Mode") + ?.shortcut! + } + value={"Tool Mode"} + icon={"Hammer"} + dataTestId="tool-mode-button" + style={`${toolMode ? "text-primary" : ""} transition-all`} + /> + + )} + + +
((set, get) => ({ let newEdges = cleanEdges(nodes, edges); const { inputs, outputs } = getInputsAndOutputs(nodes); get().updateComponentsToUpdate(nodes); + unselectAllNodesEdges(nodes, edges); set({ nodes, edges: newEdges, @@ -212,7 +214,6 @@ const useFlowStore = create((set, get) => ({ }, setReactFlowInstance: (newState) => { set({ reactFlowInstance: newState }); - get().reactFlowInstance?.fitView(); }, onNodesChange: (changes: NodeChange[]) => { set({ diff --git a/src/frontend/src/style/ag-theme-shadcn.css b/src/frontend/src/style/ag-theme-shadcn.css index 4fbed5c3dec3..d36b9ba520ad 100644 --- a/src/frontend/src/style/ag-theme-shadcn.css +++ b/src/frontend/src/style/ag-theme-shadcn.css @@ -68,7 +68,7 @@ .ag-body-horizontal-scroll-viewport::-webkit-scrollbar-thumb:hover, .ag-body-vertical-scroll-viewport::-webkit-scrollbar-thumb:hover { - background-color: hsl(var(--ring)); + background-color: hsl(var(--placeholder-foreground)); } .ag-paging-page-size { diff --git a/src/frontend/src/style/classes.css b/src/frontend/src/style/classes.css index b33254e45c62..46f46be2eb3e 100644 --- a/src/frontend/src/style/classes.css +++ b/src/frontend/src/style/classes.css @@ -26,6 +26,7 @@ pre { } .react-flow__pane { + pointer-events: all; cursor: default; } @@ -147,7 +148,7 @@ textarea[class^="ag-"]:focus { } .ace_scrollbar::-webkit-scrollbar-thumb:hover { - background-color: hsl(var(--ring)); + background-color: hsl(var(--placeholder-foreground)); border-radius: 999px; } diff --git a/src/frontend/src/types/utils/reactflowUtils.ts b/src/frontend/src/types/utils/reactflowUtils.ts index 1eb2430a89e0..bb1d4c1d18ba 100644 --- a/src/frontend/src/types/utils/reactflowUtils.ts +++ b/src/frontend/src/types/utils/reactflowUtils.ts @@ -1,11 +1,6 @@ -import { Edge, Node } from "reactflow"; +import { Edge } from "reactflow"; import { FlowType, NodeType } from "../flow"; -export type unselectAllNodesType = { - updateNodes: (nodes: Node[]) => void; - data: Node[]; -}; - export type addEscapedHandleIdsToEdgesType = { edges: Edge[]; }; diff --git a/src/frontend/src/utils/reactflowUtils.ts b/src/frontend/src/utils/reactflowUtils.ts index 60fb3685127d..41ab8cb85258 100644 --- a/src/frontend/src/utils/reactflowUtils.ts +++ b/src/frontend/src/utils/reactflowUtils.ts @@ -41,7 +41,6 @@ import { addEscapedHandleIdsToEdgesType, findLastNodeType, generateFlowType, - unselectAllNodesType, updateEdgesHandleIdsType, } from "../types/utils/reactflowUtils"; import { getLayoutedNodes } from "./layoutUtils"; @@ -202,12 +201,13 @@ export function detectBrokenEdgesEdges(nodes: NodeType[], edges: Edge[]) { return BrokenEdges; } -export function unselectAllNodes({ updateNodes, data }: unselectAllNodesType) { - let newNodes = cloneDeep(data); - newNodes.forEach((node: Node) => { +export function unselectAllNodesEdges(nodes: Node[], edges: Edge[]) { + nodes.forEach((node: Node) => { node.selected = false; }); - updateNodes(newNodes!); + edges.forEach((edge: Edge) => { + edge.selected = false; + }); } export function isValidConnection( diff --git a/src/frontend/tailwind.config.mjs b/src/frontend/tailwind.config.mjs index acb0a2e62710..d920e4ac0dc8 100644 --- a/src/frontend/tailwind.config.mjs +++ b/src/frontend/tailwind.config.mjs @@ -353,7 +353,7 @@ const config = { borderRadius: "999px", }, "&::-webkit-scrollbar-thumb:hover": { - backgroundColor: "hsl(var(--ring))", + backgroundColor: "hsl(var(--placeholder-foreground))", }, cursor: "auto", }, diff --git a/src/frontend/tests/core/features/freeze.spec.ts b/src/frontend/tests/core/features/freeze.spec.ts index 5ed418803ff7..97d787bbcdb1 100644 --- a/src/frontend/tests/core/features/freeze.spec.ts +++ b/src/frontend/tests/core/features/freeze.spec.ts @@ -305,7 +305,7 @@ test("user must be able to freeze a component", async ({ page }) => { await page.waitForTimeout(1000); - await page.getByTestId("icon-Snowflake").last().click(); + await page.getByText("Freeze", { exact: true }).click(); await page.waitForTimeout(1000); diff --git a/src/frontend/tests/extended/features/sticky-notes.spec.ts b/src/frontend/tests/extended/features/sticky-notes.spec.ts index b0587b5c4c91..34ebbe8bb78f 100644 --- a/src/frontend/tests/extended/features/sticky-notes.spec.ts +++ b/src/frontend/tests/extended/features/sticky-notes.spec.ts @@ -162,7 +162,7 @@ The future of AI is both exciting and uncertain. As the technology continues to await page.getByTestId("note_node").nth(0).focus(); await page.getByTestId("more-options-modal").click(); - await page.getByText("Delete").last().click(); + await page.getByText("Delete").first().click(); await page.waitForTimeout(1000); From b978241d16f47e14316e2480ea4f4ad0dead8f26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=8Dtalo=20Johnny?= Date: Tue, 19 Nov 2024 18:55:07 -0300 Subject: [PATCH 09/45] fix: assign orphaned flows to superuser if auto-login is enabled (#4715) * fix: assign orphaned flows to superuser if auto-login is enabled * fix: orphan flow naming * fix: ruff errors * fix: orphan flow naming (again) --- .../langflow/services/database/service.py | 46 +++++++++++++------ src/backend/base/langflow/services/utils.py | 4 +- 2 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/backend/base/langflow/services/database/service.py b/src/backend/base/langflow/services/database/service.py index 7890d9ac875f..643835aa738b 100644 --- a/src/backend/base/langflow/services/database/service.py +++ b/src/backend/base/langflow/services/database/service.py @@ -1,6 +1,7 @@ from __future__ import annotations import asyncio +import re import sqlite3 import time from contextlib import asynccontextmanager, contextmanager @@ -145,28 +146,45 @@ async def with_async_session(self): async with AsyncSession(self.async_engine, expire_on_commit=False) as session: yield session - async def migrate_flows_if_auto_login(self) -> None: - # if auto_login is enabled, we need to migrate the flows - # to the default superuser if they don't have a user id - # associated with them + async def assign_orphaned_flows_to_superuser(self) -> None: + """Assign flows without a user ID to the default superuser if auto_login is enabled.""" + # Get the settings service to check if auto_login is enabled settings_service = get_settings_service() if settings_service.auth_settings.AUTO_LOGIN: async with self.with_async_session() as session: - # `== None` translates to SQL `is NULL` + # Select flows where user_id is NULL (orphaned flows) stmt = select(models.Flow).where(models.Flow.user_id == None) # noqa: E711 - flows = (await session.exec(stmt)).all() - if flows: - logger.debug("Migrating flows to default superuser") - username = settings_service.auth_settings.SUPERUSER - user = await get_user_by_username(session, username) - if not user: + orphaned_flows = (await session.exec(stmt)).all() + + if orphaned_flows: + logger.debug("Assigning orphaned flows to the default superuser") + + # Get the default superuser + superuser_username = settings_service.auth_settings.SUPERUSER + superuser = await get_user_by_username(session, superuser_username) + + if not superuser: logger.error("Default superuser not found") msg = "Default superuser not found" raise RuntimeError(msg) - for flow in flows: - flow.user_id = user.id + + stmt = select(models.Flow.name).where(models.Flow.user_id == superuser.id) + result = await session.exec(stmt) + superuser_flows_names = result.all() + # Assign each orphaned flow to the superuser + for flow in orphaned_flows: + flow.user_id = superuser.id + if flow.name in superuser_flows_names: + name_match = re.search(r"\((\d+)\)$", flow.name) + if not name_match: + flow.name = f"{flow.name} (1)" + else: + num = int(name_match.group(1)) + 1 + flow.name = re.sub(r"\(\d+\)$", f"({num})", flow.name) + + # Commit the changes to the database await session.commit() - logger.debug("Flows migrated successfully") + logger.debug("Successfully assigned orphaned flows to the default superuser") def check_schema_health(self) -> bool: inspector = inspect(self.engine) diff --git a/src/backend/base/langflow/services/utils.py b/src/backend/base/langflow/services/utils.py index d857c6e9efab..962d128f5998 100644 --- a/src/backend/base/langflow/services/utils.py +++ b/src/backend/base/langflow/services/utils.py @@ -166,8 +166,8 @@ async def initialize_services(*, fix_migration: bool = False) -> None: async with get_db_service().with_async_session() as session: await setup_superuser(get_service(ServiceType.SETTINGS_SERVICE), session) try: - await get_db_service().migrate_flows_if_auto_login() + await get_db_service().assign_orphaned_flows_to_superuser() except Exception as exc: - msg = "Error migrating flows" + msg = "Error assigning orphaned flows to the superuser" logger.exception(msg) raise RuntimeError(msg) from exc From ebe4f3470c6e060f9698c57a0ae61e0217016811 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 19 Nov 2024 19:04:08 -0300 Subject: [PATCH 10/45] refactor: Improve time and use waitForSelector in globalVariables.spec.ts (#4619) --- src/frontend/tests/core/features/globalVariables.spec.ts | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/frontend/tests/core/features/globalVariables.spec.ts b/src/frontend/tests/core/features/globalVariables.spec.ts index 00dfc6211b1a..f37a25d25f9c 100644 --- a/src/frontend/tests/core/features/globalVariables.spec.ts +++ b/src/frontend/tests/core/features/globalVariables.spec.ts @@ -24,7 +24,9 @@ test("user must be able to save or delete a global variable", async ({ while (modalCount === 0) { await page.getByText("New Flow", { exact: true }).click(); - await page.waitForTimeout(3000); + await page.waitForSelector('[data-testid="modal-title"]', { + timeout: 3000, + }); modalCount = await page.getByTestId("modal-title")?.count(); } await page.waitForSelector('[data-testid="blank-flow"]', { @@ -34,7 +36,9 @@ test("user must be able to save or delete a global variable", async ({ await page.getByTestId("sidebar-search-input").click(); await page.getByTestId("sidebar-search-input").fill("openai"); - await page.waitForTimeout(1000); + await page.waitForSelector('[data-testid="modelsOpenAI"]', { + timeout: 1000, + }); await page .getByTestId("modelsOpenAI") @@ -82,7 +86,6 @@ test("user must be able to save or delete a global variable", async ({ 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.waitForTimeout(1000); await page .getByText(credentialName, { exact: true }) From a0acf39f8a95839d8b7c808cbee4a9345b5c6092 Mon Sep 17 00:00:00 2001 From: Gabriel Luiz Freitas Almeida Date: Tue, 19 Nov 2024 20:16:36 -0300 Subject: [PATCH 11/45] feat: Add database cleanup functions for transactions and vertex builds (#4694) * feat: Add configuration options for maximum transactions and vertex builds retention * Add functions to clean up old transactions and vertex builds in the database - Implement `clean_transactions` to delete transactions exceeding the configured limit. - Implement `clean_vertex_builds` to delete vertex builds exceeding the configured limit. - Integrate cleanup functions into the service initialization process. * Add error handling and logging for cleanup tasks in utils.py - Wrap transaction and vertex build cleanup operations in try-except blocks. - Log success and error messages for cleanup operations. - Rollback session on exceptions without re-raising, as these are cleanup tasks. - Adjust service initialization order to ensure proper setup. * Reorder setup and cleanup tasks in database initialization process * fix: Update type hints for settings_service in cleanup functions * Remove execution options in cleanup functions * Handle specific exceptions during cleanup tasks in utils.py * Use `col` for column references in delete statements to improve SQL query clarity. --- .../base/langflow/services/settings/base.py | 4 + src/backend/base/langflow/services/utils.py | 84 ++++++++++++++++++- 2 files changed, 85 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/services/settings/base.py b/src/backend/base/langflow/services/settings/base.py index 073f12c677dc..5cbed709b516 100644 --- a/src/backend/base/langflow/services/settings/base.py +++ b/src/backend/base/langflow/services/settings/base.py @@ -173,6 +173,10 @@ class Settings(BaseSettings): """The maximum file size for the upload in MB.""" deactivate_tracing: bool = False """If set to True, tracing will be deactivated.""" + max_transactions_to_keep: int = 3000 + """The maximum number of transactions to keep in the database.""" + max_vertex_builds_to_keep: int = 3000 + """The maximum number of vertex builds to keep in the database.""" @field_validator("dev") @classmethod diff --git a/src/backend/base/langflow/services/utils.py b/src/backend/base/langflow/services/utils.py index 962d128f5998..f71dba557f9b 100644 --- a/src/backend/base/langflow/services/utils.py +++ b/src/backend/base/langflow/services/utils.py @@ -1,17 +1,28 @@ +from __future__ import annotations + import asyncio +from typing import TYPE_CHECKING from loguru import logger -from sqlmodel import select -from sqlmodel.ext.asyncio.session import AsyncSession +from sqlalchemy import delete +from sqlalchemy import exc as sqlalchemy_exc +from sqlmodel import col, select from langflow.services.auth.utils import create_super_user, verify_password from langflow.services.cache.factory import CacheServiceFactory +from langflow.services.database.models.transactions.model import TransactionTable +from langflow.services.database.models.vertex_builds.model import VertexBuildTable from langflow.services.database.utils import initialize_database from langflow.services.schema import ServiceType from langflow.services.settings.constants import DEFAULT_SUPERUSER, DEFAULT_SUPERUSER_PASSWORD from .deps import get_db_service, get_service, get_settings_service +if TYPE_CHECKING: + from sqlmodel.ext.asyncio.session import AsyncSession + + from langflow.services.settings.manager import SettingsService + async def get_or_create_super_user(session: AsyncSession, username, password, is_default): from langflow.services.database.models.user.model import User @@ -157,6 +168,70 @@ def initialize_session_service() -> None: ) +async def clean_transactions(settings_service: SettingsService, session: AsyncSession) -> None: + """Clean up old transactions from the database. + + This function deletes transactions that exceed the maximum number to keep (configured in settings). + It orders transactions by timestamp descending and removes the oldest ones beyond the limit. + + Args: + settings_service: The settings service containing configuration like max_transactions_to_keep + session: The database session to use for the deletion + + Returns: + None + """ + try: + # Delete transactions using bulk delete + delete_stmt = delete(TransactionTable).where( + col(TransactionTable.id).in_( + select(TransactionTable.id) + .order_by(col(TransactionTable.timestamp).desc()) + .offset(settings_service.settings.max_transactions_to_keep) + ) + ) + + await session.exec(delete_stmt) + await session.commit() + logger.debug("Successfully cleaned up old transactions") + except (sqlalchemy_exc.SQLAlchemyError, asyncio.TimeoutError) as exc: + logger.error(f"Error cleaning up transactions: {exc!s}") + await session.rollback() + # Don't re-raise since this is a cleanup task + + +async def clean_vertex_builds(settings_service: SettingsService, session: AsyncSession) -> None: + """Clean up old vertex builds from the database. + + This function deletes vertex builds that exceed the maximum number to keep (configured in settings). + It orders vertex builds by timestamp descending and removes the oldest ones beyond the limit. + + Args: + settings_service: The settings service containing configuration like max_vertex_builds_to_keep + session: The database session to use for the deletion + + Returns: + None + """ + try: + # Delete vertex builds using bulk delete + delete_stmt = delete(VertexBuildTable).where( + col(VertexBuildTable.id).in_( + select(VertexBuildTable.id) + .order_by(col(VertexBuildTable.timestamp).desc()) + .offset(settings_service.settings.max_vertex_builds_to_keep) + ) + ) + + await session.exec(delete_stmt) + await session.commit() + logger.debug("Successfully cleaned up old vertex builds") + except (sqlalchemy_exc.SQLAlchemyError, asyncio.TimeoutError) as exc: + logger.error(f"Error cleaning up vertex builds: {exc!s}") + await session.rollback() + # Don't re-raise since this is a cleanup task + + async def initialize_services(*, fix_migration: bool = False) -> None: """Initialize all the services needed.""" # Test cache connection @@ -164,10 +239,13 @@ async def initialize_services(*, fix_migration: bool = False) -> None: # Setup the superuser await asyncio.to_thread(initialize_database, fix_migration=fix_migration) async with get_db_service().with_async_session() as session: - await setup_superuser(get_service(ServiceType.SETTINGS_SERVICE), session) + settings_service = get_service(ServiceType.SETTINGS_SERVICE) + await setup_superuser(settings_service, session) try: await get_db_service().assign_orphaned_flows_to_superuser() except Exception as exc: msg = "Error assigning orphaned flows to the superuser" logger.exception(msg) raise RuntimeError(msg) from exc + await clean_transactions(settings_service, session) + await clean_vertex_builds(settings_service, session) From 9ceb18ad4f7e1d4eb6b5291a877cd1c5316e1376 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Tue, 19 Nov 2024 20:28:00 -0300 Subject: [PATCH 12/45] fix: reset tool mode after update component code (#4723) fix bug related to tool mode --- .../pages/FlowPage/components/nodeToolbarComponent/index.tsx | 5 ++++- 1 file changed, 4 insertions(+), 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 f751ac6cf615..3726d1102d0f 100644 --- a/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/nodeToolbarComponent/index.tsx @@ -783,7 +783,10 @@ export default function NodeToolbarComponent({ open={openModal} setOpen={setOpenModal} dynamic={true} - setNodeClass={handleNodeClass} + setNodeClass={(apiClassType, type) => { + handleNodeClass(apiClassType, type); + setToolMode(false); + }} nodeClass={data.node} value={data.node?.template[name].value ?? ""} > From d5010c5d0b5fd0996b03ad21f54f8c88a17c6f4e Mon Sep 17 00:00:00 2001 From: Deon Sanchez <69873175+deon-sanchez@users.noreply.github.com> Date: Wed, 20 Nov 2024 10:04:54 -0700 Subject: [PATCH 13/45] refactor: Improve GitHub link styling in AppHeader component (#4737) --- src/frontend/src/components/appHeaderComponent/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/src/components/appHeaderComponent/index.tsx b/src/frontend/src/components/appHeaderComponent/index.tsx index 29e1b68757da..feccf23e2aaf 100644 --- a/src/frontend/src/components/appHeaderComponent/index.tsx +++ b/src/frontend/src/components/appHeaderComponent/index.tsx @@ -83,7 +83,7 @@ export default function AppHeader(): JSX.Element { <> - - )} -
)}
-
field.tool_mode); return ( - <> +
{memoizedNodeToolbarComponent} + {isOutdated && !isUserEdited && ( +
+ + + Update Ready + + +
+ )}
)} @@ -432,7 +451,6 @@ export default function GenericNode({
)}
- {showNode && (
{/* increase height!! */} @@ -509,6 +527,6 @@ export default function GenericNode({
)}
- +
); } diff --git a/src/frontend/src/CustomNodes/hooks/use-check-code-validity.tsx b/src/frontend/src/CustomNodes/hooks/use-check-code-validity.tsx index 95a0b8124afc..e70f7e470dcc 100644 --- a/src/frontend/src/CustomNodes/hooks/use-check-code-validity.tsx +++ b/src/frontend/src/CustomNodes/hooks/use-check-code-validity.tsx @@ -1,7 +1,6 @@ import { componentsToIgnoreUpdate } from "@/constants/constants"; import { useEffect } from "react"; import { NodeDataType } from "../../types/flow"; -import { nodeNames } from "../../utils/styleUtils"; const useCheckCodeValidity = ( data: NodeDataType, diff --git a/src/frontend/src/components/ui/button.tsx b/src/frontend/src/components/ui/button.tsx index a4328af41730..0e190a42ea68 100644 --- a/src/frontend/src/components/ui/button.tsx +++ b/src/frontend/src/components/ui/button.tsx @@ -15,7 +15,9 @@ const buttonVariants = cva( outline: "border border-input hover:bg-input hover:text-accent-foreground ", primary: - "border bg-background text-secondary-foreground hover:bg-muted dark:hover:bg-muted hover:shadow-sm", + "border bg-background text-secondary-foreground hover:bg-muted hover:shadow-sm", + warning: + "bg-warning-foreground text-warning-text hover:bg-warning-foreground/90 hover:shadow-sm", secondary: "border border-muted bg-muted text-secondary-foreground hover:bg-secondary-foreground/5", ghost: diff --git a/src/frontend/src/style/index.css b/src/frontend/src/style/index.css index 27dda94f5705..58c46731cf0e 100644 --- a/src/frontend/src/style/index.css +++ b/src/frontend/src/style/index.css @@ -96,6 +96,10 @@ --note-blue: 213.3 96.9% 87.3%; /* hsl(213.3, 96.9%, 87.3%) */ --note-lime: 80.9 88.5% 79.6%; /* hsl(80.9, 88.5%, 79.6%) */ + --warning: 48 96.6% 76.7%; /* hsl(48, 96.6%, 76.7%) */ + --warning-foreground: 240 6% 10%; /* hsl(240, 6%, 10%) */ + --warning-text: 0 0% 100%; /* hsl(0, 0%, 100%) */ + --error-red: 0, 86%, 97%; /*hsla(0, 86%, 97%)*/ --error-red-border: 0, 96%, 89%; /*hsla(0,96%,89%)*/ @@ -204,6 +208,10 @@ --note-blue: 211.7 96.4% 78.4%; /* hsl(211.7, 96.4%, 78.4%) */ --note-lime: 82 84.5% 67.1%; /* hsl(82, 84.5%, 67.1%) */ + --warning: 45.9 96.7% 64.5%; /* hsl(45.9, 96.7%, 64.5%) */ + --warning-foreground: 240 6% 10%; /* hsl(240, 6%, 10%) */ + --warning-text: 0 0% 100%; /* hsl(0, 0%, 100%) */ + --node-selected: 234 89% 74%; --ice: #60a5fa; diff --git a/src/frontend/tailwind.config.mjs b/src/frontend/tailwind.config.mjs index d920e4ac0dc8..3455040f0768 100644 --- a/src/frontend/tailwind.config.mjs +++ b/src/frontend/tailwind.config.mjs @@ -108,6 +108,11 @@ const config = { "status-red": "var(--status-red)", "status-yellow": "var(--status-yellow)", "status-gray": "var(--status-gray)", + warning: { + DEFAULT: "hsl(var(--warning))", + foreground: "hsl(var(--warning-foreground))", + text: "hsl(var(--warning-text))", + }, "success-background": "var(--success-background)", "success-foreground": "var(--success-foreground)", "accent-pink": "hsl(var(--accent-pink))", From 673736807376b208afcac2070566436871a2d0b9 Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 21 Nov 2024 21:40:39 -0300 Subject: [PATCH 32/45] fix: Improve wait time in saveComponents.spec.ts (#4673) Improve the wait time in the saveComponents.spec.ts file to ensure that the necessary elements are loaded before interacting with them. This helps prevent potential errors and improves the reliability of the test. --- .../tests/core/features/saveComponents.spec.ts | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/frontend/tests/core/features/saveComponents.spec.ts b/src/frontend/tests/core/features/saveComponents.spec.ts index b6e0777d9853..7e969d88cfa7 100644 --- a/src/frontend/tests/core/features/saveComponents.spec.ts +++ b/src/frontend/tests/core/features/saveComponents.spec.ts @@ -17,14 +17,15 @@ test.describe("save component tests", () => { while (modalCount === 0) { await page.getByText("New Flow", { exact: true }).click(); - await page.waitForTimeout(3000); + await page.waitForSelector('[data-testid="modal-title"]', { + timeout: 3000, + }); modalCount = await page.getByTestId("modal-title")?.count(); } await page.waitForSelector('[data-testid="blank-flow"]', { timeout: 30000, }); await page.getByTestId("blank-flow").click(); - await page.waitForTimeout(1000); // Read your file into a buffer. const jsonContent = readFileSync( @@ -43,8 +44,6 @@ test.describe("save component tests", () => { return dt; }, jsonContent); - page.waitForTimeout(1000); - // Now dispatch await page.dispatchEvent( "//*[@id='react-flow-id']/div[1]/div[1]/div", @@ -95,7 +94,7 @@ test.describe("save component tests", () => { await page.getByTestId("more-options-modal").click(); await page.getByTestId("icon-SaveAll").click(); - + // timeout to handle case where there is already a saved component with the same name await page.waitForTimeout(1000); const replaceButton = await page.getByTestId("replace-button").isVisible(); @@ -105,7 +104,6 @@ test.describe("save component tests", () => { } await page.getByTestId("sidebar-search-input").click(); await page.getByTestId("sidebar-search-input").fill("group"); - await page.waitForTimeout(1000); await page .getByText("Group") From 69465368c17584cb32c3cd5c42ed3a2ccd61e80a Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Thu, 21 Nov 2024 21:43:40 -0300 Subject: [PATCH 33/45] fix: update edge z index when node is selected (#4765) Fixed edges zindex when node selected --- .../src/pages/FlowPage/components/PageComponent/index.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 3b336b35ef30..2397fef215cf 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -546,6 +546,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { onSelectionEnd={onSelectionEnd} onSelectionStart={onSelectionStart} connectionRadius={30} + elevateEdgesOnSelect={true} edgeTypes={edgeTypes} connectionLineComponent={ConnectionLineComponent} onDragOver={onDragOver} From 725a25628567f6f8183626b0fb1becc828e62e03 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Thu, 21 Nov 2024 21:43:58 -0300 Subject: [PATCH 34/45] fix: remove useSidebar to fix errors of SidebarWrapper when opening playground page (#4767) * Added open to data of wrapper * Fix PageComponent and Header to not use useSidebar anymore --- src/frontend/src/components/ui/sidebar.tsx | 1 + .../FlowPage/components/PageComponent/index.tsx | 10 +++------- .../pages/MainPage/components/header/index.tsx | 15 +++------------ 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src/frontend/src/components/ui/sidebar.tsx b/src/frontend/src/components/ui/sidebar.tsx index 0ea0817d76e6..72c83dc0bf53 100644 --- a/src/frontend/src/components/ui/sidebar.tsx +++ b/src/frontend/src/components/ui/sidebar.tsx @@ -131,6 +131,7 @@ const SidebarProvider = React.forwardRef< "group/sidebar-wrapper flex h-full w-full text-foreground has-[[data-variant=inset]]:bg-background", className, )} + data-open={open} ref={ref} {...props} > diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index 2397fef215cf..c2313a4e504c 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -6,7 +6,7 @@ import CanvasControls, { import FlowToolbar from "@/components/flowToolbarComponent"; import ForwardedIconComponent from "@/components/genericIconComponent"; import LoadingComponent from "@/components/loadingComponent"; -import { SidebarTrigger, useSidebar } from "@/components/ui/sidebar"; +import { SidebarTrigger } from "@/components/ui/sidebar"; import { COLOR_OPTIONS, NOTE_NODE_MIN_HEIGHT, @@ -504,8 +504,6 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { reactFlowWrapper.current?.style.setProperty("--selected", accentColor); }; - const { open } = useSidebar(); - useEffect(() => { const handleGlobalMouseMove = (event) => { if (isAddingNote) { @@ -592,9 +590,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { button]:border-0 [&>button]:bg-background hover:[&>button]:bg-accent", - open - ? "pointer-events-none -translate-x-full opacity-0" - : "pointer-events-auto opacity-100", + "pointer-events-auto opacity-100 group-data-[open=true]/sidebar-wrapper:pointer-events-none group-data-[open=true]/sidebar-wrapper:-translate-x-full group-data-[open=true]/sidebar-wrapper:opacity-0", )} position="top-left" > @@ -603,7 +599,7 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { name="PanelRightClose" className="h-4 w-4" /> - Components + Components { const [debouncedSearch, setDebouncedSearch] = useState(""); - const { open } = useSidebar(); // Debounce the setSearch function from the parent const debouncedSetSearch = useCallback( @@ -57,15 +55,8 @@ const HeaderComponent = ({ className="flex items-center pb-8 text-xl font-semibold" data-testid="mainpage_title" > -
-
+
+
Date: Thu, 21 Nov 2024 19:44:14 -0500 Subject: [PATCH 35/45] fix: Error in SerpAPI search: cannot access local variable 'wrapper' where it is not associated with a value (#4758) Update serp_api.py fixes: Error: Error in SerpAPI search: cannot access local variable 'wrapper' where it is not associated with a value --- src/backend/base/langflow/components/tools/serp_api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/components/tools/serp_api.py b/src/backend/base/langflow/components/tools/serp_api.py index 38ce52055272..b0ee3d552fd6 100644 --- a/src/backend/base/langflow/components/tools/serp_api.py +++ b/src/backend/base/langflow/components/tools/serp_api.py @@ -56,17 +56,17 @@ def _build_wrapper(self, params: dict[str, Any] | None = None) -> SerpAPIWrapper return SerpAPIWrapper(serpapi_api_key=self.serpapi_api_key) def build_tool(self) -> Tool: - wrapper = self._build_wrapper(self.search_params) # noqa: F841 + wrapper = self._build_wrapper(self.search_params) def search_func( query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100 ) -> list[dict[str, Any]]: try: - # rebuild the wrapper if params are provided + local_wrapper = wrapper if params: - wrapper = self._build_wrapper(params) + local_wrapper = self._build_wrapper(params) - full_results = wrapper.results(query) + full_results = local_wrapper.results(query) organic_results = full_results.get("organic_results", [])[:max_results] limited_results = [] From 76890822b68ab2a08671bc167e6fb9011207adcd Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Thu, 21 Nov 2024 21:44:44 -0300 Subject: [PATCH 36/45] chore: remove legacy component checks and alerts (#4757) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 📝 (PageComponent/index.tsx): Remove redundant useEffect hook that sets notice data for old components 📝 (flowStore.ts): Remove unnecessary check for old components in selection nodes in useFlowStore function --- .../pages/FlowPage/components/PageComponent/index.tsx | 9 --------- src/frontend/src/stores/flowStore.ts | 9 +-------- 2 files changed, 1 insertion(+), 17 deletions(-) diff --git a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx index c2313a4e504c..2476b0438791 100644 --- a/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx +++ b/src/frontend/src/pages/FlowPage/components/PageComponent/index.tsx @@ -181,15 +181,6 @@ export default function Page({ view }: { view?: boolean }): JSX.Element { Object.keys(types).length > 0 && !isFetching; - useEffect(() => { - if (checkOldComponents({ nodes })) { - setNoticeData({ - title: - "Components created before Langflow 1.0 may be unstable. Ensure components are up to date.", - }); - } - }, [currentFlowId]); - useEffect(() => { useFlowStore.setState({ autoSaveFlow }); }); diff --git a/src/frontend/src/stores/flowStore.ts b/src/frontend/src/stores/flowStore.ts index c5b87114cd44..dec02967de92 100644 --- a/src/frontend/src/stores/flowStore.ts +++ b/src/frontend/src/stores/flowStore.ts @@ -347,14 +347,7 @@ const useFlowStore = create((set, get) => ({ selection.nodes.some((node) => edge.target === node.id), ); } - if (selection.nodes) { - if (checkOldComponents({ nodes: selection.nodes ?? [] })) { - useAlertStore.getState().setNoticeData({ - title: - "Components created before Langflow 1.0 may be unstable. Ensure components are up to date.", - }); - } - } + let minimumX = Infinity; let minimumY = Infinity; let idsMap = {}; From 226453a6069e746d31ecbc254dac22e763e1a496 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Thu, 21 Nov 2024 21:45:01 -0300 Subject: [PATCH 37/45] refactor: Replace InputComponent with Select dropdown (#4719) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔧 (nodeToolbarComponent/index.tsx): improve positioning logic for node toolbar based on scale and tool mode to enhance user experience * ✨ (GlobalVariableModal.tsx): Refactor type selection in GlobalVariableModal to use a dropdown select component for better user experience and consistency. * Revert "🔧 (nodeToolbarComponent/index.tsx): improve positioning logic for node toolbar based on scale and tool mode to enhance user experience" This reverts commit 322ba353a6984fcf870cba8c82c26689381842da. * ✨ (GlobalVariableModal.tsx): Add data-testid attribute to SelectTrigger for testing purposes 🔧 (globalVariables.spec.ts, userSettings.spec.ts): Update test scripts to interact with the SelectTrigger using data-testid attribute for consistency and improved testing accuracy * 📝 (userSettings.spec.ts): remove commented out code for focusElementsOnBoard function to improve code readability and maintainability --- .../GlobalVariableModal.tsx | 40 ++++++++++++++----- .../core/features/globalVariables.spec.ts | 9 ++--- .../extended/features/userSettings.spec.ts | 22 +--------- 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/src/frontend/src/components/GlobalVariableModal/GlobalVariableModal.tsx b/src/frontend/src/components/GlobalVariableModal/GlobalVariableModal.tsx index 116e9d0951c0..bda803f07cfb 100644 --- a/src/frontend/src/components/GlobalVariableModal/GlobalVariableModal.tsx +++ b/src/frontend/src/components/GlobalVariableModal/GlobalVariableModal.tsx @@ -14,6 +14,13 @@ import ForwardedIconComponent from "../genericIconComponent"; import InputComponent from "../inputComponent"; import { Input } from "../ui/input"; import { Label } from "../ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "../ui/select"; import { Textarea } from "../ui/textarea"; import sortByName from "./utils/sort-by-name"; @@ -155,17 +162,28 @@ export default function GlobalVariableModal({ placeholder="Insert a name for the variable..." > - { - setType(e); - }} - selectedOption={type} - password={false} - options={["Generic", "Credential"]} - placeholder="Choose a type for the variable..." - id={"type-global-variables"} - > + + + {type === "Credential" ? ( { await page .getByPlaceholder("Insert a name for the variable...") .fill(randomName); - await page.getByTestId("anchor-popover-anchor-type-global-variables").click(); - await page.getByPlaceholder("Search options...").fill("Generic"); - await page.waitForTimeout(1000); - await page.getByText("Generic", { exact: true }).last().isVisible(); - await page.getByText("Generic", { exact: true }).last().click(); - - await page.getByTestId("popover-anchor-type-global-variables").click(); - await page.waitForTimeout(1000); - await page.getByPlaceholder("Search options...").fill("Generic"); + await page.getByTestId("select-type-global-variables").first().click(); await page.getByText("Generic", { exact: true }).last().isVisible(); await page.getByText("Generic", { exact: true }).last().click(); @@ -108,18 +100,6 @@ test("should interact with global variables", async ({ page }) => { await page.waitForTimeout(2000); - // const focusElementsOnBoard = async ({ page }) => { - // await page.waitForSelector( - // '[aria-label="Press Space to toggle all rows selection (unchecked)"]', - // { timeout: 30000, state: "visible" }, - // ); - // const focusElements = await page - // .getByLabel("Press Space to toggle all rows selection (unchecked)") - // .first(); - // await focusElements.click(); - // }; - // await focusElementsOnBoard({ page }); - await page.locator(".ag-checkbox-input").first().click(); await page.getByTestId("icon-Trash2").click(); await page.getByText("No data available").isVisible(); From b1a552fa9ed7d4c4eabb90642f4b81f24775f676 Mon Sep 17 00:00:00 2001 From: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Thu, 21 Nov 2024 22:01:37 -0300 Subject: [PATCH 38/45] fix: refactor get all to fix types not being fetched before checking for outdated components (#4762) * Added use get types to fetch types from backend using tanstack * Updated typesStore to use new set types * Updated project to not use getTypes anymore * deleted unused getTypes * add tests * [autofix.ci] apply automated fixes * fix tests --------- Co-authored-by: cristhianzl Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Gabriel Luiz Freitas Almeida --- .../components/FlowMenu/index.tsx | 6 +- .../src/controllers/API/helpers/constants.ts | 1 + .../API/queries/flows/use-get-types.ts | 37 + .../API/queries/folders/use-get-folders.ts | 5 +- src/frontend/src/pages/AppInitPage/index.tsx | 18 +- src/frontend/src/pages/FlowPage/index.tsx | 2 - .../pages/MainPage/components/list/index.tsx | 1 + .../pages/GlobalVariablesPage/index.tsx | 12 +- src/frontend/src/pages/ViewPage/index.tsx | 2 - src/frontend/src/stores/typesStore.ts | 39 +- src/frontend/src/types/zustand/types/index.ts | 1 - src/frontend/tests/assets/outdated_flow.json | 1081 +++++++++++++++++ .../features/outdated-message.spec.ts | 58 + 13 files changed, 1203 insertions(+), 60 deletions(-) create mode 100644 src/frontend/src/controllers/API/queries/flows/use-get-types.ts create mode 100644 src/frontend/tests/assets/outdated_flow.json create mode 100644 src/frontend/tests/extended/features/outdated-message.spec.ts diff --git a/src/frontend/src/components/appHeaderComponent/components/FlowMenu/index.tsx b/src/frontend/src/components/appHeaderComponent/components/FlowMenu/index.tsx index 8f508d472824..09ee6adf3674 100644 --- a/src/frontend/src/components/appHeaderComponent/components/FlowMenu/index.tsx +++ b/src/frontend/src/components/appHeaderComponent/components/FlowMenu/index.tsx @@ -28,8 +28,8 @@ import useAlertStore from "@/stores/alertStore"; import useFlowsManagerStore from "@/stores/flowsManagerStore"; import useFlowStore from "@/stores/flowStore"; import { useShortcutsStore } from "@/stores/shortcuts"; -import { useTypesStore } from "@/stores/typesStore"; import { cn } from "@/utils/utils"; +import { useQueryClient } from "@tanstack/react-query"; export const MenuBar = ({}: {}): JSX.Element => { const shortcuts = useShortcutsStore((state) => state.shortcuts); @@ -44,8 +44,8 @@ export const MenuBar = ({}: {}): JSX.Element => { const uploadFlow = useUploadFlow(); const navigate = useCustomNavigate(); const isBuilding = useFlowStore((state) => state.isBuilding); - const getTypes = useTypesStore((state) => state.getTypes); const saveFlow = useSaveFlow(); + const queryClient = useQueryClient(); const autoSaving = useFlowsManagerStore((state) => state.autoSaving); const currentFlow = useFlowStore((state) => state.currentFlow); const currentSavedFlow = useFlowsManagerStore((state) => state.currentFlow); @@ -75,7 +75,7 @@ export const MenuBar = ({}: {}): JSX.Element => { } function handleReloadComponents() { - getTypes(true).then(() => { + queryClient.prefetchQuery({ queryKey: ["useGetTypes"] }).then(() => { setSuccessData({ title: "Components reloaded successfully" }); }); } diff --git a/src/frontend/src/controllers/API/helpers/constants.ts b/src/frontend/src/controllers/API/helpers/constants.ts index 6a63eac702f9..2d8809cc13ff 100644 --- a/src/frontend/src/controllers/API/helpers/constants.ts +++ b/src/frontend/src/controllers/API/helpers/constants.ts @@ -22,6 +22,7 @@ export const URLs = { CONFIG: `config`, STARTER_PROJECTS: `starter-projects`, SIDEBAR_CATEGORIES: `sidebar_categories`, + ALL: `all`, } as const; export function getURL(key: keyof typeof URLs, params: any = {}) { diff --git a/src/frontend/src/controllers/API/queries/flows/use-get-types.ts b/src/frontend/src/controllers/API/queries/flows/use-get-types.ts new file mode 100644 index 000000000000..300975ca9158 --- /dev/null +++ b/src/frontend/src/controllers/API/queries/flows/use-get-types.ts @@ -0,0 +1,37 @@ +import useFlowsManagerStore from "@/stores/flowsManagerStore"; +import { useTypesStore } from "@/stores/typesStore"; +import { APIObjectType, useQueryFunctionType } from "../../../../types/api"; +import { api } from "../../api"; +import { getURL } from "../../helpers/constants"; +import { UseRequestProcessor } from "../../services/request-processor"; + +export const useGetTypes: useQueryFunctionType = (options) => { + const { query } = UseRequestProcessor(); + const setLoading = useFlowsManagerStore((state) => state.setIsLoading); + const setTypes = useTypesStore((state) => state.setTypes); + + const getTypesFn = async () => { + try { + const response = await api.get( + `${getURL("ALL")}?force_refresh=true`, + ); + const data = response?.data; + setTypes(data); + return data; + } catch { + (error) => { + console.error("An error has occurred while fetching types."); + console.log(error); + setLoading(false); + throw error; + }; + } + }; + + const queryResult = query(["useGetTypes"], getTypesFn, { + refetchOnWindowFocus: false, + ...options, + }); + + return queryResult; +}; diff --git a/src/frontend/src/controllers/API/queries/folders/use-get-folders.ts b/src/frontend/src/controllers/API/queries/folders/use-get-folders.ts index 00f00b18459e..0c554c302c47 100644 --- a/src/frontend/src/controllers/API/queries/folders/use-get-folders.ts +++ b/src/frontend/src/controllers/API/queries/folders/use-get-folders.ts @@ -1,4 +1,4 @@ -import { DEFAULT_FOLDER, STARTER_FOLDER_NAME } from "@/constants/constants"; +import { DEFAULT_FOLDER } from "@/constants/constants"; import { FolderType } from "@/pages/MainPage/entities"; import useAuthStore from "@/stores/authStore"; import { useFolderStore } from "@/stores/foldersStore"; @@ -29,10 +29,9 @@ export const useGetFoldersQuery: useQueryFunctionType< const myCollectionId = data?.find((f) => f.name === DEFAULT_FOLDER)?.id; setMyCollectionId(myCollectionId); setFolders(data); - const { getTypes, types } = useTypesStore.getState(); + const { types } = useTypesStore.getState(); await refreshFlows({ get_all: true, header_flows: true }); - if (!types || Object.keys(types).length === 0) await getTypes(); return data; }; diff --git a/src/frontend/src/pages/AppInitPage/index.tsx b/src/frontend/src/pages/AppInitPage/index.tsx index 8901b561abd4..2eb8b2976faf 100644 --- a/src/frontend/src/pages/AppInitPage/index.tsx +++ b/src/frontend/src/pages/AppInitPage/index.tsx @@ -1,6 +1,7 @@ import { useGetAutoLogin } from "@/controllers/API/queries/auth"; import { useGetConfig } from "@/controllers/API/queries/config/use-get-config"; import { useGetBasicExamplesQuery } from "@/controllers/API/queries/flows/use-get-basic-examples"; +import { useGetTypes } from "@/controllers/API/queries/flows/use-get-types"; import { useGetFoldersQuery } from "@/controllers/API/queries/folders/use-get-folders"; import { useGetTagsQuery } from "@/controllers/API/queries/store"; import { useGetGlobalVariables } from "@/controllers/API/queries/variables"; @@ -23,17 +24,16 @@ export function AppInitPage() { const { isFetched } = useGetAutoLogin({ enabled: isLoaded }); useGetVersionQuery({ enabled: isFetched }); useGetConfig({ enabled: isFetched }); - useGetGlobalVariables({ enabled: isFetched }); - useGetBasicExamplesQuery({ enabled: isFetched }); - useGetTagsQuery({ enabled: isFetched }); - const { refetch: refetchFolders } = useGetFoldersQuery({ - enabled: isFetched, - }); + const { isFetched: typesLoaded } = useGetTypes({ enabled: isFetched }); + useGetGlobalVariables({ enabled: typesLoaded }); + useGetBasicExamplesQuery({ enabled: typesLoaded }); + useGetTagsQuery({ enabled: typesLoaded }); + + useGetFoldersQuery({ enabled: typesLoaded }); useEffect(() => { if (isFetched) { refreshStars(); - refetchFolders(); } }, [isFetched]); @@ -49,11 +49,11 @@ export function AppInitPage() { //need parent component with width and height <> {isLoaded ? ( - (isLoading || !isFetched) && + (isLoading || !isFetched || !typesLoaded) && ) : ( )} - {isFetched && } + {isFetched && typesLoaded && } ); } diff --git a/src/frontend/src/pages/FlowPage/index.tsx b/src/frontend/src/pages/FlowPage/index.tsx index 45655a2d311a..c0d4806872af 100644 --- a/src/frontend/src/pages/FlowPage/index.tsx +++ b/src/frontend/src/pages/FlowPage/index.tsx @@ -42,7 +42,6 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element { const { mutateAsync: refreshFlows } = useGetRefreshFlows(); const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading); - const getTypes = useTypesStore((state) => state.getTypes); const types = useTypesStore((state) => state.types); const updatedAt = currentSavedFlow?.updated_at; @@ -118,7 +117,6 @@ export default function FlowPage({ view }: { view?: boolean }): JSX.Element { } else if (!flows) { setIsLoading(true); await refreshFlows({ get_all: true, header_flows: true }); - if (!types || Object.keys(types).length === 0) await getTypes(); setIsLoading(false); } }; diff --git a/src/frontend/src/pages/MainPage/components/list/index.tsx b/src/frontend/src/pages/MainPage/components/list/index.tsx index 9b4f078d0ebc..a1ab295dad3f 100644 --- a/src/frontend/src/pages/MainPage/components/list/index.tsx +++ b/src/frontend/src/pages/MainPage/components/list/index.tsx @@ -114,6 +114,7 @@ const ListComponent = ({ flowData }: { flowData: FlowType }) => { className={`my-2 flex flex-row bg-background ${ isComponent ? "cursor-default" : "cursor-pointer" } group justify-between rounded-lg border border-border p-4 hover:border-placeholder-foreground hover:shadow-sm`} + data-testid="list-card" > {/* left side */}
state.setErrorData); const [openModal, setOpenModal] = useState(false); const initialData = useRef(undefined); - const getTypes = useTypesStore((state) => state.getTypes); const BadgeRenderer = (props) => { return props.value !== "" ? (
@@ -40,11 +35,6 @@ export default function GlobalVariablesPage() { ); }; - useEffect(() => { - //get the components to build the Aplly To Fields dropdown - getTypes(true); - }, []); - const DropdownEditor = ({ options, value, onValueChange }) => { return ( diff --git a/src/frontend/src/pages/ViewPage/index.tsx b/src/frontend/src/pages/ViewPage/index.tsx index 6184d925286b..7e58eb1f83bb 100644 --- a/src/frontend/src/pages/ViewPage/index.tsx +++ b/src/frontend/src/pages/ViewPage/index.tsx @@ -16,7 +16,6 @@ export default function ViewPage() { const currentFlowId = useFlowsManagerStore((state) => state.currentFlowId); const { mutateAsync: refreshFlows } = useGetRefreshFlows(); const setIsLoading = useFlowsManagerStore((state) => state.setIsLoading); - const getTypes = useTypesStore((state) => state.getTypes); const types = useTypesStore((state) => state.types); // Set flow tab id @@ -34,7 +33,6 @@ export default function ViewPage() { } else if (!flows) { setIsLoading(true); await refreshFlows({ get_all: true, header_flows: true }); - if (!types || Object.keys(types).length === 0) await getTypes(); setIsLoading(false); } }; diff --git a/src/frontend/src/stores/typesStore.ts b/src/frontend/src/stores/typesStore.ts index 2aedc0d6532a..93b2afb20cf7 100644 --- a/src/frontend/src/stores/typesStore.ts +++ b/src/frontend/src/stores/typesStore.ts @@ -1,5 +1,4 @@ import { create } from "zustand"; -import { getAll } from "../controllers/API"; import { APIDataType } from "../types/api"; import { TypesStoreType } from "../types/zustand/types"; import { @@ -7,7 +6,6 @@ import { templatesGenerator, typesGenerator, } from "../utils/reactflowUtils"; -import useFlowsManagerStore from "./flowsManagerStore"; export const useTypesStore = create((set, get) => ({ ComponentFields: new Set(), @@ -20,33 +18,16 @@ export const useTypesStore = create((set, get) => ({ types: {}, templates: {}, data: {}, - getTypes: (force_refresh: boolean = true) => { - return new Promise(async (resolve, reject) => { - const setLoading = useFlowsManagerStore.getState().setIsLoading; - getAll(force_refresh) - .then((response) => { - const data = response?.data; - set((old) => ({ - types: typesGenerator(data), - data: { ...old.data, ...data }, - ComponentFields: extractFieldsFromComponenents({ - ...old.data, - ...data, - }), - templates: templatesGenerator(data), - })); - resolve(); - }) - .catch((error) => { - console.error("An error has occurred while fetching types."); - console.log(error); - setLoading(false); - reject(); - }); - }); - }, - setTypes: (newState: {}) => { - set({ types: newState }); + setTypes: (data: APIDataType) => { + set((old) => ({ + types: typesGenerator(data), + data: { ...old.data, ...data }, + ComponentFields: extractFieldsFromComponenents({ + ...old.data, + ...data, + }), + templates: templatesGenerator(data), + })); }, setTemplates: (newState: {}) => { set({ templates: newState }); diff --git a/src/frontend/src/types/zustand/types/index.ts b/src/frontend/src/types/zustand/types/index.ts index 1f2fa1ed2e40..8692b039c165 100644 --- a/src/frontend/src/types/zustand/types/index.ts +++ b/src/frontend/src/types/zustand/types/index.ts @@ -7,7 +7,6 @@ export type TypesStoreType = { setTemplates: (newState: {}) => void; data: APIDataType; setData: (newState: {}) => void; - getTypes: (force_refresh?: boolean) => Promise; ComponentFields: Set; setComponentFields: (fields: Set) => void; addComponentField: (field: string) => void; diff --git a/src/frontend/tests/assets/outdated_flow.json b/src/frontend/tests/assets/outdated_flow.json new file mode 100644 index 000000000000..66ca450da57e --- /dev/null +++ b/src/frontend/tests/assets/outdated_flow.json @@ -0,0 +1,1081 @@ +{ + "id": "cb35184a-3446-4074-9ec7-8a935e980114", + "data": { + "edges": [ + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-KovKB", + "name": "message", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "user_message", + "id": "Prompt-Xz9bN", + "inputTypes": ["Message", "Text"], + "type": "str" + } + }, + "id": "reactflow__edge-ChatInput-KovKB{œdataTypeœ:œChatInputœ,œidœ:œChatInput-KovKBœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-Xz9bN{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-Xz9bNœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "source": "ChatInput-KovKB", + "sourceHandle": "{œdataTypeœ:œChatInputœ,œidœ:œChatInput-KovKBœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}", + "target": "Prompt-Xz9bN", + "targetHandle": "{œfieldNameœ:œuser_messageœ,œidœ:œPrompt-Xz9bNœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "Prompt", + "id": "Prompt-Xz9bN", + "name": "prompt", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "OpenAIModel-pqHDB", + "inputTypes": ["Message"], + "type": "str" + } + }, + "id": "reactflow__edge-Prompt-Xz9bN{œdataTypeœ:œPromptœ,œidœ:œPrompt-Xz9bNœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-pqHDB{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-pqHDBœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "Prompt-Xz9bN", + "sourceHandle": "{œdataTypeœ:œPromptœ,œidœ:œPrompt-Xz9bNœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}", + "target": "OpenAIModel-pqHDB", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-pqHDBœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "OpenAIModel", + "id": "OpenAIModel-pqHDB", + "name": "text_output", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "ChatOutput-NasE4", + "inputTypes": ["Message"], + "type": "str" + } + }, + "id": "reactflow__edge-OpenAIModel-pqHDB{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-pqHDBœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-NasE4{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-NasE4œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "OpenAIModel-pqHDB", + "sourceHandle": "{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-pqHDBœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}", + "target": "ChatOutput-NasE4", + "targetHandle": "{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-NasE4œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "Memory", + "id": "Memory-x4ENQ", + "name": "messages_text", + "output_types": ["Message"] + }, + "targetHandle": { + "fieldName": "context", + "id": "Prompt-Xz9bN", + "inputTypes": ["Message", "Text"], + "type": "str" + } + }, + "id": "reactflow__edge-Memory-x4ENQ{œdataTypeœ:œMemoryœ,œidœ:œMemory-x4ENQœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-Xz9bN{œfieldNameœ:œcontextœ,œidœ:œPrompt-Xz9bNœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "source": "Memory-x4ENQ", + "sourceHandle": "{œdataTypeœ:œMemoryœ,œidœ:œMemory-x4ENQœ,œnameœ:œmessages_textœ,œoutput_typesœ:[œMessageœ]}", + "target": "Prompt-Xz9bN", + "targetHandle": "{œfieldNameœ:œcontextœ,œidœ:œPrompt-Xz9bNœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}" + } + ], + "nodes": [ + { + "data": { + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "id": "Prompt-Xz9bN", + "node": { + "base_classes": ["Message"], + "beta": false, + "conditional_paths": [], + "custom_fields": { "template": ["context", "user_message"] }, + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "documentation": "", + "edited": false, + "field_order": ["template"], + "frozen": false, + "icon": "prompts", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Prompt Message", + "method": "build_prompt", + "name": "prompt", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(\n self,\n ) -> Message:\n prompt = await Message.from_template_and_variables(**self._attributes)\n self.status = prompt.text\n return prompt\n\n def _update_template(self, frontend_node: dict):\n prompt_template = frontend_node[\"template\"][\"template\"][\"value\"]\n custom_fields = frontend_node[\"custom_fields\"]\n frontend_node_template = frontend_node[\"template\"]\n _ = process_prompt_template(\n template=prompt_template,\n name=\"template\",\n custom_fields=custom_fields,\n frontend_node_template=frontend_node_template,\n )\n return frontend_node\n\n def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"\n This function is called after the code validation is done.\n \"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n" + }, + "context": { + "advanced": false, + "display_name": "context", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": ["Message", "Text"], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "context", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "template": { + "advanced": false, + "display_name": "Template", + "dynamic": false, + "info": "", + "list": false, + "load_from_db": false, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "type": "prompt", + "value": "{context}\n\nUser: {user_message}\nAI: " + }, + "user_message": { + "advanced": false, + "display_name": "user_message", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": ["Message", "Text"], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "user_message", + "password": false, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + } + } + }, + "type": "Prompt" + }, + "dragging": false, + "height": 517, + "id": "Prompt-Xz9bN", + "position": { "x": 1880.8227904110583, "y": 625.8049209882275 }, + "positionAbsolute": { "x": 1880.8227904110583, "y": 625.8049209882275 }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Get chat inputs from the Playground.", + "display_name": "Chat Input", + "id": "ChatInput-KovKB", + "node": { + "base_classes": ["Message"], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Get chat inputs from the Playground.", + "display_name": "Chat Input", + "documentation": "", + "edited": false, + "field_order": [ + "input_value", + "store_message", + "sender", + "sender_name", + "session_id", + "files" + ], + "frozen": false, + "icon": "ChatInput", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, FileInput, MessageTextInput, MultilineInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_NAME_USER\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"ChatInput\"\n name = \"ChatInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n )\n\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + }, + "files": { + "advanced": true, + "display_name": "Files", + "dynamic": false, + "fileTypes": [ + "txt", + "md", + "mdx", + "csv", + "json", + "yaml", + "yml", + "xml", + "html", + "htm", + "pdf", + "docx", + "py", + "sh", + "sql", + "js", + "ts", + "tsx", + "jpg", + "jpeg", + "png", + "bmp", + "image" + ], + "file_path": "", + "info": "Files to be sent with the message.", + "list": true, + "name": "files", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "file", + "value": "" + }, + "input_value": { + "advanced": false, + "display_name": "Text", + "dynamic": false, + "info": "Message to be passed as input.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "sender": { + "advanced": true, + "display_name": "Sender Type", + "dynamic": false, + "info": "Type of sender.", + "name": "sender", + "options": ["Machine", "User"], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "User" + }, + "sender_name": { + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Name of the sender.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "User" + }, + "session_id": { + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "should_store_message": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Store Messages", + "dynamic": false, + "info": "Store the message in the history.", + "list": false, + "name": "should_store_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + } + } + }, + "type": "ChatInput" + }, + "dragging": false, + "height": 309, + "id": "ChatInput-KovKB", + "position": { "x": 1275.9262193671882, "y": 836.1228056896347 }, + "positionAbsolute": { "x": 1275.9262193671882, "y": 836.1228056896347 }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Generates text using OpenAI LLMs.", + "display_name": "OpenAI", + "id": "OpenAIModel-pqHDB", + "node": { + "base_classes": ["LanguageModel", "Message"], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Generates text using OpenAI LLMs.", + "display_name": "OpenAI", + "documentation": "", + "edited": false, + "field_order": [ + "input_value", + "max_tokens", + "model_kwargs", + "json_mode", + "output_schema", + "model_name", + "openai_api_base", + "openai_api_key", + "temperature", + "stream", + "system_message", + "seed" + ], + "frozen": false, + "icon": "OpenAI", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Text", + "method": "text_response", + "name": "text_output", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Language Model", + "method": "build_model", + "name": "model_output", + "selected": "LanguageModel", + "types": ["LanguageModel"], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "The OpenAI API Key to use for the OpenAI model.", + "input_types": ["Message"], + "load_from_db": true, + "name": "api_key", + "password": true, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "import operator\nfrom functools import reduce\n\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import OPENAI_MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n name = \"OpenAIModel\"\n\n inputs = LCModelComponent._base_inputs + [\n IntInput(\n name=\"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 range_spec=RangeSpec(min=0, max=128000),\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n advanced=False,\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n info=\"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"The OpenAI API Key to use for the OpenAI model.\",\n advanced=False,\n value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n # self.output_schema is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\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 or 0.1,\n seed=seed,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output # type: ignore\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n" + }, + "input_value": { + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "json_mode": { + "advanced": true, + "display_name": "JSON Mode", + "dynamic": false, + "info": "If True, it will output JSON regardless of passing a schema.", + "list": false, + "name": "json_mode", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "max_tokens": { + "advanced": true, + "display_name": "Max Tokens", + "dynamic": false, + "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", + "list": false, + "name": "max_tokens", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "int", + "value": "" + }, + "model_kwargs": { + "advanced": true, + "display_name": "Model Kwargs", + "dynamic": false, + "info": "", + "list": false, + "name": "model_kwargs", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "type": "dict", + "value": {} + }, + "model_name": { + "advanced": false, + "display_name": "Model Name", + "dynamic": false, + "info": "", + "name": "model_name", + "options": [ + "gpt-4o-mini", + "gpt-4o", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-4", + "gpt-3.5-turbo", + "gpt-3.5-turbo-0125" + ], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "gpt-4o" + }, + "openai_api_base": { + "advanced": true, + "display_name": "OpenAI API Base", + "dynamic": false, + "info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.", + "list": false, + "load_from_db": false, + "name": "openai_api_base", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "output_schema": { + "advanced": true, + "display_name": "Schema", + "dynamic": false, + "info": "The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.", + "list": true, + "name": "output_schema", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "type": "dict", + "value": {} + }, + "seed": { + "advanced": true, + "display_name": "Seed", + "dynamic": false, + "info": "The seed controls the reproducibility of the job.", + "list": false, + "name": "seed", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "int", + "value": 1 + }, + "stream": { + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "info": "Stream the response from the model. Streaming works only in Chat.", + "list": false, + "name": "stream", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "system_message": { + "advanced": true, + "display_name": "System Message", + "dynamic": false, + "info": "System message to pass to the model.", + "list": false, + "load_from_db": false, + "name": "system_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "temperature": { + "advanced": false, + "display_name": "Temperature", + "dynamic": false, + "info": "", + "list": false, + "name": "temperature", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "float", + "value": 0.1 + } + } + }, + "type": "OpenAIModel" + }, + "dragging": false, + "height": 623, + "id": "OpenAIModel-pqHDB", + "position": { "x": 2468.968379487559, "y": 560.0689522326683 }, + "positionAbsolute": { "x": 2468.968379487559, "y": 560.0689522326683 }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Display a chat message in the Playground.", + "display_name": "Chat Output", + "id": "ChatOutput-NasE4", + "node": { + "base_classes": ["Message"], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Display a chat message in the Playground.", + "display_name": "Chat Output", + "documentation": "", + "edited": false, + "field_order": [ + "input_value", + "store_message", + "sender", + "sender_name", + "session_id", + "data_template" + ], + "frozen": false, + "icon": "ChatOutput", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Message", + "method": "message_response", + "name": "message", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.memory import store_message\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER, MESSAGE_SENDER_AI\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"ChatOutput\"\n name = \"ChatOutput\"\n\n inputs = [\n MessageTextInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n def message_response(self) -> Message:\n message = Message(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n )\n if (\n self.session_id\n and isinstance(message, Message)\n and isinstance(message.text, str)\n and self.should_store_message\n ):\n store_message(\n message,\n flow_id=self.graph.flow_id,\n )\n self.message.value = message\n\n self.status = message\n return message\n" + }, + "data_template": { + "advanced": true, + "display_name": "Data Template", + "dynamic": false, + "info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "name": "data_template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{text}" + }, + "input_value": { + "advanced": false, + "display_name": "Text", + "dynamic": false, + "info": "Message to be passed as output.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "sender": { + "advanced": true, + "display_name": "Sender Type", + "dynamic": false, + "info": "Type of sender.", + "name": "sender", + "options": ["Machine", "User"], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine" + }, + "sender_name": { + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Name of the sender.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "AI" + }, + "session_id": { + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "should_store_message": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Store Messages", + "dynamic": false, + "info": "Store the message in the history.", + "list": false, + "name": "should_store_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + } + } + }, + "type": "ChatOutput" + }, + "height": 385, + "id": "ChatOutput-NasE4", + "position": { "x": 3083.1710516244116, "y": 701.521688846004 }, + "selected": false, + "type": "genericNode", + "width": 384 + }, + { + "data": { + "description": "Retrieves stored chat messages from Langflow tables or an external memory.", + "display_name": "Chat Memory", + "id": "Memory-x4ENQ", + "node": { + "base_classes": ["BaseChatMemory", "Data", "Message"], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Retrieves stored chat messages from Langflow tables or an external memory.", + "display_name": "Chat Memory", + "documentation": "", + "edited": false, + "field_order": [ + "memory", + "sender", + "sender_name", + "n_messages", + "session_id", + "order", + "template" + ], + "frozen": false, + "icon": "message-square-more", + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Messages (Data)", + "method": "retrieve_messages", + "name": "messages", + "selected": "Data", + "types": ["Data"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Messages (Text)", + "method": "retrieve_messages_as_text", + "name": "messages_text", + "selected": "Message", + "types": ["Message"], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Memory", + "method": "build_lc_memory", + "name": "lc_memory", + "selected": "BaseChatMemory", + "types": ["BaseChatMemory"], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from langchain.memory import ConversationBufferMemory\n\nfrom langflow.custom import Component\nfrom langflow.field_typing import BaseChatMemory\nfrom langflow.helpers.data import data_to_text\nfrom langflow.inputs import HandleInput\nfrom langflow.io import DropdownInput, IntInput, MessageTextInput, MultilineInput, Output\nfrom langflow.memory import LCBuiltinChatMemory, get_messages\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_USER\n\n\nclass MemoryComponent(Component):\n display_name = \"Chat Memory\"\n description = \"Retrieves stored chat messages from Langflow tables or an external memory.\"\n icon = \"message-square-more\"\n name = \"Memory\"\n\n inputs = [\n HandleInput(\n name=\"memory\",\n display_name=\"External Memory\",\n input_types=[\"BaseChatMessageHistory\"],\n info=\"Retrieve messages from an external memory. If empty, it will use the Langflow tables.\",\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER, \"Machine and User\"],\n value=\"Machine and User\",\n info=\"Filter by sender type.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Filter by sender name.\",\n advanced=True,\n ),\n IntInput(\n name=\"n_messages\",\n display_name=\"Number of Messages\",\n value=100,\n info=\"Number of messages to retrieve.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n DropdownInput(\n name=\"order\",\n display_name=\"Order\",\n options=[\"Ascending\", \"Descending\"],\n value=\"Ascending\",\n info=\"Order of the messages.\",\n advanced=True,\n ),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.\",\n value=\"{sender_name}: {text}\",\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Messages (Data)\", name=\"messages\", method=\"retrieve_messages\"),\n Output(display_name=\"Messages (Text)\", name=\"messages_text\", method=\"retrieve_messages_as_text\"),\n Output(display_name=\"Memory\", name=\"lc_memory\", method=\"build_lc_memory\"),\n ]\n\n def retrieve_messages(self) -> Data:\n sender = self.sender\n sender_name = self.sender_name\n session_id = self.session_id\n n_messages = self.n_messages\n order = \"DESC\" if self.order == \"Descending\" else \"ASC\"\n\n if sender == \"Machine and User\":\n sender = None\n\n if self.memory:\n # override session_id\n self.memory.session_id = session_id\n\n stored = self.memory.messages\n # langchain memories are supposed to return messages in ascending order\n if order == \"DESC\":\n stored = stored[::-1]\n if n_messages:\n stored = stored[:n_messages]\n stored = [Message.from_lc_message(m) for m in stored]\n if sender:\n expected_type = MESSAGE_SENDER_AI if sender == MESSAGE_SENDER_AI else MESSAGE_SENDER_USER\n stored = [m for m in stored if m.type == expected_type]\n else:\n stored = get_messages(\n sender=sender,\n sender_name=sender_name,\n session_id=session_id,\n limit=n_messages,\n order=order,\n )\n self.status = stored\n return stored\n\n def retrieve_messages_as_text(self) -> Message:\n stored_text = data_to_text(self.template, self.retrieve_messages())\n self.status = stored_text\n return Message(text=stored_text)\n\n def build_lc_memory(self) -> BaseChatMemory:\n if self.memory:\n chat_memory = self.memory\n else:\n chat_memory = LCBuiltinChatMemory(flow_id=self.flow_id, session_id=self.session_id)\n return ConversationBufferMemory(chat_memory=chat_memory)\n" + }, + "memory": { + "advanced": false, + "display_name": "External Memory", + "dynamic": false, + "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", + "input_types": ["BaseChatMessageHistory"], + "list": false, + "name": "memory", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "n_messages": { + "advanced": true, + "display_name": "Number of Messages", + "dynamic": false, + "info": "Number of messages to retrieve.", + "list": false, + "name": "n_messages", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "int", + "value": 100 + }, + "order": { + "advanced": true, + "display_name": "Order", + "dynamic": false, + "info": "Order of the messages.", + "name": "order", + "options": ["Ascending", "Descending"], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "Ascending" + }, + "sender": { + "advanced": true, + "display_name": "Sender Type", + "dynamic": false, + "info": "Filter by sender type.", + "name": "sender", + "options": ["Machine", "User", "Machine and User"], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine and User" + }, + "sender_name": { + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Filter by sender name.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "session_id": { + "advanced": true, + "display_name": "Session ID", + "dynamic": false, + "info": "The session ID of the chat. If empty, the current session ID parameter will be used.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "template": { + "advanced": true, + "display_name": "Template", + "dynamic": false, + "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", + "input_types": ["Message"], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{sender_name}: {text}" + } + } + }, + "type": "Memory" + }, + "dragging": false, + "height": 387, + "id": "Memory-x4ENQ", + "position": { "x": 1301.98330242754, "y": 422.33865605652574 }, + "positionAbsolute": { "x": 1301.98330242754, "y": 422.33865605652574 }, + "selected": false, + "type": "genericNode", + "width": 384 + } + ], + "viewport": { + "x": -377.45799796990354, + "y": 18.161555190942522, + "zoom": 0.45494095964690673 + } + }, + "description": "This project can be used as a starting point for building a Chat experience with user specific memory. You can set a different Session ID to start a new message history.", + "name": "Memory Chatbot (1)", + "last_tested_version": "1.0.15", + "endpoint_name": null, + "is_component": false +} diff --git a/src/frontend/tests/extended/features/outdated-message.spec.ts b/src/frontend/tests/extended/features/outdated-message.spec.ts new file mode 100644 index 000000000000..e88eaf1136f1 --- /dev/null +++ b/src/frontend/tests/extended/features/outdated-message.spec.ts @@ -0,0 +1,58 @@ +import { test } from "@playwright/test"; +import { readFileSync } from "fs"; + +test("user must be able outdated message on error", async ({ page }) => { + await page.goto("/"); + + let modalCount = 0; + try { + const modalTitleElement = await page?.getByTestId("modal-title"); + if (modalTitleElement) { + modalCount = await modalTitleElement.count(); + } + } catch (error) { + modalCount = 0; + } + + while (modalCount === 0) { + await page.getByText("New Flow", { exact: true }).click(); + await page.waitForTimeout(3000); + modalCount = await page.getByTestId("modal-title")?.count(); + } + await page.locator("span").filter({ hasText: "Close" }).first().click(); + + await page.locator("span").filter({ hasText: "My Collection" }).isVisible(); + // Read your file into a buffer. + const jsonContent = readFileSync("tests/assets/outdated_flow.json", "utf-8"); + + // Create the DataTransfer and File + const dataTransfer = await page.evaluateHandle((data) => { + const dt = new DataTransfer(); + // Convert the buffer to a hex array + const file = new File([data], "outdated_flow.json", { + type: "application/json", + }); + dt.items.add(file); + return dt; + }, jsonContent); + + // Now dispatch + await page.getByTestId("cards-wrapper").dispatchEvent("drop", { + dataTransfer, + }); + + await page.waitForTimeout(3000); + + await page.getByTestId("list-card").first().click(); + + await page + .getByTestId("popover-anchor-input-api_key") + .fill("this is a test to crash"); + + await page.getByTestId("button_run_chat output").click(); + + await page.waitForSelector("text=there are outdated components in the flow", { + timeout: 30000, + state: "visible", + }); +}); From 1532da59f50ebc9f269c5731b406bf74a70c87a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rog=C3=A9rio=20Chaves?= Date: Fri, 22 Nov 2024 02:41:39 +0100 Subject: [PATCH 39/45] fix: traces inputs and outputs not being sent to tracing services (#4669) * Fix node inputs not being captured, add runtime inputs as well to be captured by the tracers properly * Fix outputs missing on traces due to them being reset before ending the traces because of race conditions * Fallback to project name if none * Remove 'dynamic inputs' to stop sending the component code every time * fix: Add async flow name retrieval in graph building process * fix: Retrieve flow name from database when building graph from data * Fix: make session.exec call awaitable in chat API * Refactor `_get_flow_name` to manage session internally * Refactor session handling to use `async_session_scope` in chat API * Refactor test cases to remove unnecessary async usage in mock functions * [autofix.ci] apply automated fixes --------- Co-authored-by: Gabriel Luiz Freitas Almeida Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> --- src/backend/base/langflow/api/utils.py | 15 +++++++++++++- src/backend/base/langflow/api/v1/chat.py | 11 ++++++++-- .../custom/custom_component/component.py | 6 +++--- .../langflow/services/tracing/langwatch.py | 1 + .../base/langflow/services/tracing/service.py | 4 ++-- .../tests/unit/events/test_event_manager.py | 20 +++---------------- 6 files changed, 32 insertions(+), 25 deletions(-) diff --git a/src/backend/base/langflow/api/utils.py b/src/backend/base/langflow/api/utils.py index 264d20fab94b..8328f53ed260 100644 --- a/src/backend/base/langflow/api/utils.py +++ b/src/backend/base/langflow/api/utils.py @@ -17,7 +17,7 @@ from langflow.services.database.models.flow import Flow from langflow.services.database.models.transactions.model import TransactionTable from langflow.services.database.models.vertex_builds.model import VertexBuildTable -from langflow.services.deps import get_async_session, get_session +from langflow.services.deps import async_session_scope, get_async_session, get_session from langflow.services.store.utils import get_lf_version_from_pypi if TYPE_CHECKING: @@ -142,8 +142,21 @@ def format_elapsed_time(elapsed_time: float) -> str: return f"{minutes} {minutes_unit}, {seconds} {seconds_unit}" +async def _get_flow_name(flow_id: str) -> str: + async with async_session_scope() as session: + flow = await session.get(Flow, flow_id) + if flow is None: + msg = f"Flow {flow_id} not found" + raise ValueError(msg) + return flow.name + + async def build_graph_from_data(flow_id: str, payload: dict, **kwargs): """Build and cache the graph.""" + # Get flow name + if "flow_name" not in kwargs: + flow_name = await _get_flow_name(flow_id) + kwargs["flow_name"] = flow_name graph = Graph.from_payload(payload, flow_id, **kwargs) for vertex_id in graph.has_session_id_vertices: vertex = graph.get_vertex(vertex_id) diff --git a/src/backend/base/langflow/api/v1/chat.py b/src/backend/base/langflow/api/v1/chat.py index 5568decdaf88..ce31fdede331 100644 --- a/src/backend/base/langflow/api/v1/chat.py +++ b/src/backend/base/langflow/api/v1/chat.py @@ -11,6 +11,7 @@ from fastapi import APIRouter, BackgroundTasks, Body, HTTPException from fastapi.responses import StreamingResponse from loguru import logger +from sqlmodel import select from starlette.background import BackgroundTask from starlette.responses import ContentStream from starlette.types import Receive @@ -42,7 +43,8 @@ from langflow.schema.schema import OutputValue from langflow.services.cache.utils import CacheMiss from langflow.services.chat.service import ChatService -from langflow.services.deps import get_async_session, get_chat_service, get_telemetry_service +from langflow.services.database.models.flow.model import Flow +from langflow.services.deps import async_session_scope, get_async_session, get_chat_service, get_telemetry_service from langflow.services.telemetry.schema import ComponentPayload, PlaygroundPayload if TYPE_CHECKING: @@ -166,7 +168,12 @@ async def build_graph_and_get_order() -> tuple[list[str], list[str], Graph]: if not data: graph = await build_graph_from_db_no_cache(flow_id=flow_id_str, session=session) else: - graph = await build_graph_from_data(flow_id_str, data.model_dump(), user_id=str(current_user.id)) + async with async_session_scope() as new_session: + result = await new_session.exec(select(Flow.name).where(Flow.id == flow_id_str)) + flow_name = result.first() + graph = await build_graph_from_data( + flow_id_str, data.model_dump(), user_id=str(current_user.id), flow_name=flow_name + ) graph.validate_stream() if stop_component_id or start_component_id: try: diff --git a/src/backend/base/langflow/custom/custom_component/component.py b/src/backend/base/langflow/custom/custom_component/component.py index 67c3852fa721..96a4cd3b063d 100644 --- a/src/backend/base/langflow/custom/custom_component/component.py +++ b/src/backend/base/langflow/custom/custom_component/component.py @@ -801,9 +801,9 @@ def get_trace_as_inputs(self): for input_ in self.inputs if hasattr(input_, "trace_as_input") and input_.trace_as_input } - # Dynamic inputs - dynamic_inputs = {key: value for key, value in self._attributes.items() if key not in predefined_inputs} - return {**predefined_inputs, **dynamic_inputs} + # Runtime inputs + runtime_inputs = {name: input_.value for name, input_ in self._inputs.items() if hasattr(input_, "value")} + return {**predefined_inputs, **runtime_inputs} def get_trace_as_metadata(self): return { diff --git a/src/backend/base/langflow/services/tracing/langwatch.py b/src/backend/base/langflow/services/tracing/langwatch.py index 9c974ef28013..73b3ba200fa9 100644 --- a/src/backend/base/langflow/services/tracing/langwatch.py +++ b/src/backend/base/langflow/services/tracing/langwatch.py @@ -41,6 +41,7 @@ def __init__(self, trace_name: str, trace_type: str, project_name: str, trace_id self.spans: dict[str, ContextSpan] = {} name_without_id = " - ".join(trace_name.split(" - ")[0:-1]) + name_without_id = project_name if name_without_id == "None" else name_without_id self.trace.root_span.update( # nanoid to make the span_id globally unique, which is required for LangWatch for now span_id=f"{self.flow_id}-{nanoid.generate(size=6)}", diff --git a/src/backend/base/langflow/services/tracing/service.py b/src/backend/base/langflow/services/tracing/service.py index 65546cd101a3..bd146dbe3d88 100644 --- a/src/backend/base/langflow/services/tracing/service.py +++ b/src/backend/base/langflow/services/tracing/service.py @@ -184,6 +184,7 @@ def _end_traces(self, trace_id: str, trace_name: str, error: Exception | None = ) except Exception: # noqa: BLE001 logger.exception(f"Error ending trace {trace_name}") + self._reset_io() def _end_all_traces(self, outputs: dict, error: Exception | None = None) -> None: for tracer in self._tracers.values(): @@ -192,10 +193,10 @@ def _end_all_traces(self, outputs: dict, error: Exception | None = None) -> None tracer.end(self.inputs, outputs=self.outputs, error=error, metadata=outputs) except Exception: # noqa: BLE001 logger.exception("Error ending all traces") + self._reset_io() async def end(self, outputs: dict, error: Exception | None = None) -> None: await asyncio.to_thread(self._end_all_traces, outputs, error) - self._reset_io() await self.stop() def add_log(self, trace_name: str, log: Log) -> None: @@ -236,7 +237,6 @@ def _end_and_reset(self, trace_id: str, trace_name: str, error: Exception | None task = asyncio.create_task(asyncio.to_thread(self._end_traces, trace_id, trace_name, error)) self.end_trace_tasks.add(task) task.add_done_callback(self.end_trace_tasks.discard) - self._reset_io() def set_outputs( self, diff --git a/src/backend/tests/unit/events/test_event_manager.py b/src/backend/tests/unit/events/test_event_manager.py index 4b15518d1768..8da06b6f5145 100644 --- a/src/backend/tests/unit/events/test_event_manager.py +++ b/src/backend/tests/unit/events/test_event_manager.py @@ -36,21 +36,6 @@ def test_register_event_with_valid_name_and_no_callback(self): assert "on_test_event" in manager.events assert manager.events["on_test_event"].func == manager.send_event - # Sending an event with valid event_type and data using pytest-asyncio plugin - async def test_sending_event_with_valid_type_and_data_asyncio_plugin(self): - async def mock_queue_put_nowait(item): - await queue.put(item) - - queue = asyncio.Queue() - queue.put_nowait = mock_queue_put_nowait - manager = EventManager(queue) - manager.register_event("on_test_event", "test_type", manager.noop) - event_type = "test_type" - data = "test_data" - manager.send_event(event_type=event_type, data=data) - await queue.join() - assert queue.empty() - # Accessing a non-registered event callback via __getattr__ with the recommended fix def test_accessing_non_registered_event_callback_with_recommended_fix(self): queue = asyncio.Queue() @@ -70,7 +55,7 @@ def mock_callback(event_type: str, data: LoggableType): # Handling a large number of events in the queue def test_handling_large_number_of_events(self): - async def mock_queue_put_nowait(item): + def mock_queue_put_nowait(item): pass queue = asyncio.Queue() @@ -97,6 +82,7 @@ def mock_callback(event_type, data): # Sending an event with complex data and verifying successful event transmission async def test_sending_event_with_complex_data(self): queue = asyncio.Queue() + manager = EventManager(queue) manager.register_event("on_test_event", "test_type", manager.noop) data = {"key": "value", "nested": [1, 2, 3]} @@ -134,7 +120,7 @@ async def access_events(manager): # Checking the performance impact of frequent event registrations def test_performance_impact_frequent_registrations(self): - async def mock_callback(event_type: str, data: LoggableType): + def mock_callback(event_type: str, data: LoggableType): pass queue = asyncio.Queue() From 20f26a9ccf7b119a6bb4a680a78f5a67cbc572db Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Thu, 21 Nov 2024 23:03:51 -0300 Subject: [PATCH 40/45] fix: Improve waiting logic in tweksTest.spec.ts (#4674) Improve the waiting logic in the tweksTest.spec.ts file to ensure that the necessary elements are loaded before interacting with them. This includes using the waitForSelector function with appropriate timeouts for the modal and popover elements. --- .../tests/core/features/tweaksTest.spec.ts | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/frontend/tests/core/features/tweaksTest.spec.ts b/src/frontend/tests/core/features/tweaksTest.spec.ts index 61475f1a49b9..7d14dd33f9f2 100644 --- a/src/frontend/tests/core/features/tweaksTest.spec.ts +++ b/src/frontend/tests/core/features/tweaksTest.spec.ts @@ -14,13 +14,14 @@ test("curl_api_generation", async ({ page, context }) => { while (modalCount === 0) { await page.getByText("New Flow", { exact: true }).click(); - await page.waitForTimeout(3000); + await page.waitForSelector('[data-testid="modal-title"]', { + timeout: 3000, + }); modalCount = await page.getByTestId("modal-title")?.count(); } await page.getByTestId("side_nav_options_all-templates").click(); await page.getByRole("heading", { name: "Basic Prompting" }).click(); - await page.waitForTimeout(1000); await page.getByText("API", { exact: true }).click(); await page.getByRole("tab", { name: "cURL" }).click(); await page.getByTestId("icon-Copy").click(); @@ -37,7 +38,12 @@ test("curl_api_generation", async ({ page, context }) => { .first() .click(); - await page.waitForTimeout(1000); + await page.waitForSelector( + '[data-testid="popover-anchor-input-openai_api_base-edit"]', + { + timeout: 1000, + }, + ); await page .getByTestId("popover-anchor-input-openai_api_base-edit") @@ -79,7 +85,9 @@ test("check if tweaks are updating when someothing on the flow changes", async ( while (modalCount === 0) { await page.getByText("New Flow", { exact: true }).click(); - await page.waitForTimeout(3000); + await page.waitForSelector('[data-testid="modal-title"]', { + timeout: 3000, + }); modalCount = await page.getByTestId("modal-title")?.count(); } @@ -91,7 +99,9 @@ test("check if tweaks are updating when someothing on the flow changes", async ( await page.getByTestId("sidebar-search-input").click(); await page.getByTestId("sidebar-search-input").fill("Chroma"); - await page.waitForTimeout(1000); + await page.waitForSelector('[data-testid="vectorstoresChroma DB"]', { + timeout: 1000, + }); await page .getByTestId("vectorstoresChroma DB") From 84fa2fcc2f4b2ee13e956c4d79a6d8ad6930070a Mon Sep 17 00:00:00 2001 From: Samuel Matioli <101875785+smatiolids@users.noreply.github.com> Date: Thu, 21 Nov 2024 23:25:18 -0300 Subject: [PATCH 41/45] fix(astradb_tools): sets some options as Advanced and improves descriptions (#4732) Setting some options as Advanced --- src/backend/base/langflow/components/tools/astradb.py | 5 +++-- src/backend/base/langflow/components/tools/astradb_cql.py | 7 +++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/backend/base/langflow/components/tools/astradb.py b/src/backend/base/langflow/components/tools/astradb.py index 8355eb3550f3..2153b8082181 100644 --- a/src/backend/base/langflow/components/tools/astradb.py +++ b/src/backend/base/langflow/components/tools/astradb.py @@ -11,8 +11,8 @@ class AstraDBToolComponent(LCToolComponent): display_name: str = "Astra DB" - description: str = "Create a tool to get data from DataStax Astra DB Collection" - documentation: str = "https://astra.datastax.com" + description: str = "Create a tool to get transactional data from DataStax Astra DB Collection" + documentation: str = "https://docs.langflow.org/Components/components-tools#astra-db-tool" icon: str = "AstraDB" inputs = [ @@ -73,6 +73,7 @@ class AstraDBToolComponent(LCToolComponent): name="static_filters", info="Attributes to filter and correspoding value", display_name="Static filters", + advanced=True, is_list=True, ), IntInput( diff --git a/src/backend/base/langflow/components/tools/astradb_cql.py b/src/backend/base/langflow/components/tools/astradb_cql.py index 5a7206e99278..79c3904c84bc 100644 --- a/src/backend/base/langflow/components/tools/astradb_cql.py +++ b/src/backend/base/langflow/components/tools/astradb_cql.py @@ -13,8 +13,8 @@ class AstraDBCQLToolComponent(LCToolComponent): display_name: str = "Astra DB CQL" - description: str = "Create a tool to get data from DataStax Astra DB CQL Table" - documentation: str = "https://astra.datastax.com" + description: str = "Create a tool to get transactional data from DataStax Astra DB CQL Table" + documentation: str = "https://docs.langflow.org/Components/components-tools#astra-db-cql-tool" icon: str = "AstraDB" inputs = [ @@ -31,6 +31,7 @@ class AstraDBCQLToolComponent(LCToolComponent): value="default_keyspace", info="The keyspace name within Astra DB where the data is stored.", required=True, + advanced=True, ), StrInput( name="table_name", @@ -58,6 +59,7 @@ class AstraDBCQLToolComponent(LCToolComponent): info="Attributes to return separated by comma.", required=True, value="*", + advanced=True, ), DictInput( name="partition_keys", @@ -76,6 +78,7 @@ class AstraDBCQLToolComponent(LCToolComponent): name="static_filters", display_name="Static Filters", is_list=True, + advanced=True, info="Field name and value. When filled, it will not be generated by the LLM.", ), IntInput( From efca9f9aeb04d638b9d3112e8729258f7d779fdf Mon Sep 17 00:00:00 2001 From: anovazzi1 Date: Fri, 22 Nov 2024 00:14:45 -0300 Subject: [PATCH 42/45] refactor: Improve componentHoverAdd.spec test (#4608) --- .../core/features/componentHoverAdd.spec.ts | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/src/frontend/tests/core/features/componentHoverAdd.spec.ts b/src/frontend/tests/core/features/componentHoverAdd.spec.ts index fe90def1e002..0114e79b6753 100644 --- a/src/frontend/tests/core/features/componentHoverAdd.spec.ts +++ b/src/frontend/tests/core/features/componentHoverAdd.spec.ts @@ -21,19 +21,25 @@ test("user can add components by hovering and clicking the plus icon", async ({ while (modalCount === 0) { await page.getByText("New Flow", { exact: true }).click(); - await page.waitForTimeout(3000); + await page.waitForSelector('[data-testid="modal-title"]', { + timeout: 3000, + }); modalCount = await page.getByTestId("modal-title")?.count(); } // Start with blank flow await page.getByTestId("blank-flow").click(); - await page.waitForTimeout(1000); + await page.waitForSelector('[data-testid="sidebar-search-input"]', { + timeout: 3000, + }); // Search for a component await page.getByTestId("sidebar-search-input").click(); await page.getByTestId("sidebar-search-input").fill("chat input"); - await page.waitForTimeout(500); + await page.waitForSelector('[data-testid="inputsChat Input"]', { + timeout: 2000, + }); // Hover over the component and verify plus icon const componentLocator = page.getByTestId("inputsChat Input"); // Find the plus icon within the specific component container @@ -48,26 +54,24 @@ test("user can add components by hovering and clicking the plus icon", async ({ await expect(opacity).toBe("0"); - // Hover over the component await componentLocator.hover(); - - // Check if the plus icon is visible and has full opacity - + // Hover over the component await expect(plusIcon).toBeVisible(); - + // Wait for the animation to change the opacity await page.waitForTimeout(500); const opacityAfterHover = await plusIcon.evaluate((el) => window.getComputedStyle(el).getPropertyValue("opacity"), ); - await expect(Number(opacityAfterHover)).toBeGreaterThan(0); + expect(Number(opacityAfterHover)).toBeGreaterThan(0); // Click the plus icon associated with this component await plusIcon.click(); - await page.waitForTimeout(500); + // Wait for the component to be added to the flow + await page.waitForSelector(".react-flow__node", { timeout: 1000 }); // Verify component was added to the flow - const addedComponent = await page.locator(".react-flow__node").first(); + const addedComponent = page.locator(".react-flow__node").first(); await expect(addedComponent).toBeVisible(); }); From 54d6121b6e431d8c551764ccd9a8067e151eff01 Mon Sep 17 00:00:00 2001 From: cristhianzl Date: Tue, 19 Nov 2024 13:50:08 -0300 Subject: [PATCH 43/45] =?UTF-8?q?=E2=9C=A8=20(playground-button.tsx):=20Ad?= =?UTF-8?q?d=20a=20new=20PlaygroundButton=20component=20to=20the=20flowToo?= =?UTF-8?q?lbarComponent=20to=20handle=20the=20display=20of=20the=20Playgr?= =?UTF-8?q?ound=20button=20based=20on=20the=20presence=20of=20Chat=20Input?= =?UTF-8?q?=20or=20Chat=20Output=20components.=20=F0=9F=93=9D=20(index.tsx?= =?UTF-8?q?):=20Import=20and=20use=20the=20PlaygroundButton=20component=20?= =?UTF-8?q?in=20the=20FlowToolbar=20component=20to=20replace=20the=20previ?= =?UTF-8?q?ous=20implementation=20of=20the=20Playground=20button=20display?= =?UTF-8?q?.=20=F0=9F=94=A7=20(applies.css):=20Add=20styling=20for=20the?= =?UTF-8?q?=20playground-btn-flow-toolbar=20class=20to=20ensure=20consiste?= =?UTF-8?q?nt=20styling=20for=20the=20Playground=20button=20in=20the=20too?= =?UTF-8?q?lbar.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../components/playground-button.tsx | 50 +++++++++++++++++++ .../components/flowToolbarComponent/index.tsx | 39 +++------------ src/frontend/src/style/applies.css | 4 ++ 3 files changed, 61 insertions(+), 32 deletions(-) create mode 100644 src/frontend/src/components/flowToolbarComponent/components/playground-button.tsx diff --git a/src/frontend/src/components/flowToolbarComponent/components/playground-button.tsx b/src/frontend/src/components/flowToolbarComponent/components/playground-button.tsx new file mode 100644 index 000000000000..675ff2c48aaa --- /dev/null +++ b/src/frontend/src/components/flowToolbarComponent/components/playground-button.tsx @@ -0,0 +1,50 @@ +import ForwardedIconComponent from "@/components/genericIconComponent"; +import ShadTooltip from "@/components/shadTooltipComponent"; +import IOModal from "@/modals/IOModal"; + +const PlaygroundButton = ({ hasIO, open, setOpen, canvasOpen }) => { + const PlayIcon = () => ( + + ); + + const ButtonLabel = () => Playground; + + const ActiveButton = () => ( +
+ + +
+ ); + + const DisabledButton = () => ( +
+ + +
+ ); + + return hasIO ? ( + + + + ) : ( + +
+ +
+
+ ); +}; + +export default PlaygroundButton; diff --git a/src/frontend/src/components/flowToolbarComponent/index.tsx b/src/frontend/src/components/flowToolbarComponent/index.tsx index 0784f5e98d7e..fedc7f259981 100644 --- a/src/frontend/src/components/flowToolbarComponent/index.tsx +++ b/src/frontend/src/components/flowToolbarComponent/index.tsx @@ -15,6 +15,7 @@ import { useShortcutsStore } from "../../stores/shortcuts"; import { useStoreStore } from "../../stores/storeStore"; import { classNames, isThereModal } from "../../utils/utils"; import ForwardedIconComponent from "../genericIconComponent"; +import PlaygroundButton from "./components/playground-button"; export default function FlowToolbar(): JSX.Element { const preventDefault = true; @@ -125,38 +126,12 @@ export default function FlowToolbar(): JSX.Element { >
- {hasIO ? ( - -
- - Playground -
-
- ) : ( - -
- - Playground -
-
- )} +
{ENABLE_API && ( <> diff --git a/src/frontend/src/style/applies.css b/src/frontend/src/style/applies.css index e4089c8426ad..42cadc8f67d3 100644 --- a/src/frontend/src/style/applies.css +++ b/src/frontend/src/style/applies.css @@ -1268,6 +1268,10 @@ .toolbar-wrapper { @apply flex h-10 items-center gap-1 rounded-xl border border-border bg-background p-1 shadow-sm; } + + .playground-btn-flow-toolbar { + @apply relative inline-flex h-8 w-full items-center justify-center gap-1.5 rounded px-3 py-1.5 text-sm font-semibold transition-all duration-500 ease-in-out; + } } /* Gradient background */ From 13321a363509d97af45557c906d7d5d53f909e8e Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Fri, 22 Nov 2024 00:31:27 -0300 Subject: [PATCH 44/45] feat: rename MetaphorToolkit file to ExaSearchToolkit and update component icons (#4754) --- src/backend/base/langflow/components/tools/__init__.py | 4 ++-- .../langflow/components/tools/{metaphor.py => exa_search.py} | 3 ++- .../base/langflow/components/tools/wolfram_alpha_api.py | 2 ++ src/backend/base/langflow/components/vectorstores/pgvector.py | 2 +- src/backend/base/langflow/components/vectorstores/redis.py | 1 + src/frontend/src/utils/styleUtils.ts | 1 - 6 files changed, 8 insertions(+), 5 deletions(-) rename src/backend/base/langflow/components/tools/{metaphor.py => exa_search.py} (97%) diff --git a/src/backend/base/langflow/components/tools/__init__.py b/src/backend/base/langflow/components/tools/__init__.py index 2ffabdb20b2d..c8265e929b42 100644 --- a/src/backend/base/langflow/components/tools/__init__.py +++ b/src/backend/base/langflow/components/tools/__init__.py @@ -5,10 +5,10 @@ from .bing_search_api import BingSearchAPIComponent from .calculator import CalculatorToolComponent from .duck_duck_go_search_run import DuckDuckGoSearchComponent +from .exa_search import ExaSearchToolkit from .glean_search_api import GleanSearchAPIComponent from .google_search_api import GoogleSearchAPIComponent from .google_serper_api import GoogleSerperAPIComponent -from .metaphor import MetaphorToolkit from .python_code_structured_tool import PythonCodeStructuredTool from .python_repl import PythonREPLToolComponent from .retriever import RetrieverToolComponent @@ -36,7 +36,7 @@ "GleanSearchAPIComponent", "GoogleSearchAPIComponent", "GoogleSerperAPIComponent", - "MetaphorToolkit", + "ExaSearchToolkit", "PythonCodeStructuredTool", "PythonREPLToolComponent", "RetrieverToolComponent", diff --git a/src/backend/base/langflow/components/tools/metaphor.py b/src/backend/base/langflow/components/tools/exa_search.py similarity index 97% rename from src/backend/base/langflow/components/tools/metaphor.py rename to src/backend/base/langflow/components/tools/exa_search.py index ad4c97159429..b0600fc260e3 100644 --- a/src/backend/base/langflow/components/tools/metaphor.py +++ b/src/backend/base/langflow/components/tools/exa_search.py @@ -6,12 +6,13 @@ from langflow.io import BoolInput, IntInput, Output, SecretStrInput -class MetaphorToolkit(Component): +class ExaSearchToolkit(Component): display_name = "Exa Search" description = "Exa Search toolkit for search and content retrieval" documentation = "https://python.langchain.com/docs/integrations/tools/metaphor_search" beta = True name = "ExaSearch" + icon = "ExaSearch" inputs = [ SecretStrInput( diff --git a/src/backend/base/langflow/components/tools/wolfram_alpha_api.py b/src/backend/base/langflow/components/tools/wolfram_alpha_api.py index 27c398ec6066..410677c65eb8 100644 --- a/src/backend/base/langflow/components/tools/wolfram_alpha_api.py +++ b/src/backend/base/langflow/components/tools/wolfram_alpha_api.py @@ -19,6 +19,8 @@ class WolframAlphaAPIComponent(LCToolComponent): SecretStrInput(name="app_id", display_name="App ID", required=True), ] + icon = "WolframAlphaAPI" + def run_model(self) -> list[Data]: wrapper = self._build_wrapper() result_str = wrapper.run(self.input_value) diff --git a/src/backend/base/langflow/components/vectorstores/pgvector.py b/src/backend/base/langflow/components/vectorstores/pgvector.py index cb3c8838cc52..4ff171388a75 100644 --- a/src/backend/base/langflow/components/vectorstores/pgvector.py +++ b/src/backend/base/langflow/components/vectorstores/pgvector.py @@ -12,7 +12,7 @@ class PGVectorStoreComponent(LCVectorStoreComponent): description = "PGVector Vector Store with search capabilities" documentation = "https://python.langchain.com/v0.2/docs/integrations/vectorstores/pgvector/" name = "pgvector" - icon = "PGVector" + icon = "cpu" inputs = [ SecretStrInput(name="pg_server_url", display_name="PostgreSQL Server Connection String", required=True), diff --git a/src/backend/base/langflow/components/vectorstores/redis.py b/src/backend/base/langflow/components/vectorstores/redis.py index 9e25f0bb227a..d4d5ce07a622 100644 --- a/src/backend/base/langflow/components/vectorstores/redis.py +++ b/src/backend/base/langflow/components/vectorstores/redis.py @@ -16,6 +16,7 @@ class RedisVectorStoreComponent(LCVectorStoreComponent): description: str = "Implementation of Vector Store using Redis" documentation = "https://python.langchain.com/docs/integrations/vectorstores/redis" name = "Redis" + icon = "Redis" inputs = [ SecretStrInput(name="redis_server_url", display_name="Redis Server Connection String", required=True), diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index 222f05dd49a0..47b9660e98ff 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -663,7 +663,6 @@ export const nodeIconsLucide: iconsType = { Discord: FaDiscord, MistralAI: MistralIcon, Upstash: UpstashSvgIcon, - PGVector: CpuIcon, Confluence: ConfluenceIcon, AIML: AIMLIcon, "AI/ML": AIMLIcon, From d31fa355868511a1a4622f7ebc950347cb78d28e Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa Date: Fri, 22 Nov 2024 09:34:00 -0300 Subject: [PATCH 45/45] feat: add Google icons to search-related components (#4760) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ✨ (google_search_api.py): Add icon property to GoogleSearchAPIComponent for better visualization in the UI ✨ (google_serper_api.py): Add icon property to GoogleSerperAPIComponent for better visualization in the UI 📝 (styleUtils.ts): Add GoogleSearchAPI and GoogleSerperAPI icons to nodeIconsLucide for consistent styling in the frontend. --- src/backend/base/langflow/components/tools/google_search_api.py | 2 +- src/backend/base/langflow/components/tools/google_serper_api.py | 2 +- src/frontend/src/utils/styleUtils.ts | 2 ++ 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/backend/base/langflow/components/tools/google_search_api.py b/src/backend/base/langflow/components/tools/google_search_api.py index 7df0fef5fe6d..a57ebb346c1a 100644 --- a/src/backend/base/langflow/components/tools/google_search_api.py +++ b/src/backend/base/langflow/components/tools/google_search_api.py @@ -9,7 +9,7 @@ class GoogleSearchAPIComponent(LCToolComponent): display_name = "Google Search API" description = "Call Google Search API." name = "GoogleSearchAPI" - + icon = "Google" inputs = [ SecretStrInput(name="google_api_key", display_name="Google API Key", required=True), SecretStrInput(name="google_cse_id", display_name="Google CSE ID", required=True), diff --git a/src/backend/base/langflow/components/tools/google_serper_api.py b/src/backend/base/langflow/components/tools/google_serper_api.py index a7f53ce07a2d..038c9d2e1bb3 100644 --- a/src/backend/base/langflow/components/tools/google_serper_api.py +++ b/src/backend/base/langflow/components/tools/google_serper_api.py @@ -10,7 +10,7 @@ class GoogleSerperAPIComponent(LCToolComponent): display_name = "Google Serper API" description = "Call the Serper.dev Google Search API." name = "GoogleSerperAPI" - + icon = "Google" inputs = [ SecretStrInput(name="serper_api_key", display_name="Serper API Key", required=True), MultilineInput( diff --git a/src/frontend/src/utils/styleUtils.ts b/src/frontend/src/utils/styleUtils.ts index 47b9660e98ff..0c3b940a81d2 100644 --- a/src/frontend/src/utils/styleUtils.ts +++ b/src/frontend/src/utils/styleUtils.ts @@ -613,6 +613,8 @@ export const nodeIconsLucide: iconsType = { GoogleSearchAPIWrapper: GoogleIcon, GoogleSearchResults: GoogleIcon, GoogleSearchRun: GoogleIcon, + GoogleSearchAPI: GoogleIcon, + GoogleSerperAPI: GoogleIcon, Google: GoogleIcon, GoogleGenerativeAI: GoogleGenerativeAIIcon, Groq: GroqIcon,