diff --git a/src/backend/base/langflow/api/v2/files.py b/src/backend/base/langflow/api/v2/files.py index 826ffc83c..391e0e2ce 100644 --- a/src/backend/base/langflow/api/v2/files.py +++ b/src/backend/base/langflow/api/v2/files.py @@ -23,6 +23,7 @@ router = APIRouter(tags=["Files"], prefix="/files") # Set the static name of the MCP servers file MCP_SERVERS_FILE = "_mcp_servers" +SAMPLE_DATA_DIR = Path(__file__).parent / "sample_data" async def byte_stream_generator(file_input, chunk_size: int = 8192) -> AsyncGenerator[bytes, None]: @@ -61,6 +62,21 @@ async def fetch_file_object(file_id: uuid.UUID, current_user: CurrentActiveUser, return file +async def save_file_routine(file, storage_service, current_user: CurrentActiveUser, file_content=None, file_name=None): + """Routine to save the file content to the storage service.""" + file_id = uuid.uuid4() + + if not file_content: + file_content = await file.read() + if not file_name: + file_name = file.filename + + # Save the file using the storage service. + await storage_service.save_file(flow_id=str(current_user.id), file_name=file_name, data=file_content) + + return file_id, file_name + + @router.post("", status_code=HTTPStatus.CREATED) @router.post("/", status_code=HTTPStatus.CREATED) async def upload_user_file( @@ -90,18 +106,7 @@ async def upload_user_file( # Read file content and create a unique file name try: - # Create a unique file name - file_id = uuid.uuid4() - file_content = await file.read() - - # Get file extension of the file - file_extension = "." + file.filename.split(".")[-1] if file.filename and "." in file.filename else "" - anonymized_file_name = f"{file_id!s}{file_extension}" - - # Here we use the current user's id as the folder name - folder = str(current_user.id) - # Save the file using the storage service. - await storage_service.save_file(flow_id=folder, file_name=anonymized_file_name, data=file_content) + file_id, file_name = await save_file_routine(file, storage_service, current_user) except Exception as e: raise HTTPException(status_code=500, detail=f"Error saving file: {e}") from e @@ -138,17 +143,17 @@ async def upload_user_file( root_filename = f"{root_filename} ({count + 1})" # Compute the file size based on the path - file_size = await storage_service.get_file_size(flow_id=folder, file_name=anonymized_file_name) - - # Compute the file path - file_path = f"{folder}/{anonymized_file_name}" + file_size = await storage_service.get_file_size( + flow_id=str(current_user.id), + file_name=file_name, + ) # Create a new file record new_file = UserFile( id=file_id, user_id=current_user.id, name=root_filename, - path=file_path, + path=f"{current_user.id}/{file_name}", size=file_size, ) session.add(new_file) @@ -178,14 +183,61 @@ async def get_file_by_name( raise HTTPException(status_code=500, detail=f"Error fetching file: {e}") from e +async def load_sample_files(current_user: CurrentActiveUser, session: DbSession, storage_service: StorageService): + # Check if the sample files in the SAMPLE_DATA_DIR exist + for sample_file_path in Path(SAMPLE_DATA_DIR).iterdir(): + sample_file_name = sample_file_path.name + root_filename, _ = sample_file_name.rsplit(".", 1) + + # Check if the sample file exists in the storage service + existing_sample_file = await get_file_by_name( + file_name=root_filename, current_user=current_user, session=session + ) + if existing_sample_file: + continue + + # Read the binary data of the sample file + binary_data = sample_file_path.read_bytes() + + # Write the sample file content to the storage service + file_id, _ = await save_file_routine( + sample_file_path, + storage_service, + current_user, + file_content=binary_data, + file_name=sample_file_name, + ) + file_size = await storage_service.get_file_size( + flow_id=str(current_user.id), + file_name=sample_file_name, + ) + # Create a UserFile object for the sample file + sample_file = UserFile( + id=file_id, + user_id=current_user.id, + name=root_filename, + path=sample_file_name, + size=file_size, + ) + + session.add(sample_file) + + await session.commit() + await session.refresh(sample_file) + + @router.get("") @router.get("/", status_code=HTTPStatus.OK) async def list_files( current_user: CurrentActiveUser, session: DbSession, + # storage_service: Annotated[StorageService, Depends(get_storage_service)], ) -> list[UserFile]: """List the files available to the current user.""" try: + # Load sample files if they don't exist + # TODO: Pending further testing + # await load_sample_files(current_user, session, get_storage_service()) # Fetch from the UserFile table stmt = select(UserFile).where(UserFile.user_id == current_user.id) results = await session.exec(stmt) diff --git a/src/backend/base/langflow/base/constants.py b/src/backend/base/langflow/base/constants.py index 6e1c40688..f096fba7d 100644 --- a/src/backend/base/langflow/base/constants.py +++ b/src/backend/base/langflow/base/constants.py @@ -43,4 +43,4 @@ FIELD_FORMAT_ATTRIBUTES = [ ] SKIPPED_FIELD_ATTRIBUTES = ["advanced"] ORJSON_OPTIONS = orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS | orjson.OPT_OMIT_MICROSECONDS -SKIPPED_COMPONENTS = {"LanguageModelComponent", "Agent"} +SKIPPED_COMPONENTS = {"LanguageModelComponent", "Agent", "File", "FileComponent"} diff --git a/src/backend/base/langflow/base/data/base_file.py b/src/backend/base/langflow/base/data/base_file.py index c09fc73df..640ad41f5 100644 --- a/src/backend/base/langflow/base/data/base_file.py +++ b/src/backend/base/langflow/base/data/base_file.py @@ -1,8 +1,11 @@ +import ast +import json import shutil import tarfile from abc import ABC, abstractmethod from pathlib import Path from tempfile import TemporaryDirectory +from typing import TYPE_CHECKING from zipfile import ZipFile, is_zipfile import pandas as pd @@ -13,6 +16,9 @@ from langflow.schema.data import Data from langflow.schema.dataframe import DataFrame from langflow.schema.message import Message +if TYPE_CHECKING: + from collections.abc import Callable + class BaseFileComponent(Component, ABC): """Base class for handling file processing components. @@ -174,8 +180,7 @@ class BaseFileComponent(Component, ABC): ] _base_outputs = [ - Output(display_name="Loaded Files", name="dataframe", method="load_files"), - Output(display_name="Raw Content", name="message", method="load_files_message"), + Output(display_name="Files", name="dataframe", method="load_files"), ] @abstractmethod @@ -257,6 +262,104 @@ class BaseFileComponent(Component, ABC): return Message(text=sep.join(parts)) + def load_files_path(self) -> Message: + """Returns a Message containing file paths from loaded files. + + Returns: + Message: Message containing file paths + """ + files = self._validate_and_resolve_paths() + paths = [file.path.as_posix() for file in files if file.path.exists()] + + return Message(text="\n".join(paths) if paths else "") + + def load_files_structured_helper(self, file_path: str) -> list[dict] | None: + if not file_path: + return None + + # Map file extensions to pandas read functions with type annotation + file_readers: dict[str, Callable[[str], pd.DataFrame]] = { + ".csv": pd.read_csv, + ".xlsx": pd.read_excel, + ".parquet": pd.read_parquet, + # TODO: sqlite and json support? + } + + # Get file extension in lowercase + ext = Path(file_path).suffix.lower() + + # Get the appropriate reader function or None + reader = file_readers.get(ext) + + if reader: + result = reader(file_path) # MyPy now knows reader is callable + return result.to_dict("records") + + return None + + def load_files_structured(self) -> DataFrame: + """Load files and return as DataFrame with structured content. + + Returns: + DataFrame: DataFrame containing structured content from all files + """ + data_list = self.load_files_core() + if not data_list: + return DataFrame() + + # Get the file path from the first Data object + file_path = data_list[0].data.get(self.SERVER_FILE_PATH_FIELDNAME, None) + + # If file_path is provided and is a CSV, read it directly + if file_path and str(file_path).lower().endswith((".csv", ".xlsx", ".parquet")): + rows = self.load_files_structured_helper(file_path) + else: + # Convert Data objects to a list of dictionaries + # TODO: Parse according to docling standards + rows = [data_list[0].data] + + self.status = DataFrame(rows) + + return DataFrame(rows) + + def parse_string_to_dict(self, s: str) -> dict: + # Try JSON first (handles true/false/null) + try: + result = json.loads(s) + if isinstance(result, dict): + return result + except json.JSONDecodeError: + pass + + # Fall back to Python literal evaluation + try: + result = ast.literal_eval(s) + if isinstance(result, dict): + return result + except (SyntaxError, ValueError): + pass + + # If all parsing fails, return the fallback + return {"value": s} + + def load_files_json(self) -> Data: + """Load files and return as a single Data object containing JSON content. + + Returns: + Data: Data object containing JSON content from all files + """ + data_list = self.load_files_core() + if not data_list: + return Data() + + # Grab the JSON data + json_data = data_list[0].data[data_list[0].text_key] + json_data = self.parse_string_to_dict(json_data) + + self.status = Data(data=json_data) + + return Data(data=json_data) + def load_files(self) -> DataFrame: """Load files and return as DataFrame. @@ -267,30 +370,21 @@ class BaseFileComponent(Component, ABC): if not data_list: return DataFrame() - # First handle CSV files specially - csv_data = [] - non_csv_rows = [] - + # Convert Data objects to a list of dictionaries + all_rows = [] for data in data_list: file_path = data.data.get(self.SERVER_FILE_PATH_FIELDNAME) - if file_path and str(file_path).lower().endswith(".csv"): - try: - csv_data.extend(pd.read_csv(file_path).to_dict("records")) - except Exception as e: - self.log(f"Error processing CSV file {file_path}: {e}") - if not self.silent_errors: - raise - else: - # Handle non-CSV files as before - row = dict(data.data) if data.data else {} - if "text" in data.data: - row["text"] = data.data["text"] - if file_path: - row["file_path"] = file_path - non_csv_rows.append(row) + row = dict(data.data) if data.data else {} + + # Add text if available, otherwise use the data's text property + if "text" in data.data: + row["text"] = data.data["text"] + if file_path: + row["file_path"] = file_path + all_rows.append(row) + + self.status = DataFrame(all_rows) - # Combine CSV and non-CSV data - all_rows = csv_data + non_csv_rows return DataFrame(all_rows) @property diff --git a/src/backend/base/langflow/components/data/file.py b/src/backend/base/langflow/components/data/file.py index 95ba804dc..cc0df7dd7 100644 --- a/src/backend/base/langflow/components/data/file.py +++ b/src/backend/base/langflow/components/data/file.py @@ -1,6 +1,9 @@ +from copy import deepcopy +from typing import Any + from langflow.base.data.base_file import BaseFileComponent from langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data -from langflow.io import BoolInput, IntInput +from langflow.io import BoolInput, FileInput, IntInput, Output from langflow.schema.data import Data @@ -18,8 +21,15 @@ class FileComponent(BaseFileComponent): VALID_EXTENSIONS = TEXT_FILE_TYPES + _base_inputs = deepcopy(BaseFileComponent._base_inputs) + + for input_item in _base_inputs: + if isinstance(input_item, FileInput) and input_item.name == "path": + input_item.real_time_refresh = True + break + inputs = [ - *BaseFileComponent._base_inputs, + *_base_inputs, BoolInput( name="use_multithreading", display_name="[Deprecated] Use Multithreading", @@ -37,9 +47,45 @@ class FileComponent(BaseFileComponent): ] outputs = [ - *BaseFileComponent._base_outputs, + Output(display_name="Raw Content", name="message", method="load_files_message"), ] + def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict: + """Dynamically show only the relevant output based on the number of files processed.""" + if field_name == "path": + # Add outputs based on the number of files in the path + if len(field_value) == 0: + return frontend_node + + frontend_node["outputs"] = [] + + if len(field_value) == 1: + # We need to check if the file is structured content + file_path = frontend_node["template"]["path"]["file_path"][0] + if file_path.endswith((".csv", ".xlsx", ".parquet")): + frontend_node["outputs"].append( + Output(display_name="Structured Content", name="dataframe", method="load_files_structured"), + ) + elif file_path.endswith(".json"): + frontend_node["outputs"].append( + Output(display_name="Structured Content", name="json", method="load_files_json"), + ) + + # All files get the raw content and path outputs + frontend_node["outputs"].append( + Output(display_name="Raw Content", name="message", method="load_files_message"), + ) + frontend_node["outputs"].append( + Output(display_name="File Path", name="path", method="load_files_path"), + ) + else: + # For multiple files, we only show the files output + frontend_node["outputs"].append( + Output(display_name="Files", name="dataframe", method="load_files"), + ) + + return frontend_node + def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]: """Processes files either sequentially or in parallel, depending on concurrency settings. diff --git a/src/backend/base/langflow/components/processing/split_text.py b/src/backend/base/langflow/components/processing/split_text.py index 610670bc0..5c2acacee 100644 --- a/src/backend/base/langflow/components/processing/split_text.py +++ b/src/backend/base/langflow/components/processing/split_text.py @@ -4,6 +4,7 @@ from langflow.custom.custom_component.component import Component from langflow.io import DropdownInput, HandleInput, IntInput, MessageTextInput, Output from langflow.schema.data import Data from langflow.schema.dataframe import DataFrame +from langflow.schema.message import Message from langflow.utils.util import unescape_string @@ -16,9 +17,9 @@ class SplitTextComponent(Component): inputs = [ HandleInput( name="data_inputs", - display_name="Data or DataFrame", + display_name="Input", info="The data with texts to split in chunks.", - input_types=["Data", "DataFrame"], + input_types=["Data", "DataFrame", "Message"], required=True, ), IntInput( @@ -93,6 +94,9 @@ class SplitTextComponent(Component): except Exception as e: msg = f"Error converting DataFrame to documents: {e}" raise TypeError(msg) from e + elif isinstance(self.data_inputs, Message): + self.data_inputs = [self.data_inputs.to_data()] + return self.split_text_base() else: if not self.data_inputs: msg = "No data inputs provided" diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json b/src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json index 9944b070c..3a1ea10db 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Document Q&A.json @@ -1,42 +1,13 @@ { "data": { "edges": [ - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "parser", - "id": "parser-1O59j", - "name": "parsed_text", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "Document", - "id": "Prompt-GjFNQ", - "inputTypes": [ - "Message", - "Text" - ], - "type": "str" - } - }, - "id": "reactflow__edge-parser-1O59j{œdataTypeœ:œparserœ,œidœ:œparser-1O59jœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-GjFNQ{œfieldNameœ:œDocumentœ,œidœ:œPrompt-GjFNQœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "selected": false, - "source": "parser-1O59j", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-1O59jœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-GjFNQ", - "targetHandle": "{œfieldNameœ: œDocumentœ, œidœ: œPrompt-GjFNQœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" - }, { "animated": false, "className": "", "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-l9kmd", + "id": "ChatInput-e74mn", "name": "message", "output_types": [ "Message" @@ -44,19 +15,19 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "LanguageModelComponent-IEakg", + "id": "LanguageModelComponent-htMuI", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "xy-edge__ChatInput-l9kmd{œdataTypeœ:œChatInputœ,œidœ:œChatInput-l9kmdœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-IEakg{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-IEakgœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-e74mn{œdataTypeœ:œChatInputœ,œidœ:œChatInput-e74mnœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-htMuI{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-htMuIœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-l9kmd", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-l9kmdœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "LanguageModelComponent-IEakg", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-IEakgœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "ChatInput-e74mn", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-e74mnœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-htMuI", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-htMuIœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -64,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-GjFNQ", + "id": "Prompt-odlqe", "name": "prompt", "output_types": [ "Message" @@ -72,19 +43,19 @@ }, "targetHandle": { "fieldName": "system_message", - "id": "LanguageModelComponent-IEakg", + "id": "LanguageModelComponent-htMuI", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "xy-edge__Prompt-GjFNQ{œdataTypeœ:œPromptœ,œidœ:œPrompt-GjFNQœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-IEakg{œfieldNameœ:œsystem_messageœ,œidœ:œLanguageModelComponent-IEakgœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Prompt-odlqe{œdataTypeœ:œPromptœ,œidœ:œPrompt-odlqeœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-htMuI{œfieldNameœ:œsystem_messageœ,œidœ:œLanguageModelComponent-htMuIœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Prompt-GjFNQ", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-GjFNQœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "LanguageModelComponent-IEakg", - "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œLanguageModelComponent-IEakgœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Prompt-odlqe", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-odlqeœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-htMuI", + "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œLanguageModelComponent-htMuIœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -92,7 +63,7 @@ "data": { "sourceHandle": { "dataType": "LanguageModelComponent", - "id": "LanguageModelComponent-IEakg", + "id": "LanguageModelComponent-htMuI", "name": "text_output", "output_types": [ "Message" @@ -100,7 +71,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-FO9AH", + "id": "ChatOutput-bcQIH", "inputTypes": [ "Data", "DataFrame", @@ -109,12 +80,12 @@ "type": "str" } }, - "id": "xy-edge__LanguageModelComponent-IEakg{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-IEakgœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-FO9AH{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-FO9AHœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-LanguageModelComponent-htMuI{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-htMuIœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-bcQIH{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-bcQIHœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "LanguageModelComponent-IEakg", - "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-IEakgœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-FO9AH", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-FO9AHœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "source": "LanguageModelComponent-htMuI", + "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-htMuIœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-bcQIH", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-bcQIHœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -122,28 +93,28 @@ "data": { "sourceHandle": { "dataType": "File", - "id": "File-cLJI5", - "name": "dataframe", + "id": "File-b2gOG", + "name": "message", "output_types": [ - "DataFrame" + "Message" ] }, "targetHandle": { - "fieldName": "input_data", - "id": "parser-1O59j", + "fieldName": "Document", + "id": "Prompt-odlqe", "inputTypes": [ - "DataFrame", - "Data" + "Message", + "Text" ], - "type": "other" + "type": "str" } }, - "id": "xy-edge__File-cLJI5{œdataTypeœ:œFileœ,œidœ:œFile-cLJI5œ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-parser-1O59j{œfieldNameœ:œinput_dataœ,œidœ:œparser-1O59jœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-File-b2gOG{œdataTypeœ:œFileœ,œidœ:œFile-b2gOGœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-odlqe{œfieldNameœ:œDocumentœ,œidœ:œPrompt-odlqeœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", "selected": false, - "source": "File-cLJI5", - "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-cLJI5œ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", - "target": "parser-1O59j", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-1O59jœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + "source": "File-b2gOG", + "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-b2gOGœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-odlqe", + "targetHandle": "{œfieldNameœ: œDocumentœ, œidœ: œPrompt-odlqeœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" } ], "nodes": [ @@ -151,7 +122,7 @@ "data": { "description": "Get chat inputs from the Playground.", "display_name": "Chat Input", - "id": "ChatInput-l9kmd", + "id": "ChatInput-e74mn", "node": { "base_classes": [ "Message" @@ -419,7 +390,7 @@ }, "dragging": false, "height": 234, - "id": "ChatInput-l9kmd", + "id": "ChatInput-e74mn", "measured": { "height": 234, "width": 320 @@ -440,7 +411,7 @@ "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-FO9AH", + "id": "ChatOutput-bcQIH", "node": { "base_classes": [ "Message" @@ -723,7 +694,7 @@ }, "dragging": false, "height": 234, - "id": "ChatOutput-FO9AH", + "id": "ChatOutput-bcQIH", "measured": { "height": 234, "width": 320 @@ -742,7 +713,7 @@ }, { "data": { - "id": "note-CTOUn", + "id": "note-gLzpv", "node": { "description": "# Document Q&A\n\nThis flow loads a file and uses an LLM to answer questions based on content from the loaded document. \n\n## Prerequisites\n\n* An [OpenAI API key](https://platform.openai.com/)\n\n## Quickstart\n\n1. Paste your OpenAI API key in the **OpenAI** model component.\n2. In the **File** component, select a file you want to load.\n3. Open the **Playground** and chat with your document.", "display_name": "", @@ -753,14 +724,14 @@ }, "dragging": false, "height": 509, - "id": "note-CTOUn", + "id": "note-gLzpv", "measured": { "height": 509, "width": 420 }, "position": { - "x": -306.48794920240215, - "y": -177.11912020709357 + "x": 59.87035937663103, + "y": -81.89823824161797 }, "positionAbsolute": { "x": -338.7070086205371, @@ -779,7 +750,7 @@ "data": { "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", - "id": "Prompt-GjFNQ", + "id": "Prompt-odlqe", "node": { "base_classes": [ "Message" @@ -916,7 +887,7 @@ }, "dragging": false, "height": 347, - "id": "Prompt-GjFNQ", + "id": "Prompt-odlqe", "measured": { "height": 347, "width": 320 @@ -935,183 +906,7 @@ }, { "data": { - "id": "parser-1O59j", - "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "category": "processing", - "conditional_paths": [], - "custom_fields": {}, - "description": "Format a DataFrame or Data object into text using a template. Enable 'Stringify' to convert input into a readable string instead.", - "display_name": "Parser", - "documentation": "", - "edited": false, - "field_order": [ - "mode", - "pattern", - "input_data", - "sep" - ], - "frozen": false, - "icon": "braces", - "key": "parser", - "legacy": false, - "lf_version": "1.4.3", - "metadata": {}, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Parsed Text", - "method": "parse_combined_text", - "name": "parsed_text", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "score": 2.220446049250313e-16, - "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": "import json\nfrom typing import Any\n\nfrom langflow.custom import Component\nfrom langflow.io import (\n BoolInput,\n HandleInput,\n MessageTextInput,\n MultilineInput,\n Output,\n TabInput,\n)\nfrom langflow.schema import Data, DataFrame\nfrom langflow.schema.message import Message\n\n\nclass ParserComponent(Component):\n name = \"parser\"\n display_name = \"Parser\"\n description = (\n \"Format a DataFrame or Data object into text using a template. \"\n \"Enable 'Stringify' to convert input into a readable string instead.\"\n )\n icon = \"braces\"\n\n inputs = [\n TabInput(\n name=\"mode\",\n display_name=\"Mode\",\n options=[\"Parser\", \"Stringify\"],\n value=\"Parser\",\n info=\"Convert into raw string instead of using a template.\",\n real_time_refresh=True,\n ),\n MultilineInput(\n name=\"pattern\",\n display_name=\"Template\",\n info=(\n \"Use variables within curly brackets to extract column values for DataFrames \"\n \"or key values for Data.\"\n \"For example: `Name: {Name}, Age: {Age}, Country: {Country}`\"\n ),\n value=\"Text: {text}\", # Example default\n dynamic=True,\n show=True,\n required=True,\n ),\n HandleInput(\n name=\"input_data\",\n display_name=\"Data or DataFrame\",\n input_types=[\"DataFrame\", \"Data\"],\n info=\"Accepts either a DataFrame or a Data object.\",\n required=True,\n ),\n MessageTextInput(\n name=\"sep\",\n display_name=\"Separator\",\n advanced=True,\n value=\"\\n\",\n info=\"String used to separate rows/items.\",\n ),\n ]\n\n outputs = [\n Output(\n display_name=\"Parsed Text\",\n name=\"parsed_text\",\n info=\"Formatted text output.\",\n method=\"parse_combined_text\",\n ),\n ]\n\n def update_build_config(self, build_config, field_value, field_name=None):\n \"\"\"Dynamically hide/show `template` and enforce requirement based on `stringify`.\"\"\"\n if field_name == \"mode\":\n build_config[\"pattern\"][\"show\"] = self.mode == \"Parser\"\n build_config[\"pattern\"][\"required\"] = self.mode == \"Parser\"\n if field_value:\n clean_data = BoolInput(\n name=\"clean_data\",\n display_name=\"Clean Data\",\n info=(\n \"Enable to clean the data by removing empty rows and lines \"\n \"in each cell of the DataFrame/ Data object.\"\n ),\n value=True,\n advanced=True,\n required=False,\n )\n build_config[\"clean_data\"] = clean_data.to_dict()\n else:\n build_config.pop(\"clean_data\", None)\n\n return build_config\n\n def _clean_args(self):\n \"\"\"Prepare arguments based on input type.\"\"\"\n input_data = self.input_data\n\n match input_data:\n case list() if all(isinstance(item, Data) for item in input_data):\n msg = \"List of Data objects is not supported.\"\n raise ValueError(msg)\n case DataFrame():\n return input_data, None\n case Data():\n return None, input_data\n case dict() if \"data\" in input_data:\n try:\n if \"columns\" in input_data: # Likely a DataFrame\n return DataFrame.from_dict(input_data), None\n # Likely a Data object\n return None, Data(**input_data)\n except (TypeError, ValueError, KeyError) as e:\n msg = f\"Invalid structured input provided: {e!s}\"\n raise ValueError(msg) from e\n case _:\n msg = f\"Unsupported input type: {type(input_data)}. Expected DataFrame or Data.\"\n raise ValueError(msg)\n\n def parse_combined_text(self) -> Message:\n \"\"\"Parse all rows/items into a single text or convert input to string if `stringify` is enabled.\"\"\"\n # Early return for stringify option\n if self.mode == \"Stringify\":\n return self.convert_to_string()\n\n df, data = self._clean_args()\n\n lines = []\n if df is not None:\n for _, row in df.iterrows():\n formatted_text = self.pattern.format(**row.to_dict())\n lines.append(formatted_text)\n elif data is not None:\n formatted_text = self.pattern.format(**data.data)\n lines.append(formatted_text)\n\n combined_text = self.sep.join(lines)\n self.status = combined_text\n return Message(text=combined_text)\n\n def _safe_convert(self, data: Any) -> str:\n \"\"\"Safely convert input data to string.\"\"\"\n try:\n if isinstance(data, str):\n return data\n if isinstance(data, Message):\n return data.get_text()\n if isinstance(data, Data):\n return json.dumps(data.data)\n if isinstance(data, DataFrame):\n if hasattr(self, \"clean_data\") and self.clean_data:\n # Remove empty rows\n data = data.dropna(how=\"all\")\n # Remove empty lines in each cell\n data = data.replace(r\"^\\s*$\", \"\", regex=True)\n # Replace multiple newlines with a single newline\n data = data.replace(r\"\\n+\", \"\\n\", regex=True)\n return data.to_markdown(index=False)\n return str(data)\n except (ValueError, TypeError, AttributeError) as e:\n msg = f\"Error converting data: {e!s}\"\n raise ValueError(msg) from e\n\n def convert_to_string(self) -> Message:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n result = \"\"\n if isinstance(self.input_data, list):\n result = \"\\n\".join([self._safe_convert(item) for item in self.input_data])\n else:\n result = self._safe_convert(self.input_data)\n self.log(f\"Converted to string with length: {len(result)}\")\n\n message = Message(text=result)\n self.status = message\n return message\n" - }, - "input_data": { - "_input_type": "HandleInput", - "advanced": false, - "display_name": "Data or DataFrame", - "dynamic": false, - "info": "Accepts either a DataFrame or a Data object.", - "input_types": [ - "DataFrame", - "Data" - ], - "list": false, - "list_add_label": "Add More", - "name": "input_data", - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "other", - "value": "" - }, - "mode": { - "_input_type": "TabInput", - "advanced": false, - "display_name": "Mode", - "dynamic": false, - "info": "Convert into raw string instead of using a template.", - "name": "mode", - "options": [ - "Parser", - "Stringify" - ], - "placeholder": "", - "real_time_refresh": true, - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "tab", - "value": "Parser" - }, - "pattern": { - "_input_type": "MultilineInput", - "advanced": false, - "copy_field": false, - "display_name": "Template", - "dynamic": true, - "info": "Use variables within curly brackets to extract column values for DataFrames or key values for Data.For example: `Name: {Name}, Age: {Age}, Country: {Country}`", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "pattern", - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "Text: {text}" - }, - "sep": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Separator", - "dynamic": false, - "info": "String used to separate rows/items.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "sep", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "\n" - } - }, - "tool_mode": false - }, - "selected_output": "parsed_text", - "showNode": true, - "type": "parser" - }, - "dragging": false, - "id": "parser-1O59j", - "measured": { - "height": 360, - "width": 320 - }, - "position": { - "x": 517.4341822104063, - "y": -211.09448665229442 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "LanguageModelComponent-IEakg", + "id": "LanguageModelComponent-htMuI", "node": { "base_classes": [ "LanguageModel", @@ -1390,7 +1185,7 @@ "type": "LanguageModelComponent" }, "dragging": false, - "id": "LanguageModelComponent-IEakg", + "id": "LanguageModelComponent-htMuI", "measured": { "height": 532, "width": 320 @@ -1404,11 +1199,10 @@ }, { "data": { - "id": "File-cLJI5", + "id": "File-b2gOG", "node": { "base_classes": [ - "DataFrame", - "Message" + "DataFrame" ], "beta": false, "conditional_paths": [], @@ -1436,20 +1230,6 @@ "minimized": false, "output_types": [], "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Loaded Files", - "group_outputs": false, - "method": "load_files", - "name": "dataframe", - "selected": "DataFrame", - "tool_mode": true, - "types": [ - "DataFrame" - ], - "value": "__UNDEFINED__" - }, { "allows_loop": false, "cache": true, @@ -1457,6 +1237,8 @@ "group_outputs": false, "method": "load_files_message", "name": "message", + "options": null, + "required_inputs": null, "selected": "Message", "tool_mode": true, "types": [ @@ -1484,7 +1266,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, IntInput\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n inputs = [\n *BaseFileComponent._base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n *BaseFileComponent._base_outputs,\n ]\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" + "value": "from copy import deepcopy\nfrom typing import Any\n\nfrom langflow.base.data.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, FileInput, IntInput, Output\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n _base_inputs = deepcopy(BaseFileComponent._base_inputs)\n\n for input_item in _base_inputs:\n if isinstance(input_item, FileInput) and input_item.name == \"path\":\n input_item.real_time_refresh = True\n break\n\n inputs = [\n *_base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Raw Content\", name=\"message\", method=\"load_files_message\"),\n ]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the number of files processed.\"\"\"\n if field_name == \"path\":\n # Add outputs based on the number of files in the path\n if len(field_value) == 0:\n return frontend_node\n\n frontend_node[\"outputs\"] = []\n\n if len(field_value) == 1:\n # We need to check if the file is structured content\n file_path = frontend_node[\"template\"][\"path\"][\"file_path\"][0]\n if file_path.endswith((\".csv\", \".xlsx\", \".parquet\")):\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Structured Content\", name=\"dataframe\", method=\"load_files_structured\"),\n )\n elif file_path.endswith(\".json\"):\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Structured Content\", name=\"json\", method=\"load_files_json\"),\n )\n\n # All files get the raw content and path outputs\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Raw Content\", name=\"message\", method=\"load_files_message\"),\n )\n frontend_node[\"outputs\"].append(\n Output(display_name=\"File Path\", name=\"path\", method=\"load_files_path\"),\n )\n else:\n # For multiple files, we only show the files output\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Files\", name=\"dataframe\", method=\"load_files\"),\n )\n\n return frontend_node\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" }, "concurrency_multithreading": { "_input_type": "IntInput", @@ -1615,6 +1397,7 @@ "list_add_label": "Add More", "name": "path", "placeholder": "", + "real_time_refresh": true, "required": false, "show": true, "temp_file": false, @@ -1681,32 +1464,33 @@ }, "tool_mode": false }, + "selected_output": "message", "showNode": true, "type": "File" }, "dragging": false, - "id": "File-cLJI5", + "id": "File-b2gOG", "measured": { - "height": 233, + "height": 229, "width": 320 }, "position": { - "x": 148.2876240515333, - "y": -84.59506014967624 + "x": 521.4495499211398, + "y": -58.80407388658117 }, - "selected": true, + "selected": false, "type": "genericNode" } ], "viewport": { - "x": 262.39677390913835, - "y": 326.83109064058834, - "zoom": 0.6244838480835059 + "x": 25.718555473905894, + "y": 276.4285357228162, + "zoom": 0.7396221600663749 } }, "description": "Integrates PDF reading with a language model to answer document-specific questions. Ideal for small-scale texts, it facilitates direct queries with immediate insights.", "endpoint_name": null, - "id": "8aa6b909-19cb-4e09-8395-a107d6aa278b", + "id": "f313f4f1-d0bf-43df-8d7f-2efc99d7d553", "is_component": false, "last_tested_version": "1.4.3", "name": "Document Q&A", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json b/src/backend/base/langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json index 924256e94..55e384105 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Portfolio Website Code Generator.json @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "TextInput", - "id": "TextInput-5rO9p", + "id": "TextInput-349bj", "name": "text", "output_types": [ "Message" @@ -15,19 +15,19 @@ }, "targetHandle": { "fieldName": "system_message", - "id": "AnthropicModel-Te07A", + "id": "AnthropicModel-gTuZr", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-TextInput-5rO9p{œdataTypeœ:œTextInputœ,œidœ:œTextInput-5rO9pœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-Te07A{œfieldNameœ:œsystem_messageœ,œidœ:œAnthropicModel-Te07Aœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-TextInput-349bj{œdataTypeœ:œTextInputœ,œidœ:œTextInput-349bjœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-gTuZr{œfieldNameœ:œsystem_messageœ,œidœ:œAnthropicModel-gTuZrœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "TextInput-5rO9p", - "sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-5rO9pœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", - "target": "AnthropicModel-Te07A", - "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œAnthropicModel-Te07Aœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "TextInput-349bj", + "sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-349bjœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}", + "target": "AnthropicModel-gTuZr", + "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œAnthropicModel-gTuZrœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -35,7 +35,7 @@ "data": { "sourceHandle": { "dataType": "AnthropicModel", - "id": "AnthropicModel-baay5", + "id": "AnthropicModel-4vzkg", "name": "model_output", "output_types": [ "LanguageModel" @@ -43,76 +43,19 @@ }, "targetHandle": { "fieldName": "llm", - "id": "StructuredOutput-GJfDv", + "id": "StructuredOutput-UHT27", "inputTypes": [ "LanguageModel" ], "type": "other" } }, - "id": "reactflow__edge-AnthropicModel-baay5{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-baay5œ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-GJfDv{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-GJfDvœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-AnthropicModel-4vzkg{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-4vzkgœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-StructuredOutput-UHT27{œfieldNameœ:œllmœ,œidœ:œStructuredOutput-UHT27œ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", "selected": false, - "source": "AnthropicModel-baay5", - "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-baay5œ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", - "target": "StructuredOutput-GJfDv", - "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-GJfDvœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "ParserComponent", - "id": "ParserComponent-FPw08", - "name": "parsed_text", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "input_value", - "id": "StructuredOutput-GJfDv", - "inputTypes": [ - "Message" - ], - "type": "str" - } - }, - "id": "reactflow__edge-ParserComponent-FPw08{œdataTypeœ:œParserComponentœ,œidœ:œParserComponent-FPw08œ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-GJfDv{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-GJfDvœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "selected": false, - "source": "ParserComponent-FPw08", - "sourceHandle": "{œdataTypeœ: œParserComponentœ, œidœ: œParserComponent-FPw08œ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "StructuredOutput-GJfDv", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-GJfDvœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "File", - "id": "File-nhzgk", - "name": "dataframe", - "output_types": [ - "DataFrame" - ] - }, - "targetHandle": { - "fieldName": "input_data", - "id": "ParserComponent-FPw08", - "inputTypes": [ - "DataFrame", - "Data" - ], - "type": "other" - } - }, - "id": "reactflow__edge-File-nhzgk{œdataTypeœ:œFileœ,œidœ:œFile-nhzgkœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-ParserComponent-FPw08{œfieldNameœ:œinput_dataœ,œidœ:œParserComponent-FPw08œ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", - "selected": false, - "source": "File-nhzgk", - "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-nhzgkœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", - "target": "ParserComponent-FPw08", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œParserComponent-FPw08œ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + "source": "AnthropicModel-4vzkg", + "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-4vzkgœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", + "target": "StructuredOutput-UHT27", + "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œStructuredOutput-UHT27œ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -120,7 +63,7 @@ "data": { "sourceHandle": { "dataType": "AnthropicModel", - "id": "AnthropicModel-Te07A", + "id": "AnthropicModel-gTuZr", "name": "text_output", "output_types": [ "Message" @@ -128,7 +71,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-XN5RE", + "id": "ChatOutput-4ofWu", "inputTypes": [ "Data", "DataFrame", @@ -137,12 +80,12 @@ "type": "str" } }, - "id": "reactflow__edge-AnthropicModel-Te07A{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-Te07Aœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-XN5RE{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-XN5REœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-AnthropicModel-gTuZr{œdataTypeœ:œAnthropicModelœ,œidœ:œAnthropicModel-gTuZrœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-4ofWu{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-4ofWuœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "AnthropicModel-Te07A", - "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-Te07Aœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-XN5RE", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-XN5REœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "source": "AnthropicModel-gTuZr", + "sourceHandle": "{œdataTypeœ: œAnthropicModelœ, œidœ: œAnthropicModel-gTuZrœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-4ofWu", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-4ofWuœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -150,7 +93,7 @@ "data": { "sourceHandle": { "dataType": "parser", - "id": "parser-8Jyry", + "id": "parser-GmFfK", "name": "parsed_text", "output_types": [ "Message" @@ -158,26 +101,27 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "AnthropicModel-Te07A", + "id": "AnthropicModel-gTuZr", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-parser-8Jyry{œdataTypeœ:œparserœ,œidœ:œparser-8Jyryœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-Te07A{œfieldNameœ:œinput_valueœ,œidœ:œAnthropicModel-Te07Aœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-parser-GmFfK{œdataTypeœ:œparserœ,œidœ:œparser-GmFfKœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-AnthropicModel-gTuZr{œfieldNameœ:œinput_valueœ,œidœ:œAnthropicModel-gTuZrœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "parser-8Jyry", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-8Jyryœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "AnthropicModel-Te07A", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAnthropicModel-Te07Aœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "parser-GmFfK", + "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-GmFfKœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "AnthropicModel-gTuZr", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAnthropicModel-gTuZrœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "StructuredOutput", - "id": "StructuredOutput-GJfDv", + "id": "StructuredOutput-UHT27", "name": "structured_output", "output_types": [ "Data" @@ -185,7 +129,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "parser-8Jyry", + "id": "parser-GmFfK", "inputTypes": [ "DataFrame", "Data" @@ -193,18 +137,44 @@ "type": "other" } }, - "id": "xy-edge__StructuredOutput-GJfDv{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-GJfDvœ,œnameœ:œstructured_outputœ,œoutput_typesœ:[œDataœ]}-parser-8Jyry{œfieldNameœ:œinput_dataœ,œidœ:œparser-8Jyryœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-StructuredOutput-UHT27{œdataTypeœ:œStructuredOutputœ,œidœ:œStructuredOutput-UHT27œ,œnameœ:œstructured_outputœ,œoutput_typesœ:[œDataœ]}-parser-GmFfK{œfieldNameœ:œinput_dataœ,œidœ:œparser-GmFfKœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", "selected": false, - "source": "StructuredOutput-GJfDv", - "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-GJfDvœ, œnameœ: œstructured_outputœ, œoutput_typesœ: [œDataœ]}", - "target": "parser-8Jyry", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-8Jyryœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + "source": "StructuredOutput-UHT27", + "sourceHandle": "{œdataTypeœ: œStructuredOutputœ, œidœ: œStructuredOutput-UHT27œ, œnameœ: œstructured_outputœ, œoutput_typesœ: [œDataœ]}", + "target": "parser-GmFfK", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-GmFfKœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "File", + "id": "File-B56ad", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "StructuredOutput-UHT27", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "xy-edge__File-B56ad{œdataTypeœ:œFileœ,œidœ:œFile-B56adœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-StructuredOutput-UHT27{œfieldNameœ:œinput_valueœ,œidœ:œStructuredOutput-UHT27œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "File-B56ad", + "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-B56adœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "StructuredOutput-UHT27", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œStructuredOutput-UHT27œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" } ], "nodes": [ { "data": { - "id": "TextInput-5rO9p", + "id": "TextInput-349bj", "node": { "base_classes": [ "Message" @@ -298,9 +268,9 @@ "type": "TextInput" }, "dragging": false, - "id": "TextInput-5rO9p", + "id": "TextInput-349bj", "measured": { - "height": 204, + "height": 203, "width": 320 }, "position": { @@ -312,7 +282,7 @@ }, { "data": { - "id": "AnthropicModel-baay5", + "id": "AnthropicModel-4vzkg", "node": { "base_classes": [ "LanguageModel", @@ -403,7 +373,7 @@ "show": true, "title_case": false, "type": "str", - "value": "" + "value": "ANTHROPIC_API_KEY" }, "base_url": { "_input_type": "MessageTextInput", @@ -637,21 +607,21 @@ "type": "AnthropicModel" }, "dragging": false, - "id": "AnthropicModel-baay5", + "id": "AnthropicModel-4vzkg", "measured": { - "height": 510, + "height": 508, "width": 320 }, "position": { - "x": 833.1645098419217, - "y": -53.112118311795115 + "x": 926.3374034022025, + "y": 49.15081364461059 }, "selected": false, "type": "genericNode" }, { "data": { - "id": "AnthropicModel-Te07A", + "id": "AnthropicModel-gTuZr", "node": { "base_classes": [ "LanguageModel", @@ -742,7 +712,7 @@ "show": true, "title_case": false, "type": "str", - "value": "" + "value": "ANTHROPIC_API_KEY" }, "base_url": { "_input_type": "MessageTextInput", @@ -976,9 +946,9 @@ "type": "AnthropicModel" }, "dragging": false, - "id": "AnthropicModel-Te07A", + "id": "AnthropicModel-gTuZr", "measured": { - "height": 510, + "height": 508, "width": 320 }, "position": { @@ -990,7 +960,7 @@ }, { "data": { - "id": "ChatOutput-XN5RE", + "id": "ChatOutput-4ofWu", "node": { "base_classes": [ "Message" @@ -1289,7 +1259,7 @@ "type": "ChatOutput" }, "dragging": false, - "id": "ChatOutput-XN5RE", + "id": "ChatOutput-4ofWu", "measured": { "height": 48, "width": 192 @@ -1303,7 +1273,7 @@ }, { "data": { - "id": "note-OWjt8", + "id": "note-5KyRW", "node": { "description": "### 💡 Upload your resume here", "display_name": "", @@ -1316,14 +1286,14 @@ }, "dragging": false, "height": 358, - "id": "note-OWjt8", + "id": "note-5KyRW", "measured": { "height": 358, - "width": 345 + "width": 346 }, "position": { - "x": 309.867023997511, - "y": 504.0319457920041 + "x": 956.8090415611422, + "y": 591.8723166928154 }, "resizing": false, "selected": false, @@ -1332,7 +1302,7 @@ }, { "data": { - "id": "note-jSID0", + "id": "note-DksnL", "node": { "description": "## 📝 Portfolio Website Code Generator\n\nYour uploaded resume is parsed into a structured format, and output as HTML/CSS code for your own resume website!\n\n✅ **Accepted Formats:** PDF or TXT \n✅ To ensure readability, provide clear headings, bullet points, and proper formatting. \n### 📌 Structured output fields:\n1. 🏷 **Full Name** - Candidate's full name \n2. 📧 **Email** - A valid email address \n3. 📞 **Phone Number** - Contact number \n4. 🔗 **LinkedIn** - LinkedIn profile URL \n5. 🖥 **GitHub** - GitHub profile URL (if applicable) \n6. 🌐 **Portfolio** - Personal website or portfolio URL \n7. 🛂 **Visa Status** - Work authorization details (if applicable) \n8. 📝 **Summary** - A brief professional summary or objective statement \n9. 💼 **Experience** - Work experience details (in dictionary format) \n10. 🎓 **Education** - Education details (in dictionary format) \n11. 🛠 **Skills** - Skills mentioned in the resume (comma-separated) \n12. 🚀 **Projects** - Titles, descriptions, and details of projects.", "display_name": "", @@ -1343,14 +1313,14 @@ }, "dragging": false, "height": 621, - "id": "note-jSID0", + "id": "note-DksnL", "measured": { "height": 621, "width": 478 }, "position": { - "x": -195.6127187156656, - "y": 477.17146980202494 + "x": 386.51295365584554, + "y": 170.2187453922128 }, "resizing": false, "selected": false, @@ -1359,7 +1329,7 @@ }, { "data": { - "id": "note-ufZW4", + "id": "note-yABNr", "node": { "description": "### 💡 Click **Open table** to view the schema", "display_name": "", @@ -1372,10 +1342,10 @@ }, "dragging": false, "height": 438, - "id": "note-ufZW4", + "id": "note-yABNr", "measured": { "height": 438, - "width": 358 + "width": 359 }, "position": { "x": 1293.169988591412, @@ -1388,7 +1358,7 @@ }, { "data": { - "id": "note-trdVO", + "id": "note-kqPtU", "node": { "description": "### 💡 Add your Anthropic API key here", "display_name": "", @@ -1401,14 +1371,14 @@ }, "dragging": false, "height": 324, - "id": "note-trdVO", + "id": "note-kqPtU", "measured": { "height": 324, - "width": 361 + "width": 362 }, "position": { - "x": 814.421927194915, - "y": -106.9729752344411 + "x": 924.6299565901533, + "y": 1.8200755150596493 }, "resizing": false, "selected": false, @@ -1417,7 +1387,7 @@ }, { "data": { - "id": "note-UU4gf", + "id": "note-AUL5d", "node": { "description": "### 💡 Add your Anthropic API key here", "display_name": "", @@ -1430,14 +1400,14 @@ }, "dragging": false, "height": 324, - "id": "note-UU4gf", + "id": "note-AUL5d", "measured": { "height": 324, - "width": 343 + "width": 344 }, "position": { - "x": 2185.328157509855, - "y": 431.30980788789844 + "x": 2191.028572823402, + "y": 446.51091539068995 }, "resizing": false, "selected": false, @@ -1446,7 +1416,7 @@ }, { "data": { - "id": "StructuredOutput-GJfDv", + "id": "StructuredOutput-UHT27", "node": { "base_classes": [ "Data", @@ -1769,9 +1739,9 @@ "type": "StructuredOutput" }, "dragging": false, - "id": "StructuredOutput-GJfDv", + "id": "StructuredOutput-UHT27", "measured": { - "height": 349, + "height": 348, "width": 320 }, "position": { @@ -1783,481 +1753,7 @@ }, { "data": { - "id": "File-nhzgk", - "node": { - "base_classes": [ - "Data" - ], - "beta": false, - "category": "data", - "conditional_paths": [], - "custom_fields": {}, - "description": "Loads content from one or more files as a DataFrame.", - "display_name": "File", - "documentation": "", - "edited": false, - "field_order": [ - "path", - "file_path", - "silent_errors", - "delete_server_file_after_processing", - "ignore_unsupported_extensions", - "ignore_unspecified_files", - "use_multithreading", - "concurrency_multithreading" - ], - "frozen": false, - "icon": "file-text", - "key": "File", - "legacy": false, - "lf_version": "1.2.0", - "metadata": {}, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Loaded Files", - "group_outputs": false, - "method": "load_files", - "name": "dataframe", - "selected": "DataFrame", - "tool_mode": true, - "types": [ - "DataFrame" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Raw Content", - "group_outputs": false, - "method": "load_files_message", - "name": "message", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "score": 9.159206968830713e-17, - "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.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, IntInput\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n inputs = [\n *BaseFileComponent._base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n *BaseFileComponent._base_outputs,\n ]\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" - }, - "concurrency_multithreading": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Processing Concurrency", - "dynamic": false, - "info": "When multiple files are being processed, the number of files to process concurrently.", - "list": false, - "list_add_label": "Add More", - "name": "concurrency_multithreading", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": 1 - }, - "delete_server_file_after_processing": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Delete Server File After Processing", - "dynamic": false, - "info": "If true, the Server File Path will be deleted after processing.", - "list": false, - "list_add_label": "Add More", - "name": "delete_server_file_after_processing", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "file_path": { - "_input_type": "HandleInput", - "advanced": true, - "display_name": "Server File Path", - "dynamic": false, - "info": "Data object with a 'file_path' property pointing to server file or a Message object with a path to the file. Supercedes 'Path' but supports same file types.", - "input_types": [ - "Data", - "Message" - ], - "list": true, - "list_add_label": "Add More", - "name": "file_path", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "other", - "value": "" - }, - "ignore_unspecified_files": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Ignore Unspecified Files", - "dynamic": false, - "info": "If true, Data with no 'file_path' property will be ignored.", - "list": false, - "list_add_label": "Add More", - "name": "ignore_unspecified_files", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "ignore_unsupported_extensions": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Ignore Unsupported Extensions", - "dynamic": false, - "info": "If true, files with unsupported extensions will not be processed.", - "list": false, - "list_add_label": "Add More", - "name": "ignore_unsupported_extensions", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "path": { - "_input_type": "FileInput", - "advanced": false, - "display_name": "Files", - "dynamic": false, - "fileTypes": [ - "txt", - "md", - "mdx", - "csv", - "json", - "yaml", - "yml", - "xml", - "html", - "htm", - "pdf", - "docx", - "py", - "sh", - "sql", - "js", - "ts", - "tsx", - "zip", - "tar", - "tgz", - "bz2", - "gz" - ], - "file_path": [], - "info": "Supported file extensions: txt, md, mdx, csv, json, yaml, yml, xml, html, htm, pdf, docx, py, sh, sql, js, ts, tsx; optionally bundled in file extensions: zip, tar, tgz, bz2, gz", - "list": true, - "list_add_label": "Add More", - "name": "path", - "placeholder": "", - "required": false, - "show": true, - "temp_file": false, - "title_case": false, - "trace_as_metadata": true, - "type": "file", - "value": "" - }, - "separator": { - "_input_type": "StrInput", - "advanced": true, - "display_name": "Separator", - "dynamic": false, - "info": "Specify the separator to use between multiple outputs in Message format.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "separator", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "\n\n" - }, - "silent_errors": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Silent Errors", - "dynamic": false, - "info": "If true, errors will not raise an exception.", - "list": false, - "list_add_label": "Add More", - "name": "silent_errors", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "use_multithreading": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "[Deprecated] Use Multithreading", - "dynamic": false, - "info": "Set 'Processing Concurrency' greater than 1 to enable multithreading.", - "list": false, - "list_add_label": "Add More", - "name": "use_multithreading", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - } - }, - "tool_mode": false - }, - "selected_output": "dataframe", - "showNode": true, - "type": "File" - }, - "dragging": false, - "id": "File-nhzgk", - "measured": { - "height": 230, - "width": 320 - }, - "position": { - "x": 313.61127452987654, - "y": 566.1365503756354 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "ParserComponent-FPw08", - "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "category": "processing", - "conditional_paths": [], - "custom_fields": {}, - "description": "Extracts text using a template.", - "display_name": "Parser", - "documentation": "", - "edited": false, - "field_order": [ - "stringify", - "template", - "input_data", - "sep" - ], - "frozen": false, - "icon": "braces", - "key": "ParserComponent", - "legacy": false, - "lf_version": "1.2.0", - "metadata": {}, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Parsed Text", - "group_outputs": false, - "method": "parse_combined_text", - "name": "parsed_text", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "score": 0.001, - "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.custom.custom_component.component import Component\nfrom langflow.helpers.data import safe_convert\nfrom langflow.inputs.inputs import BoolInput, HandleInput, MessageTextInput, MultilineInput, TabInput\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.template.field.base import Output\n\n\nclass ParserComponent(Component):\n display_name = \"Parser\"\n description = \"Extracts text using a template.\"\n icon = \"braces\"\n\n inputs = [\n HandleInput(\n name=\"input_data\",\n display_name=\"Data or DataFrame\",\n input_types=[\"DataFrame\", \"Data\"],\n info=\"Accepts either a DataFrame or a Data object.\",\n required=True,\n ),\n TabInput(\n name=\"mode\",\n display_name=\"Mode\",\n options=[\"Parser\", \"Stringify\"],\n value=\"Parser\",\n info=\"Convert into raw string instead of using a template.\",\n real_time_refresh=True,\n ),\n MultilineInput(\n name=\"pattern\",\n display_name=\"Template\",\n info=(\n \"Use variables within curly brackets to extract column values for DataFrames \"\n \"or key values for Data.\"\n \"For example: `Name: {Name}, Age: {Age}, Country: {Country}`\"\n ),\n value=\"Text: {text}\", # Example default\n dynamic=True,\n show=True,\n required=True,\n ),\n MessageTextInput(\n name=\"sep\",\n display_name=\"Separator\",\n advanced=True,\n value=\"\\n\",\n info=\"String used to separate rows/items.\",\n ),\n ]\n\n outputs = [\n Output(\n display_name=\"Parsed Text\",\n name=\"parsed_text\",\n info=\"Formatted text output.\",\n method=\"parse_combined_text\",\n ),\n ]\n\n def update_build_config(self, build_config, field_value, field_name=None):\n \"\"\"Dynamically hide/show `template` and enforce requirement based on `stringify`.\"\"\"\n if field_name == \"mode\":\n build_config[\"pattern\"][\"show\"] = self.mode == \"Parser\"\n build_config[\"pattern\"][\"required\"] = self.mode == \"Parser\"\n if field_value:\n clean_data = BoolInput(\n name=\"clean_data\",\n display_name=\"Clean Data\",\n info=(\n \"Enable to clean the data by removing empty rows and lines \"\n \"in each cell of the DataFrame/ Data object.\"\n ),\n value=True,\n advanced=True,\n required=False,\n )\n build_config[\"clean_data\"] = clean_data.to_dict()\n else:\n build_config.pop(\"clean_data\", None)\n\n return build_config\n\n def _clean_args(self):\n \"\"\"Prepare arguments based on input type.\"\"\"\n input_data = self.input_data\n\n match input_data:\n case list() if all(isinstance(item, Data) for item in input_data):\n msg = \"List of Data objects is not supported.\"\n raise ValueError(msg)\n case DataFrame():\n return input_data, None\n case Data():\n return None, input_data\n case dict() if \"data\" in input_data:\n try:\n if \"columns\" in input_data: # Likely a DataFrame\n return DataFrame.from_dict(input_data), None\n # Likely a Data object\n return None, Data(**input_data)\n except (TypeError, ValueError, KeyError) as e:\n msg = f\"Invalid structured input provided: {e!s}\"\n raise ValueError(msg) from e\n case _:\n msg = f\"Unsupported input type: {type(input_data)}. Expected DataFrame or Data.\"\n raise ValueError(msg)\n\n def parse_combined_text(self) -> Message:\n \"\"\"Parse all rows/items into a single text or convert input to string if `stringify` is enabled.\"\"\"\n # Early return for stringify option\n if self.mode == \"Stringify\":\n return self.convert_to_string()\n\n df, data = self._clean_args()\n\n lines = []\n if df is not None:\n for _, row in df.iterrows():\n formatted_text = self.pattern.format(**row.to_dict())\n lines.append(formatted_text)\n elif data is not None:\n formatted_text = self.pattern.format(**data.data)\n lines.append(formatted_text)\n\n combined_text = self.sep.join(lines)\n self.status = combined_text\n return Message(text=combined_text)\n\n def convert_to_string(self) -> Message:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n result = \"\"\n if isinstance(self.input_data, list):\n result = \"\\n\".join([safe_convert(item, clean_data=self.clean_data or False) for item in self.input_data])\n else:\n result = safe_convert(self.input_data or False)\n self.log(f\"Converted to string with length: {len(result)}\")\n\n message = Message(text=result)\n self.status = message\n return message\n" - }, - "input_data": { - "_input_type": "HandleInput", - "advanced": false, - "display_name": "Data or DataFrame", - "dynamic": false, - "info": "Accepts either a DataFrame or a Data object.", - "input_types": [ - "DataFrame", - "Data" - ], - "list": false, - "list_add_label": "Add More", - "name": "input_data", - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "other", - "value": "" - }, - "mode": { - "_input_type": "TabInput", - "advanced": false, - "display_name": "Mode", - "dynamic": false, - "info": "Convert into raw string instead of using a template.", - "name": "mode", - "options": [ - "Parser", - "Stringify" - ], - "placeholder": "", - "real_time_refresh": true, - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "tab", - "value": "Parser" - }, - "pattern": { - "_input_type": "MultilineInput", - "advanced": false, - "copy_field": false, - "display_name": "Template", - "dynamic": true, - "info": "Use variables within curly brackets to extract column values for DataFrames or key values for Data.For example: `Name: {Name}, Age: {Age}, Country: {Country}`", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "pattern", - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "Text: {text}" - }, - "sep": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Separator", - "dynamic": false, - "info": "String used to separate rows/items.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "sep", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "\n" - } - }, - "tool_mode": false - }, - "selected_output": "parsed_text", - "showNode": true, - "type": "ParserComponent" - }, - "dragging": false, - "id": "ParserComponent-FPw08", - "measured": { - "height": 329, - "width": 320 - }, - "position": { - "x": 705.1095587847711, - "y": 565.677039723129 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "parser-8Jyry", + "id": "parser-GmFfK", "node": { "base_classes": [ "Message" @@ -2419,7 +1915,7 @@ "type": "parser" }, "dragging": false, - "id": "parser-8Jyry", + "id": "parser-GmFfK", "measured": { "height": 278, "width": 320 @@ -2430,17 +1926,297 @@ }, "selected": false, "type": "genericNode" + }, + { + "data": { + "id": "File-B56ad", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Loads content from one or more files as a DataFrame.", + "display_name": "File", + "documentation": "", + "edited": false, + "field_order": [ + "path", + "file_path", + "separator", + "silent_errors", + "delete_server_file_after_processing", + "ignore_unsupported_extensions", + "ignore_unspecified_files", + "use_multithreading", + "concurrency_multithreading" + ], + "frozen": false, + "icon": "file-text", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Raw Content", + "group_outputs": false, + "method": "load_files_message", + "name": "message", + "selected": "Message", + "tool_mode": true, + "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 copy import deepcopy\nfrom typing import Any\n\nfrom langflow.base.data.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, FileInput, IntInput, Output\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n _base_inputs = deepcopy(BaseFileComponent._base_inputs)\n\n for input_item in _base_inputs:\n if isinstance(input_item, FileInput) and input_item.name == \"path\":\n input_item.real_time_refresh = True\n break\n\n inputs = [\n *_base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Raw Content\", name=\"message\", method=\"load_files_message\"),\n ]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the number of files processed.\"\"\"\n if field_name == \"path\":\n # Add outputs based on the number of files in the path\n if len(field_value) == 0:\n return frontend_node\n\n frontend_node[\"outputs\"] = []\n\n if len(field_value) == 1:\n # We need to check if the file is structured content\n file_path = frontend_node[\"template\"][\"path\"][\"file_path\"][0]\n if file_path.endswith((\".csv\", \".xlsx\", \".parquet\")):\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Structured Content\", name=\"dataframe\", method=\"load_files_structured\"),\n )\n elif file_path.endswith(\".json\"):\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Structured Content\", name=\"json\", method=\"load_files_json\"),\n )\n\n # All files get the raw content and path outputs\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Raw Content\", name=\"message\", method=\"load_files_message\"),\n )\n frontend_node[\"outputs\"].append(\n Output(display_name=\"File Path\", name=\"path\", method=\"load_files_path\"),\n )\n else:\n # For multiple files, we only show the files output\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Files\", name=\"dataframe\", method=\"load_files\"),\n )\n\n return frontend_node\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" + }, + "concurrency_multithreading": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Processing Concurrency", + "dynamic": false, + "info": "When multiple files are being processed, the number of files to process concurrently.", + "list": false, + "list_add_label": "Add More", + "name": "concurrency_multithreading", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 1 + }, + "delete_server_file_after_processing": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Delete Server File After Processing", + "dynamic": false, + "info": "If true, the Server File Path will be deleted after processing.", + "list": false, + "list_add_label": "Add More", + "name": "delete_server_file_after_processing", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "file_path": { + "_input_type": "HandleInput", + "advanced": true, + "display_name": "Server File Path", + "dynamic": false, + "info": "Data object with a 'file_path' property pointing to server file or a Message object with a path to the file. Supercedes 'Path' but supports same file types.", + "input_types": [ + "Data", + "Message" + ], + "list": true, + "list_add_label": "Add More", + "name": "file_path", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "ignore_unspecified_files": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Ignore Unspecified Files", + "dynamic": false, + "info": "If true, Data with no 'file_path' property will be ignored.", + "list": false, + "list_add_label": "Add More", + "name": "ignore_unspecified_files", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "ignore_unsupported_extensions": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Ignore Unsupported Extensions", + "dynamic": false, + "info": "If true, files with unsupported extensions will not be processed.", + "list": false, + "list_add_label": "Add More", + "name": "ignore_unsupported_extensions", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "path": { + "_input_type": "FileInput", + "advanced": false, + "display_name": "Files", + "dynamic": false, + "fileTypes": [ + "txt", + "md", + "mdx", + "csv", + "json", + "yaml", + "yml", + "xml", + "html", + "htm", + "pdf", + "docx", + "py", + "sh", + "sql", + "js", + "ts", + "tsx", + "zip", + "tar", + "tgz", + "bz2", + "gz" + ], + "file_path": [], + "info": "Supported file extensions: txt, md, mdx, csv, json, yaml, yml, xml, html, htm, pdf, docx, py, sh, sql, js, ts, tsx; optionally bundled in file extensions: zip, tar, tgz, bz2, gz", + "list": true, + "list_add_label": "Add More", + "name": "path", + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "temp_file": false, + "title_case": false, + "trace_as_metadata": true, + "type": "file", + "value": "" + }, + "separator": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Separator", + "dynamic": false, + "info": "Specify the separator to use between multiple outputs in Message format.", + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "separator", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "\n\n" + }, + "silent_errors": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Silent Errors", + "dynamic": false, + "info": "If true, errors will not raise an exception.", + "list": false, + "list_add_label": "Add More", + "name": "silent_errors", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "use_multithreading": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "[Deprecated] Use Multithreading", + "dynamic": false, + "info": "Set 'Processing Concurrency' greater than 1 to enable multithreading.", + "list": false, + "list_add_label": "Add More", + "name": "use_multithreading", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "File" + }, + "dragging": false, + "id": "File-B56ad", + "measured": { + "height": 229, + "width": 320 + }, + "position": { + "x": 927.3702382252801, + "y": 647.5603858397075 + }, + "selected": false, + "type": "genericNode" } ], "viewport": { - "x": -459.3293356910542, - "y": 141.83609996213664, - "zoom": 0.5655674747790203 + "x": -79.22365165434849, + "y": 83.98976192819657, + "zoom": 0.5262774438330168 } }, "description": "This template transforms PDF or TXT resume documents into structured JSON, generating a portfolio website HTML code from the structured data.", "endpoint_name": null, - "id": "0d96753b-833e-48ca-8548-ca82b8ab6823", + "id": "94db95ca-aaf2-4968-81cd-d0eeaa4d9699", "is_component": false, "last_tested_version": "1.4.3", "name": "Portfolio Website Code Generator", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Text Sentiment Analysis.json b/src/backend/base/langflow/initial_setup/starter_projects/Text Sentiment Analysis.json index 505d30ff7..e329089ba 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Text Sentiment Analysis.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Text Sentiment Analysis.json @@ -4,37 +4,10 @@ { "animated": false, "className": "", - "data": { - "sourceHandle": { - "dataType": "File", - "id": "File-fosKE", - "name": "message", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "text", - "id": "Prompt-3IzFz", - "inputTypes": [ - "Message" - ], - "type": "str" - } - }, - "id": "reactflow__edge-File-fosKE{œdataTypeœ:œFileœ,œidœ:œFile-fosKEœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-3IzFz{œfieldNameœ:œtextœ,œidœ:œPrompt-3IzFzœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "selected": false, - "source": "File-fosKE", - "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-fosKEœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-3IzFz", - "targetHandle": "{œfieldNameœ: œtextœ, œidœ: œPrompt-3IzFzœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" - }, - { - "animated": false, "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-3IzFz", + "id": "Prompt-RaLHB", "name": "prompt", "output_types": [ "Message" @@ -42,26 +15,27 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "LanguageModelComponent-oBZy3", + "id": "LanguageModelComponent-qFXT1", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "xy-edge__Prompt-3IzFz{œdataTypeœ:œPromptœ,œidœ:œPrompt-3IzFzœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-oBZy3{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-oBZy3œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Prompt-RaLHB{œdataTypeœ:œPromptœ,œidœ:œPrompt-RaLHBœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-qFXT1{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-qFXT1œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Prompt-3IzFz", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-3IzFzœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "LanguageModelComponent-oBZy3", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-oBZy3œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Prompt-RaLHB", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-RaLHBœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-qFXT1", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-qFXT1œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "LanguageModelComponent", - "id": "LanguageModelComponent-oBZy3", + "id": "LanguageModelComponent-qFXT1", "name": "text_output", "output_types": [ "Message" @@ -69,26 +43,27 @@ }, "targetHandle": { "fieldName": "summary", - "id": "Prompt-roFWL", + "id": "Prompt-C5FYM", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "xy-edge__LanguageModelComponent-oBZy3{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-oBZy3œ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-Prompt-roFWL{œfieldNameœ:œsummaryœ,œidœ:œPrompt-roFWLœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-LanguageModelComponent-qFXT1{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-qFXT1œ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-Prompt-C5FYM{œfieldNameœ:œsummaryœ,œidœ:œPrompt-C5FYMœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "LanguageModelComponent-oBZy3", - "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-oBZy3œ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-roFWL", - "targetHandle": "{œfieldNameœ: œsummaryœ, œidœ: œPrompt-roFWLœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "LanguageModelComponent-qFXT1", + "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-qFXT1œ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-C5FYM", + "targetHandle": "{œfieldNameœ: œsummaryœ, œidœ: œPrompt-C5FYMœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-roFWL", + "id": "Prompt-C5FYM", "name": "prompt", "output_types": [ "Message" @@ -96,26 +71,27 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "LanguageModelComponent-k9uIC", + "id": "LanguageModelComponent-Wp3pC", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "xy-edge__Prompt-roFWL{œdataTypeœ:œPromptœ,œidœ:œPrompt-roFWLœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-k9uIC{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-k9uICœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Prompt-C5FYM{œdataTypeœ:œPromptœ,œidœ:œPrompt-C5FYMœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-Wp3pC{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-Wp3pCœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Prompt-roFWL", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-roFWLœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "LanguageModelComponent-k9uIC", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-k9uICœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Prompt-C5FYM", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-C5FYMœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-Wp3pC", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-Wp3pCœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "LanguageModelComponent", - "id": "LanguageModelComponent-k9uIC", + "id": "LanguageModelComponent-Wp3pC", "name": "text_output", "output_types": [ "Message" @@ -123,7 +99,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-C9zZD", + "id": "ChatOutput-K40Du", "inputTypes": [ "Data", "DataFrame", @@ -132,19 +108,20 @@ "type": "other" } }, - "id": "xy-edge__LanguageModelComponent-k9uIC{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-k9uICœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-C9zZD{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-C9zZDœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-LanguageModelComponent-Wp3pC{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-Wp3pCœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-K40Du{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-K40Duœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", "selected": false, - "source": "LanguageModelComponent-k9uIC", - "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-k9uICœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-C9zZD", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-C9zZDœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" + "source": "LanguageModelComponent-Wp3pC", + "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-Wp3pCœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-K40Du", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-K40Duœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-ryDcq", + "id": "Prompt-dabhh", "name": "prompt", "output_types": [ "Message" @@ -152,53 +129,27 @@ }, "targetHandle": { "fieldName": "system_message", - "id": "LanguageModelComponent-e7Tpe", + "id": "LanguageModelComponent-gYAmH", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "xy-edge__Prompt-ryDcq{œdataTypeœ:œPromptœ,œidœ:œPrompt-ryDcqœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-e7Tpe{œfieldNameœ:œsystem_messageœ,œidœ:œLanguageModelComponent-e7Tpeœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-Prompt-dabhh{œdataTypeœ:œPromptœ,œidœ:œPrompt-dabhhœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-gYAmH{œfieldNameœ:œsystem_messageœ,œidœ:œLanguageModelComponent-gYAmHœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "Prompt-ryDcq", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-ryDcqœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "LanguageModelComponent-e7Tpe", - "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œLanguageModelComponent-e7Tpeœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" - }, - { - "animated": false, - "data": { - "sourceHandle": { - "dataType": "File", - "id": "File-fosKE", - "name": "message", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "input_value", - "id": "LanguageModelComponent-e7Tpe", - "inputTypes": [ - "Message" - ], - "type": "str" - } - }, - "id": "xy-edge__File-fosKE{œdataTypeœ:œFileœ,œidœ:œFile-fosKEœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-e7Tpe{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-e7Tpeœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "selected": false, - "source": "File-fosKE", - "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-fosKEœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "LanguageModelComponent-e7Tpe", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-e7Tpeœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "Prompt-dabhh", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-dabhhœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-gYAmH", + "targetHandle": "{œfieldNameœ: œsystem_messageœ, œidœ: œLanguageModelComponent-gYAmHœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "animated": false, + "className": "", "data": { "sourceHandle": { "dataType": "LanguageModelComponent", - "id": "LanguageModelComponent-e7Tpe", + "id": "LanguageModelComponent-gYAmH", "name": "text_output", "output_types": [ "Message" @@ -206,7 +157,7 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-0vZ51", + "id": "ChatOutput-yiSgq", "inputTypes": [ "Data", "DataFrame", @@ -215,21 +166,2111 @@ "type": "other" } }, - "id": "xy-edge__LanguageModelComponent-e7Tpe{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-e7Tpeœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-0vZ51{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-0vZ51œ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-LanguageModelComponent-gYAmH{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-gYAmHœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-yiSgq{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-yiSgqœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", "selected": false, - "source": "LanguageModelComponent-e7Tpe", - "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-e7Tpeœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-0vZ51", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-0vZ51œ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" + "source": "LanguageModelComponent-gYAmH", + "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-gYAmHœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-yiSgq", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-yiSgqœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "File", + "id": "File-oAEuF", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "LanguageModelComponent-gYAmH", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "xy-edge__File-oAEuF{œdataTypeœ:œFileœ,œidœ:œFile-oAEuFœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-gYAmH{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-gYAmHœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "File-oAEuF", + "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-oAEuFœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-gYAmH", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-gYAmHœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "File", + "id": "File-oAEuF", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "text", + "id": "Prompt-RaLHB", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "xy-edge__File-oAEuF{œdataTypeœ:œFileœ,œidœ:œFile-oAEuFœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-RaLHB{œfieldNameœ:œtextœ,œidœ:œPrompt-RaLHBœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "File-oAEuF", + "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-oAEuFœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-RaLHB", + "targetHandle": "{œfieldNameœ: œtextœ, œidœ: œPrompt-RaLHBœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" } ], "nodes": [ { "data": { - "id": "File-fosKE", + "id": "Prompt-C5FYM", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": { + "template": [ + "summary" + ] + }, + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "documentation": "", + "edited": false, + "field_order": [ + "template", + "tool_placeholder" + ], + "frozen": false, + "icon": "braces", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Prompt", + "group_outputs": false, + "method": "build_prompt", + "name": "prompt", + "selected": "Message", + "tool_mode": true, + "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.custom_component.component import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, 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 = \"braces\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**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 async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(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" + }, + "summary": { + "advanced": false, + "display_name": "summary", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "summary", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "template": { + "_input_type": "PromptInput", + "advanced": false, + "display_name": "Template", + "dynamic": false, + "info": "", + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "type": "prompt", + "value": "Based on the following summary of discussions in the Langflow community, generate a well-structured and actionable technical recommendation for the development team. The recommendation should be clear, precise, and directly applicable to improve Langflow.\n\nGuidelines for the Action Item:\nBe Specific: Clearly define the issue, feature request, or improvement.\nProvide Context: Briefly justify why this action is necessary based on user discussions.\nSuggest a Next Step: Outline what the technical team should do to address the issue or enhance the platform.\nPrioritize if Relevant: If multiple issues are discussed, focus on the most urgent or impactful one.\n\nSummary:\n{summary}\n\nProvide a concise, structured, and technically sound action item that the Langflow team can implement.\n\nreturn:\n- {summary}\n- [action item]\n- [sentiment]\n- [start_date]\n- [end_date]\n\nyou need to return the data in json format\n\nReturn Format:\nEnsure that both \"summary\" and \"action_item\" are single string values, not lists and without nested objects or additional keys.\n\n" + }, + "tool_placeholder": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Tool Placeholder", + "dynamic": false, + "info": "A placeholder input for tool mode.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "tool_placeholder", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + }, + "tool_mode": false + }, + "selected_output": "prompt", + "showNode": true, + "type": "Prompt" + }, + "dragging": false, + "id": "Prompt-C5FYM", + "measured": { + "height": 366, + "width": 320 + }, + "position": { + "x": -973.805557188769, + "y": 1081.112870159395 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "Prompt-RaLHB", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": { + "template": [ + "text" + ] + }, + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "documentation": "", + "edited": false, + "error": null, + "field_order": [ + "template", + "tool_placeholder" + ], + "frozen": false, + "full_path": null, + "icon": "braces", + "is_composition": null, + "is_input": null, + "is_output": null, + "legacy": false, + "metadata": {}, + "minimized": false, + "name": "", + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Prompt", + "group_outputs": false, + "method": "build_prompt", + "name": "prompt", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "priority": null, + "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.custom_component.component import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, 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 = \"braces\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**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 async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(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" + }, + "template": { + "_input_type": "PromptInput", + "advanced": false, + "display_name": "Template", + "dynamic": false, + "info": "", + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "type": "prompt", + "value": "You are analyzing messages from an online community or discussion platform. Your task is to generate a well-structured, concise, and informative summary of the key discussions that took place in a specific channel or group during a given time period. This summary will provide actionable insights for community managers, stakeholders, or decision-makers. \n\nYou will receive a list of messages containing **message_id, created_at, and message_content**. \n\n### **Task 1: Summarization** \nFrom the **message_content**, generate a cohesive, flowing summary in paragraph form, avoiding bullet points or fragmented structures. The summary should be written in clear, professional English, ensuring a natural reading experience. \n\nFocus on the following key discussion areas (if applicable): \n- **Feature Requests & Enhancements**: Suggestions for new features, improvements, or emerging use cases. \n- **Issues & Problems**: Reports of challenges, technical difficulties, or troubleshooting discussions. \n- **Community Feedback**: Reactions to updates, usability concerns, and overall user sentiment. \n- **General Topics**: Broader discussions relevant to the platform, industry trends, or key interests of the community. \n\nMessages may be written in different languages, depending on the community. However, the summary must always be in English and should be structured as a natural, flowing narrative rather than a list of points. \n\n### **Task 2: Sentiment Analysis** \nAnalyze the overall **sentiment** of the summary based on user feedback, discussions, and general tone. The sentiment should reflect how the community perceives recent updates, issues, or the overall experience. \n\n#### **Classification Criteria:** \n- **Positive**: The discussion contains mostly favorable feedback, enthusiasm about new topics, successful problem resolutions, or constructive engagement. \n- **Neutral**: The discussion is balanced, with a mix of praise, constructive criticism, and open-ended conversations without strong emotions. \n- **Negative**: The discussion is dominated by frustrations, unresolved issues, strong criticisms, or concerns about the platform's direction. \n\n### **Input:** \nHere is the list of messages: \n{text} \n\n### **Return Format:** \n- **summary**: A cohesive, well-structured summary written in continuous prose, without bullet points. \n- **sentiment**: One of the sentiment categories (**Positive, Neutral, or Negative**). \n- **start_date**: The earliest message timestamp in the format **'yyyy-mm-dd'**. \n- **end_date**: The most recent message timestamp in the format **'yyyy-mm-dd'**. \n" + }, + "text": { + "advanced": false, + "display_name": "text", + "dynamic": false, + "field_type": "str", + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "text", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "tool_placeholder": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Tool Placeholder", + "dynamic": false, + "info": "A placeholder input for tool mode.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "tool_placeholder", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + }, + "tool_mode": false + }, + "selected_output": "prompt", + "showNode": true, + "type": "Prompt" + }, + "dragging": false, + "id": "Prompt-RaLHB", + "measured": { + "height": 366, + "width": 320 + }, + "position": { + "x": -1691.3505617322087, + "y": 1010.953782441708 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "Prompt-dabhh", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": { + "template": [] + }, + "description": "Create a prompt template with dynamic variables.", + "display_name": "Prompt", + "documentation": "", + "edited": false, + "field_order": [ + "template", + "tool_placeholder" + ], + "frozen": false, + "icon": "braces", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Prompt", + "group_outputs": false, + "method": "build_prompt", + "name": "prompt", + "selected": "Message", + "tool_mode": true, + "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.custom_component.component import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, 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 = \"braces\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**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 async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(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" + }, + "template": { + "_input_type": "PromptInput", + "advanced": false, + "display_name": "Template", + "dynamic": false, + "info": "", + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "type": "prompt", + "value": "You are an NLP expert assisting the technical team in analyzing user messages. The analysis will be used by the team to make decisions more effectively.\n\n**Task 1:**\n#### **1. Message Classification **\nClassify the message into a **single category** that best describes its purpose. Choose the category from the predefined list below. This classification will help the technical team identify specific themes and make decisions faster.\n\n- **Release Notes**: A message announcing updates, new features, integrations, important information about new versions for users.\n- **Feature Request**: A suggestion or idea for a new feature or enhancement.\n- **Bug Report**: A message reporting a clear technical failure, malfunction, or unexpected behavior in Langflow. The user explicitly describes an issue where: A feature does not work as expected; The system crashes, freezes, or behaves unpredictably.\n- **User Question**: Any question or uncertainty regarding features, functionality, implementation, or general use. This includes both technical questions and general doubts, regardless of complexity or specificity.\n- **Complaint**: A message expressing dissatisfaction or frustration with a feature or issue.\n- **Positive Feedback**: Messages expressing gratitude alongside useful feedback, such as confirming that an issue has been resolved or a feature works as intended.\n\n\n##### **Important Instructions for Classification:**\n- Ensure each message is classified into **only one category** based on its primary intent or purpose. If multiple intents are detected, select the most relevant category that reflects the user's main goal.\n- Do not create new categories or use freeform text for classification. Always use one of the predefined categories exactly as they appear in the list above.\n\n**task 2**\nAnalyze the sentiment of the message and classify it into one of the following categories:\n- **Positive**: The message conveys positivity, satisfaction, gratitude, or encouragement.\n- **Neutral**: The message is factual, descriptive, or lacks any emotional tone.\n- **Negative**: The message conveys frustration, dissatisfaction, or criticism.\n\n\nreturn:\n- [message_id]\n- [message_category] \n- [message_sentiment]\n\nYou need to output the results in JSON format.\n" + }, + "tool_placeholder": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Tool Placeholder", + "dynamic": false, + "info": "A placeholder input for tool mode.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "tool_placeholder", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "Prompt" + }, + "dragging": false, + "id": "Prompt-dabhh", + "measured": { + "height": 283, + "width": 320 + }, + "position": { + "x": -1668.597994833308, + "y": 651.6625805464743 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "note-VyXUq", + "node": { + "description": "# Sentiment Analysis Flow \n\nThis flow processes text data, analyzes sentiment, and provides structured insights. \n\n## Prerequisite\n\n* [OpenAI API key](https://platform.openai.com/docs/)\n\n## Quickstart\n\n1. Add your [OpenAI API key](https://platform.openai.com/docs/) to the OpenAI model components.\n2. In the **File Component**, load text data in `.txt`, `.csv`, or `.json` formats.\n3. Open the **Playground** to see the analysis and recommendation the flow constructs.\n\n## How It Works \n\n1. The **Data to Message** component converts raw data into structured messages. \n\n2. The **NLP expert** prompt provides sentiment analysis, while the **Analyzing messages** prompt summarizes messages from a discussion board.\n\n3. The final **Prompt Component** creates a structured recommendation prompt to ensure that the AI model receives well-formatted input. \n\n4. The **OpenAI Model Component** processes the text and classifies the sentiment as **Positive, Neutral, or Negative**. \n\n", + "display_name": "", + "documentation": "", + "template": {} + }, + "type": "note" + }, + "dragging": false, + "height": 574, + "id": "note-VyXUq", + "measured": { + "height": 574, + "width": 412 + }, + "position": { + "x": -2540.3162463532744, + "y": 678.7879484347079 + }, + "resizing": false, + "selected": false, + "type": "noteNode", + "width": 412 + }, + { + "data": { + "id": "ChatOutput-yiSgq", + "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", + "should_store_message", + "sender", + "sender_name", + "session_id", + "data_template", + "background_color", + "chat_icon", + "text_color", + "clean_data" + ], + "frozen": false, + "icon": "MessagesSquare", + "legacy": false, + "metadata": {}, + "minimized": true, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Output Message", + "group_outputs": false, + "method": "message_response", + "name": "message", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "clean_data": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Basic Clean Data", + "dynamic": false, + "info": "Whether to clean the data", + "list": false, + "list_add_label": "Add More", + "name": "clean_data", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "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 collections.abc import Generator\nfrom typing import Any\n\nimport orjson\nfrom fastapi.encoders import jsonable_encoder\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.helpers.data import safe_convert\nfrom langflow.inputs.inputs import BoolInput, DropdownInput, HandleInput, MessageTextInput\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Source\nfrom langflow.template.field.base import Output\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_AI,\n MESSAGE_SENDER_USER,\n)\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n minimized = True\n\n inputs = [\n HandleInput(\n name=\"input_value\",\n display_name=\"Inputs\",\n info=\"Message to be passed as output.\",\n input_types=[\"Data\", \"DataFrame\", \"Message\"],\n required=True,\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 MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n BoolInput(\n name=\"clean_data\",\n display_name=\"Basic Clean Data\",\n value=True,\n info=\"Whether to clean the data\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Output Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, id_: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if id_:\n source_dict[\"id\"] = id_\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n # Handle case where source is a ChatOpenAI object\n if hasattr(source, \"model_name\"):\n source_dict[\"source\"] = source.model_name\n elif hasattr(source, \"model\"):\n source_dict[\"source\"] = str(source.model)\n else:\n source_dict[\"source\"] = str(source)\n return Source(**source_dict)\n\n async def message_response(self) -> Message:\n # First convert the input to string if needed\n text = self.convert_to_string()\n\n # Get source properties\n source, icon, display_name, source_id = self.get_properties_from_source_component()\n background_color = self.background_color\n text_color = self.text_color\n if self.chat_icon:\n icon = self.chat_icon\n\n # Create or use existing Message object\n if isinstance(self.input_value, Message):\n message = self.input_value\n # Update message properties\n message.text = text\n else:\n message = Message(text=text)\n\n # Set message properties\n message.sender = self.sender\n message.sender_name = self.sender_name\n message.session_id = self.session_id\n message.flow_id = self.graph.flow_id if hasattr(self, \"graph\") else None\n message.properties.source = self._build_source(source_id, display_name, source)\n message.properties.icon = icon\n message.properties.background_color = background_color\n message.properties.text_color = text_color\n\n # Store message if needed\n if self.session_id and self.should_store_message:\n stored_message = await self.send_message(message)\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n\n def _serialize_data(self, data: Data) -> str:\n \"\"\"Serialize Data object to JSON string.\"\"\"\n # Convert data.data to JSON-serializable format\n serializable_data = jsonable_encoder(data.data)\n # Serialize with orjson, enabling pretty printing with indentation\n json_bytes = orjson.dumps(serializable_data, option=orjson.OPT_INDENT_2)\n # Convert bytes to string and wrap in Markdown code blocks\n return \"```json\\n\" + json_bytes.decode(\"utf-8\") + \"\\n```\"\n\n def _validate_input(self) -> None:\n \"\"\"Validate the input data and raise ValueError if invalid.\"\"\"\n if self.input_value is None:\n msg = \"Input data cannot be None\"\n raise ValueError(msg)\n if isinstance(self.input_value, list) and not all(\n isinstance(item, Message | Data | DataFrame | str) for item in self.input_value\n ):\n invalid_types = [\n type(item).__name__\n for item in self.input_value\n if not isinstance(item, Message | Data | DataFrame | str)\n ]\n msg = f\"Expected Data or DataFrame or Message or str, got {invalid_types}\"\n raise TypeError(msg)\n if not isinstance(\n self.input_value,\n Message | Data | DataFrame | str | list | Generator | type(None),\n ):\n type_name = type(self.input_value).__name__\n msg = f\"Expected Data or DataFrame or Message or str, Generator or None, got {type_name}\"\n raise TypeError(msg)\n\n def convert_to_string(self) -> str | Generator[Any, None, None]:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n self._validate_input()\n if isinstance(self.input_value, list):\n return \"\\n\".join([safe_convert(item, clean_data=self.clean_data) for item in self.input_value])\n if isinstance(self.input_value, Generator):\n return self.input_value\n return safe_convert(self.input_value)\n" + }, + "data_template": { + "_input_type": "MessageTextInput", + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "data_template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{text}" + }, + "input_value": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Inputs", + "dynamic": false, + "info": "Message to be passed as output.", + "input_types": [ + "Data", + "DataFrame", + "Message" + ], + "list": false, + "list_add_label": "Add More", + "name": "input_value", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Type of sender.", + "name": "sender", + "options": [ + "Machine", + "User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Name of the sender.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "AI" + }, + "session_id": { + "_input_type": "MessageTextInput", + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": 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, + "list_add_label": "Add More", + "name": "should_store_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + }, + "tool_mode": false + }, + "showNode": false, + "type": "ChatOutput" + }, + "dragging": false, + "id": "ChatOutput-yiSgq", + "measured": { + "height": 48, + "width": 192 + }, + "position": { + "x": -729.8594083908358, + "y": 571.9033552511398 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "ChatOutput-K40Du", + "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", + "should_store_message", + "sender", + "sender_name", + "session_id", + "data_template", + "background_color", + "chat_icon", + "text_color", + "clean_data" + ], + "frozen": false, + "icon": "MessagesSquare", + "legacy": false, + "metadata": {}, + "minimized": true, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Output Message", + "group_outputs": false, + "method": "message_response", + "name": "message", + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "background_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Background Color", + "dynamic": false, + "info": "The background color of the icon.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "background_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "chat_icon": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Icon", + "dynamic": false, + "info": "The icon of the message.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "chat_icon", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "clean_data": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Basic Clean Data", + "dynamic": false, + "info": "Whether to clean the data", + "list": false, + "list_add_label": "Add More", + "name": "clean_data", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "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 collections.abc import Generator\nfrom typing import Any\n\nimport orjson\nfrom fastapi.encoders import jsonable_encoder\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.helpers.data import safe_convert\nfrom langflow.inputs.inputs import BoolInput, DropdownInput, HandleInput, MessageTextInput\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Source\nfrom langflow.template.field.base import Output\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_AI,\n MESSAGE_SENDER_USER,\n)\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n minimized = True\n\n inputs = [\n HandleInput(\n name=\"input_value\",\n display_name=\"Inputs\",\n info=\"Message to be passed as output.\",\n input_types=[\"Data\", \"DataFrame\", \"Message\"],\n required=True,\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 MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n BoolInput(\n name=\"clean_data\",\n display_name=\"Basic Clean Data\",\n value=True,\n info=\"Whether to clean the data\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Output Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, id_: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if id_:\n source_dict[\"id\"] = id_\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n # Handle case where source is a ChatOpenAI object\n if hasattr(source, \"model_name\"):\n source_dict[\"source\"] = source.model_name\n elif hasattr(source, \"model\"):\n source_dict[\"source\"] = str(source.model)\n else:\n source_dict[\"source\"] = str(source)\n return Source(**source_dict)\n\n async def message_response(self) -> Message:\n # First convert the input to string if needed\n text = self.convert_to_string()\n\n # Get source properties\n source, icon, display_name, source_id = self.get_properties_from_source_component()\n background_color = self.background_color\n text_color = self.text_color\n if self.chat_icon:\n icon = self.chat_icon\n\n # Create or use existing Message object\n if isinstance(self.input_value, Message):\n message = self.input_value\n # Update message properties\n message.text = text\n else:\n message = Message(text=text)\n\n # Set message properties\n message.sender = self.sender\n message.sender_name = self.sender_name\n message.session_id = self.session_id\n message.flow_id = self.graph.flow_id if hasattr(self, \"graph\") else None\n message.properties.source = self._build_source(source_id, display_name, source)\n message.properties.icon = icon\n message.properties.background_color = background_color\n message.properties.text_color = text_color\n\n # Store message if needed\n if self.session_id and self.should_store_message:\n stored_message = await self.send_message(message)\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n\n def _serialize_data(self, data: Data) -> str:\n \"\"\"Serialize Data object to JSON string.\"\"\"\n # Convert data.data to JSON-serializable format\n serializable_data = jsonable_encoder(data.data)\n # Serialize with orjson, enabling pretty printing with indentation\n json_bytes = orjson.dumps(serializable_data, option=orjson.OPT_INDENT_2)\n # Convert bytes to string and wrap in Markdown code blocks\n return \"```json\\n\" + json_bytes.decode(\"utf-8\") + \"\\n```\"\n\n def _validate_input(self) -> None:\n \"\"\"Validate the input data and raise ValueError if invalid.\"\"\"\n if self.input_value is None:\n msg = \"Input data cannot be None\"\n raise ValueError(msg)\n if isinstance(self.input_value, list) and not all(\n isinstance(item, Message | Data | DataFrame | str) for item in self.input_value\n ):\n invalid_types = [\n type(item).__name__\n for item in self.input_value\n if not isinstance(item, Message | Data | DataFrame | str)\n ]\n msg = f\"Expected Data or DataFrame or Message or str, got {invalid_types}\"\n raise TypeError(msg)\n if not isinstance(\n self.input_value,\n Message | Data | DataFrame | str | list | Generator | type(None),\n ):\n type_name = type(self.input_value).__name__\n msg = f\"Expected Data or DataFrame or Message or str, Generator or None, got {type_name}\"\n raise TypeError(msg)\n\n def convert_to_string(self) -> str | Generator[Any, None, None]:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n self._validate_input()\n if isinstance(self.input_value, list):\n return \"\\n\".join([safe_convert(item, clean_data=self.clean_data) for item in self.input_value])\n if isinstance(self.input_value, Generator):\n return self.input_value\n return safe_convert(self.input_value)\n" + }, + "data_template": { + "_input_type": "MessageTextInput", + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "data_template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{text}" + }, + "input_value": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Inputs", + "dynamic": false, + "info": "Message to be passed as output.", + "input_types": [ + "Data", + "DataFrame", + "Message" + ], + "list": false, + "list_add_label": "Add More", + "name": "input_value", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Type of sender.", + "name": "sender", + "options": [ + "Machine", + "User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Name of the sender.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "sender_name", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "AI" + }, + "session_id": { + "_input_type": "MessageTextInput", + "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, + "list_add_label": "Add More", + "load_from_db": false, + "name": "session_id", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": 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, + "list_add_label": "Add More", + "name": "should_store_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "text_color": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Text Color", + "dynamic": false, + "info": "The text color of the name", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text_color", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + }, + "tool_mode": false + }, + "showNode": false, + "type": "ChatOutput" + }, + "dragging": false, + "id": "ChatOutput-K40Du", + "measured": { + "height": 48, + "width": 192 + }, + "position": { + "x": -155.57589738233636, + "y": 1127.8168564025477 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "note-S1pun", + "node": { + "description": "### 💡 Add your OpenAI API key here 👇", + "display_name": "", + "documentation": "", + "template": { + "backgroundColor": "transparent" + } + }, + "type": "note" + }, + "dragging": false, + "height": 326, + "id": "note-S1pun", + "measured": { + "height": 326, + "width": 353 + }, + "position": { + "x": -1279.8293449946098, + "y": 253.39163784131915 + }, + "resizing": false, + "selected": false, + "type": "noteNode", + "width": 353 + }, + { + "data": { + "id": "note-ZAMRC", + "node": { + "description": "### 💡 Add your OpenAI API key here 👇", + "display_name": "", + "documentation": "", + "template": { + "backgroundColor": "transparent" + } + }, + "type": "note" + }, + "dragging": false, + "id": "note-ZAMRC", + "measured": { + "height": 324, + "width": 324 + }, + "position": { + "x": -619.6308693439651, + "y": 957.9933268279298 + }, + "selected": false, + "type": "noteNode" + }, + { + "data": { + "id": "note-KreFC", + "node": { + "description": "### 💡 Add your OpenAI API key here 👇", + "display_name": "", + "documentation": "", + "template": { + "backgroundColor": "transparent" + } + }, + "type": "note" + }, + "dragging": false, + "id": "note-KreFC", + "measured": { + "height": 324, + "width": 324 + }, + "position": { + "x": -1309.8036340068625, + "y": 925.6888138443481 + }, + "selected": false, + "type": "noteNode" + }, + { + "data": { + "id": "LanguageModelComponent-qFXT1", + "node": { + "base_classes": [ + "LanguageModel", + "Message" + ], + "beta": false, + "category": "models", + "conditional_paths": [], + "custom_fields": {}, + "description": "Runs a language model given a specified provider. ", + "display_name": "Language Model", + "documentation": "", + "edited": false, + "field_order": [ + "provider", + "model_name", + "api_key", + "input_value", + "system_message", + "stream", + "temperature" + ], + "frozen": false, + "icon": "brain-circuit", + "key": "LanguageModelComponent", + "legacy": false, + "metadata": { + "keywords": [ + "model", + "llm", + "language model", + "large language model" + ] + }, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Model Response", + "group_outputs": false, + "method": "text_response", + "name": "text_output", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Language Model", + "group_outputs": false, + "method": "build_model", + "name": "model_output", + "options": null, + "required_inputs": null, + "selected": "LanguageModel", + "tool_mode": true, + "types": [ + "LanguageModel" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "priority": 0, + "score": 0.28173906304863156, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "Model Provider API key", + "input_types": [], + "load_from_db": true, + "name": "api_key", + "password": true, + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "OPENAI_API_KEY" + }, + "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 typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\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.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"GoogleGenerativeAI\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" + }, + "input_value": { + "_input_type": "MessageInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "The input text to send to the model", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "Select the model to use", + "name": "model_name", + "options": [ + "gpt-4o-mini", + "gpt-4o", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.5-preview", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-4", + "gpt-3.5-turbo" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "gpt-4o-mini" + }, + "provider": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", + "dynamic": false, + "info": "Select the model provider", + "name": "provider", + "options": [ + "OpenAI", + "Anthropic", + "Google" + ], + "options_metadata": [ + { + "icon": "OpenAI" + }, + { + "icon": "Anthropic" + }, + { + "icon": "Google" + } + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "OpenAI" + }, + "stream": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "info": "Whether to stream the response", + "list": false, + "list_add_label": "Add More", + "name": "stream", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "system_message": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "display_name": "System Message", + "dynamic": false, + "info": "A system message that helps set the behavior of the assistant", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "system_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "temperature": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "Temperature", + "dynamic": false, + "info": "Controls randomness in responses", + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "temperature", + "placeholder": "", + "range_spec": { + "max": 1, + "min": 0, + "step": 0.01, + "step_type": "float" + }, + "required": false, + "show": true, + "slider_buttons": false, + "slider_buttons_options": [], + "slider_input": false, + "title_case": false, + "tool_mode": false, + "type": "slider", + "value": 0.1 + } + }, + "tool_mode": false + }, + "selected_output": "text_output", + "showNode": true, + "type": "LanguageModelComponent" + }, + "dragging": false, + "id": "LanguageModelComponent-qFXT1", + "measured": { + "height": 450, + "width": 320 + }, + "position": { + "x": -1319.462305610481, + "y": 976.7543982544241 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "LanguageModelComponent-Wp3pC", + "node": { + "base_classes": [ + "LanguageModel", + "Message" + ], + "beta": false, + "category": "models", + "conditional_paths": [], + "custom_fields": {}, + "description": "Runs a language model given a specified provider. ", + "display_name": "Language Model", + "documentation": "", + "edited": false, + "field_order": [ + "provider", + "model_name", + "api_key", + "input_value", + "system_message", + "stream", + "temperature" + ], + "frozen": false, + "icon": "brain-circuit", + "key": "LanguageModelComponent", + "legacy": false, + "metadata": { + "keywords": [ + "model", + "llm", + "language model", + "large language model" + ] + }, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Model Response", + "group_outputs": false, + "method": "text_response", + "name": "text_output", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Language Model", + "group_outputs": false, + "method": "build_model", + "name": "model_output", + "options": null, + "required_inputs": null, + "selected": "LanguageModel", + "tool_mode": true, + "types": [ + "LanguageModel" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "priority": 0, + "score": 0.28173906304863156, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "Model Provider API key", + "input_types": [], + "load_from_db": true, + "name": "api_key", + "password": true, + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "OPENAI_API_KEY" + }, + "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 typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\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.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"GoogleGenerativeAI\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" + }, + "input_value": { + "_input_type": "MessageInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "The input text to send to the model", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "Select the model to use", + "name": "model_name", + "options": [ + "gpt-4o-mini", + "gpt-4o", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.5-preview", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-4", + "gpt-3.5-turbo" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "gpt-4o-mini" + }, + "provider": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", + "dynamic": false, + "info": "Select the model provider", + "name": "provider", + "options": [ + "OpenAI", + "Anthropic", + "Google" + ], + "options_metadata": [ + { + "icon": "OpenAI" + }, + { + "icon": "Anthropic" + }, + { + "icon": "Google" + } + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "OpenAI" + }, + "stream": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "info": "Whether to stream the response", + "list": false, + "list_add_label": "Add More", + "name": "stream", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "system_message": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "display_name": "System Message", + "dynamic": false, + "info": "A system message that helps set the behavior of the assistant", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "system_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "temperature": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "Temperature", + "dynamic": false, + "info": "Controls randomness in responses", + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "temperature", + "placeholder": "", + "range_spec": { + "max": 1, + "min": 0, + "step": 0.01, + "step_type": "float" + }, + "required": false, + "show": true, + "slider_buttons": false, + "slider_buttons_options": [], + "slider_input": false, + "title_case": false, + "tool_mode": false, + "type": "slider", + "value": 0.1 + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "LanguageModelComponent" + }, + "dragging": false, + "id": "LanguageModelComponent-Wp3pC", + "measured": { + "height": 450, + "width": 320 + }, + "position": { + "x": -610.7115573962577, + "y": 1009.6862512017519 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "LanguageModelComponent-gYAmH", + "node": { + "base_classes": [ + "LanguageModel", + "Message" + ], + "beta": false, + "category": "models", + "conditional_paths": [], + "custom_fields": {}, + "description": "Runs a language model given a specified provider. ", + "display_name": "Language Model", + "documentation": "", + "edited": false, + "field_order": [ + "provider", + "model_name", + "api_key", + "input_value", + "system_message", + "stream", + "temperature" + ], + "frozen": false, + "icon": "brain-circuit", + "key": "LanguageModelComponent", + "legacy": false, + "metadata": { + "keywords": [ + "model", + "llm", + "language model", + "large language model" + ] + }, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Model Response", + "group_outputs": false, + "method": "text_response", + "name": "text_output", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Language Model", + "group_outputs": false, + "method": "build_model", + "name": "model_output", + "options": null, + "required_inputs": null, + "selected": "LanguageModel", + "tool_mode": true, + "types": [ + "LanguageModel" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "priority": 0, + "score": 0.28173906304863156, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "Model Provider API key", + "input_types": [], + "load_from_db": true, + "name": "api_key", + "password": true, + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "OPENAI_API_KEY" + }, + "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 typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\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.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"GoogleGenerativeAI\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" + }, + "input_value": { + "_input_type": "MessageInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "The input text to send to the model", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "Select the model to use", + "name": "model_name", + "options": [ + "gpt-4o-mini", + "gpt-4o", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.5-preview", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-4", + "gpt-3.5-turbo" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "gpt-4o-mini" + }, + "provider": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", + "dynamic": false, + "info": "Select the model provider", + "name": "provider", + "options": [ + "OpenAI", + "Anthropic", + "Google" + ], + "options_metadata": [ + { + "icon": "OpenAI" + }, + { + "icon": "Anthropic" + }, + { + "icon": "Google" + } + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "OpenAI" + }, + "stream": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "info": "Whether to stream the response", + "list": false, + "list_add_label": "Add More", + "name": "stream", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "system_message": { + "_input_type": "MultilineInput", + "advanced": false, + "copy_field": false, + "display_name": "System Message", + "dynamic": false, + "info": "A system message that helps set the behavior of the assistant", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "system_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "temperature": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "Temperature", + "dynamic": false, + "info": "Controls randomness in responses", + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "temperature", + "placeholder": "", + "range_spec": { + "max": 1, + "min": 0, + "step": 0.01, + "step_type": "float" + }, + "required": false, + "show": true, + "slider_buttons": false, + "slider_buttons_options": [], + "slider_input": false, + "title_case": false, + "tool_mode": false, + "type": "slider", + "value": 0.1 + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "LanguageModelComponent" + }, + "dragging": false, + "id": "LanguageModelComponent-gYAmH", + "measured": { + "height": 532, + "width": 320 + }, + "position": { + "x": -1273.6440754228947, + "y": 303.799142374253 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "File-oAEuF", "node": { "base_classes": [ - "DataFrame", "Message" ], "beta": false, @@ -257,20 +2298,6 @@ "minimized": false, "output_types": [], "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Loaded Files", - "group_outputs": false, - "method": "load_files", - "name": "dataframe", - "selected": null, - "tool_mode": true, - "types": [ - "DataFrame" - ], - "value": "__UNDEFINED__" - }, { "allows_loop": false, "cache": true, @@ -305,7 +2332,7 @@ "show": true, "title_case": false, "type": "code", - "value": "from langflow.base.data.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, IntInput\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n inputs = [\n *BaseFileComponent._base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n *BaseFileComponent._base_outputs,\n ]\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" + "value": "from copy import deepcopy\nfrom typing import Any\n\nfrom langflow.base.data.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, FileInput, IntInput, Output\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n _base_inputs = deepcopy(BaseFileComponent._base_inputs)\n\n for input_item in _base_inputs:\n if isinstance(input_item, FileInput) and input_item.name == \"path\":\n input_item.real_time_refresh = True\n break\n\n inputs = [\n *_base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Raw Content\", name=\"message\", method=\"load_files_message\"),\n ]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the number of files processed.\"\"\"\n if field_name == \"path\":\n # Add outputs based on the number of files in the path\n if len(field_value) == 0:\n return frontend_node\n\n frontend_node[\"outputs\"] = []\n\n if len(field_value) == 1:\n # We need to check if the file is structured content\n file_path = frontend_node[\"template\"][\"path\"][\"file_path\"][0]\n if file_path.endswith((\".csv\", \".xlsx\", \".parquet\")):\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Structured Content\", name=\"dataframe\", method=\"load_files_structured\"),\n )\n elif file_path.endswith(\".json\"):\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Structured Content\", name=\"json\", method=\"load_files_json\"),\n )\n\n # All files get the raw content and path outputs\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Raw Content\", name=\"message\", method=\"load_files_message\"),\n )\n frontend_node[\"outputs\"].append(\n Output(display_name=\"File Path\", name=\"path\", method=\"load_files_path\"),\n )\n else:\n # For multiple files, we only show the files output\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Files\", name=\"dataframe\", method=\"load_files\"),\n )\n\n return frontend_node\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" }, "concurrency_multithreading": { "_input_type": "IntInput", @@ -436,6 +2463,7 @@ "list_add_label": "Add More", "name": "path", "placeholder": "", + "real_time_refresh": true, "required": false, "show": true, "temp_file": false, @@ -502,2050 +2530,32 @@ }, "tool_mode": false }, - "selected_output": "message", "showNode": true, "type": "File" }, "dragging": false, - "id": "File-fosKE", + "id": "File-oAEuF", "measured": { - "height": 230, + "height": 229, "width": 320 }, "position": { - "x": -2114.5031608334, - "y": 681.3411437663555 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "Prompt-roFWL", - "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": { - "template": [ - "summary" - ] - }, - "description": "Create a prompt template with dynamic variables.", - "display_name": "Prompt", - "documentation": "", - "edited": false, - "field_order": [ - "template", - "tool_placeholder" - ], - "frozen": false, - "icon": "braces", - "legacy": false, - "metadata": {}, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Prompt", - "group_outputs": false, - "method": "build_prompt", - "name": "prompt", - "selected": "Message", - "tool_mode": true, - "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.custom_component.component import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, 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 = \"braces\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**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 async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(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" - }, - "summary": { - "advanced": false, - "display_name": "summary", - "dynamic": false, - "field_type": "str", - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "summary", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, - "template": { - "_input_type": "PromptInput", - "advanced": false, - "display_name": "Template", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "template", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "type": "prompt", - "value": "Based on the following summary of discussions in the Langflow community, generate a well-structured and actionable technical recommendation for the development team. The recommendation should be clear, precise, and directly applicable to improve Langflow.\n\nGuidelines for the Action Item:\nBe Specific: Clearly define the issue, feature request, or improvement.\nProvide Context: Briefly justify why this action is necessary based on user discussions.\nSuggest a Next Step: Outline what the technical team should do to address the issue or enhance the platform.\nPrioritize if Relevant: If multiple issues are discussed, focus on the most urgent or impactful one.\n\nSummary:\n{summary}\n\nProvide a concise, structured, and technically sound action item that the Langflow team can implement.\n\nreturn:\n- {summary}\n- [action item]\n- [sentiment]\n- [start_date]\n- [end_date]\n\nyou need to return the data in json format\n\nReturn Format:\nEnsure that both \"summary\" and \"action_item\" are single string values, not lists and without nested objects or additional keys.\n\n" - }, - "tool_placeholder": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Tool Placeholder", - "dynamic": false, - "info": "A placeholder input for tool mode.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "tool_placeholder", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "showNode": true, - "type": "Prompt" - }, - "dragging": false, - "id": "Prompt-roFWL", - "measured": { - "height": 367, - "width": 320 - }, - "position": { - "x": -973.805557188769, - "y": 1081.112870159395 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "Prompt-3IzFz", - "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": { - "template": [ - "text" - ] - }, - "description": "Create a prompt template with dynamic variables.", - "display_name": "Prompt", - "documentation": "", - "edited": false, - "field_order": [ - "template", - "tool_placeholder" - ], - "frozen": false, - "icon": "braces", - "legacy": false, - "metadata": {}, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Prompt", - "group_outputs": false, - "method": "build_prompt", - "name": "prompt", - "selected": "Message", - "tool_mode": true, - "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.custom_component.component import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, 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 = \"braces\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**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 async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(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" - }, - "template": { - "_input_type": "PromptInput", - "advanced": false, - "display_name": "Template", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "template", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "type": "prompt", - "value": "You are analyzing messages from an online community or discussion platform. Your task is to generate a well-structured, concise, and informative summary of the key discussions that took place in a specific channel or group during a given time period. This summary will provide actionable insights for community managers, stakeholders, or decision-makers. \n\nYou will receive a list of messages containing **message_id, created_at, and message_content**. \n\n### **Task 1: Summarization** \nFrom the **message_content**, generate a cohesive, flowing summary in paragraph form, avoiding bullet points or fragmented structures. The summary should be written in clear, professional English, ensuring a natural reading experience. \n\nFocus on the following key discussion areas (if applicable): \n- **Feature Requests & Enhancements**: Suggestions for new features, improvements, or emerging use cases. \n- **Issues & Problems**: Reports of challenges, technical difficulties, or troubleshooting discussions. \n- **Community Feedback**: Reactions to updates, usability concerns, and overall user sentiment. \n- **General Topics**: Broader discussions relevant to the platform, industry trends, or key interests of the community. \n\nMessages may be written in different languages, depending on the community. However, the summary must always be in English and should be structured as a natural, flowing narrative rather than a list of points. \n\n### **Task 2: Sentiment Analysis** \nAnalyze the overall **sentiment** of the summary based on user feedback, discussions, and general tone. The sentiment should reflect how the community perceives recent updates, issues, or the overall experience. \n\n#### **Classification Criteria:** \n- **Positive**: The discussion contains mostly favorable feedback, enthusiasm about new topics, successful problem resolutions, or constructive engagement. \n- **Neutral**: The discussion is balanced, with a mix of praise, constructive criticism, and open-ended conversations without strong emotions. \n- **Negative**: The discussion is dominated by frustrations, unresolved issues, strong criticisms, or concerns about the platform's direction. \n\n### **Input:** \nHere is the list of messages: \n{text} \n\n### **Return Format:** \n- **summary**: A cohesive, well-structured summary written in continuous prose, without bullet points. \n- **sentiment**: One of the sentiment categories (**Positive, Neutral, or Negative**). \n- **start_date**: The earliest message timestamp in the format **'yyyy-mm-dd'**. \n- **end_date**: The most recent message timestamp in the format **'yyyy-mm-dd'**. \n" - }, - "text": { - "advanced": false, - "display_name": "text", - "dynamic": false, - "field_type": "str", - "fileTypes": [], - "file_path": "", - "info": "", - "input_types": [ - "Message" - ], - "list": false, - "load_from_db": false, - "multiline": true, - "name": "text", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, - "tool_placeholder": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Tool Placeholder", - "dynamic": false, - "info": "A placeholder input for tool mode.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "tool_placeholder", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "showNode": true, - "type": "Prompt" - }, - "dragging": false, - "id": "Prompt-3IzFz", - "measured": { - "height": 367, - "width": 320 - }, - "position": { - "x": -1691.3505617322087, - "y": 1010.953782441708 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "Prompt-ryDcq", - "node": { - "base_classes": [ - "Message" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": { - "template": [] - }, - "description": "Create a prompt template with dynamic variables.", - "display_name": "Prompt", - "documentation": "", - "edited": false, - "field_order": [ - "template", - "tool_placeholder" - ], - "frozen": false, - "icon": "braces", - "legacy": false, - "metadata": {}, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Prompt", - "group_outputs": false, - "method": "build_prompt", - "name": "prompt", - "selected": "Message", - "tool_mode": true, - "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.custom_component.component import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import MessageTextInput, 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 = \"braces\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n MessageTextInput(\n name=\"tool_placeholder\",\n display_name=\"Tool Placeholder\",\n tool_mode=True,\n advanced=True,\n info=\"A placeholder input for tool mode.\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Prompt\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(self) -> Message:\n prompt = Message.from_template(**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 async def update_frontend_node(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = await super().update_frontend_node(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" - }, - "template": { - "_input_type": "PromptInput", - "advanced": false, - "display_name": "Template", - "dynamic": false, - "info": "", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "template", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "type": "prompt", - "value": "You are an NLP expert assisting the technical team in analyzing user messages. The analysis will be used by the team to make decisions more effectively.\n\n**Task 1:**\n#### **1. Message Classification **\nClassify the message into a **single category** that best describes its purpose. Choose the category from the predefined list below. This classification will help the technical team identify specific themes and make decisions faster.\n\n- **Release Notes**: A message announcing updates, new features, integrations, important information about new versions for users.\n- **Feature Request**: A suggestion or idea for a new feature or enhancement.\n- **Bug Report**: A message reporting a clear technical failure, malfunction, or unexpected behavior in Langflow. The user explicitly describes an issue where: A feature does not work as expected; The system crashes, freezes, or behaves unpredictably.\n- **User Question**: Any question or uncertainty regarding features, functionality, implementation, or general use. This includes both technical questions and general doubts, regardless of complexity or specificity.\n- **Complaint**: A message expressing dissatisfaction or frustration with a feature or issue.\n- **Positive Feedback**: Messages expressing gratitude alongside useful feedback, such as confirming that an issue has been resolved or a feature works as intended.\n\n\n##### **Important Instructions for Classification:**\n- Ensure each message is classified into **only one category** based on its primary intent or purpose. If multiple intents are detected, select the most relevant category that reflects the user's main goal.\n- Do not create new categories or use freeform text for classification. Always use one of the predefined categories exactly as they appear in the list above.\n\n**task 2**\nAnalyze the sentiment of the message and classify it into one of the following categories:\n- **Positive**: The message conveys positivity, satisfaction, gratitude, or encouragement.\n- **Neutral**: The message is factual, descriptive, or lacks any emotional tone.\n- **Negative**: The message conveys frustration, dissatisfaction, or criticism.\n\n\nreturn:\n- [message_id]\n- [message_category] \n- [message_sentiment]\n\nYou need to output the results in JSON format.\n" - }, - "tool_placeholder": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Tool Placeholder", - "dynamic": false, - "info": "A placeholder input for tool mode.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "tool_placeholder", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": true, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "showNode": true, - "type": "Prompt" - }, - "dragging": false, - "id": "Prompt-ryDcq", - "measured": { - "height": 284, - "width": 320 - }, - "position": { - "x": -1668.597994833308, - "y": 651.6625805464743 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "note-gFBr6", - "node": { - "description": "# Sentiment Analysis Flow \n\nThis flow processes text data, analyzes sentiment, and provides structured insights. \n\n## Prerequisite\n\n* [OpenAI API key](https://platform.openai.com/docs/)\n\n## Quickstart\n\n1. Add your [OpenAI API key](https://platform.openai.com/docs/) to the OpenAI model components.\n2. In the **File Component**, load text data in `.txt`, `.csv`, or `.json` formats.\n3. Open the **Playground** to see the analysis and recommendation the flow constructs.\n\n## How It Works \n\n1. The **Data to Message** component converts raw data into structured messages. \n\n2. The **NLP expert** prompt provides sentiment analysis, while the **Analyzing messages** prompt summarizes messages from a discussion board.\n\n3. The final **Prompt Component** creates a structured recommendation prompt to ensure that the AI model receives well-formatted input. \n\n4. The **OpenAI Model Component** processes the text and classifies the sentiment as **Positive, Neutral, or Negative**. \n\n", - "display_name": "", - "documentation": "", - "template": {} - }, - "type": "note" - }, - "dragging": false, - "height": 574, - "id": "note-gFBr6", - "measured": { - "height": 574, - "width": 413 - }, - "position": { - "x": -2891.3528304066394, - "y": 384.370168260918 - }, - "resizing": false, - "selected": false, - "type": "noteNode", - "width": 412 - }, - { - "data": { - "id": "ChatOutput-0vZ51", - "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", - "should_store_message", - "sender", - "sender_name", - "session_id", - "data_template", - "background_color", - "chat_icon", - "text_color", - "clean_data" - ], - "frozen": false, - "icon": "MessagesSquare", - "legacy": false, - "metadata": {}, - "minimized": true, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Output Message", - "group_outputs": false, - "method": "message_response", - "name": "message", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "background_color": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Background Color", - "dynamic": false, - "info": "The background color of the icon.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "background_color", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "chat_icon": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Icon", - "dynamic": false, - "info": "The icon of the message.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "chat_icon", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "clean_data": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Basic Clean Data", - "dynamic": false, - "info": "Whether to clean the data", - "list": false, - "list_add_label": "Add More", - "name": "clean_data", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "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 collections.abc import Generator\nfrom typing import Any\n\nimport orjson\nfrom fastapi.encoders import jsonable_encoder\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.helpers.data import safe_convert\nfrom langflow.inputs.inputs import BoolInput, DropdownInput, HandleInput, MessageTextInput\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Source\nfrom langflow.template.field.base import Output\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_AI,\n MESSAGE_SENDER_USER,\n)\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n minimized = True\n\n inputs = [\n HandleInput(\n name=\"input_value\",\n display_name=\"Inputs\",\n info=\"Message to be passed as output.\",\n input_types=[\"Data\", \"DataFrame\", \"Message\"],\n required=True,\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 MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n BoolInput(\n name=\"clean_data\",\n display_name=\"Basic Clean Data\",\n value=True,\n info=\"Whether to clean the data\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Output Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, id_: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if id_:\n source_dict[\"id\"] = id_\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n # Handle case where source is a ChatOpenAI object\n if hasattr(source, \"model_name\"):\n source_dict[\"source\"] = source.model_name\n elif hasattr(source, \"model\"):\n source_dict[\"source\"] = str(source.model)\n else:\n source_dict[\"source\"] = str(source)\n return Source(**source_dict)\n\n async def message_response(self) -> Message:\n # First convert the input to string if needed\n text = self.convert_to_string()\n\n # Get source properties\n source, icon, display_name, source_id = self.get_properties_from_source_component()\n background_color = self.background_color\n text_color = self.text_color\n if self.chat_icon:\n icon = self.chat_icon\n\n # Create or use existing Message object\n if isinstance(self.input_value, Message):\n message = self.input_value\n # Update message properties\n message.text = text\n else:\n message = Message(text=text)\n\n # Set message properties\n message.sender = self.sender\n message.sender_name = self.sender_name\n message.session_id = self.session_id\n message.flow_id = self.graph.flow_id if hasattr(self, \"graph\") else None\n message.properties.source = self._build_source(source_id, display_name, source)\n message.properties.icon = icon\n message.properties.background_color = background_color\n message.properties.text_color = text_color\n\n # Store message if needed\n if self.session_id and self.should_store_message:\n stored_message = await self.send_message(message)\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n\n def _serialize_data(self, data: Data) -> str:\n \"\"\"Serialize Data object to JSON string.\"\"\"\n # Convert data.data to JSON-serializable format\n serializable_data = jsonable_encoder(data.data)\n # Serialize with orjson, enabling pretty printing with indentation\n json_bytes = orjson.dumps(serializable_data, option=orjson.OPT_INDENT_2)\n # Convert bytes to string and wrap in Markdown code blocks\n return \"```json\\n\" + json_bytes.decode(\"utf-8\") + \"\\n```\"\n\n def _validate_input(self) -> None:\n \"\"\"Validate the input data and raise ValueError if invalid.\"\"\"\n if self.input_value is None:\n msg = \"Input data cannot be None\"\n raise ValueError(msg)\n if isinstance(self.input_value, list) and not all(\n isinstance(item, Message | Data | DataFrame | str) for item in self.input_value\n ):\n invalid_types = [\n type(item).__name__\n for item in self.input_value\n if not isinstance(item, Message | Data | DataFrame | str)\n ]\n msg = f\"Expected Data or DataFrame or Message or str, got {invalid_types}\"\n raise TypeError(msg)\n if not isinstance(\n self.input_value,\n Message | Data | DataFrame | str | list | Generator | type(None),\n ):\n type_name = type(self.input_value).__name__\n msg = f\"Expected Data or DataFrame or Message or str, Generator or None, got {type_name}\"\n raise TypeError(msg)\n\n def convert_to_string(self) -> str | Generator[Any, None, None]:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n self._validate_input()\n if isinstance(self.input_value, list):\n return \"\\n\".join([safe_convert(item, clean_data=self.clean_data) for item in self.input_value])\n if isinstance(self.input_value, Generator):\n return self.input_value\n return safe_convert(self.input_value)\n" - }, - "data_template": { - "_input_type": "MessageTextInput", - "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, - "list_add_label": "Add More", - "load_from_db": false, - "name": "data_template", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "{text}" - }, - "input_value": { - "_input_type": "HandleInput", - "advanced": false, - "display_name": "Inputs", - "dynamic": false, - "info": "Message to be passed as output.", - "input_types": [ - "Data", - "DataFrame", - "Message" - ], - "list": false, - "list_add_label": "Add More", - "name": "input_value", - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "other", - "value": "" - }, - "sender": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Sender Type", - "dynamic": false, - "info": "Type of sender.", - "name": "sender", - "options": [ - "Machine", - "User" - ], - "options_metadata": [], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "Machine" - }, - "sender_name": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Sender Name", - "dynamic": false, - "info": "Name of the sender.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "sender_name", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "AI" - }, - "session_id": { - "_input_type": "MessageTextInput", - "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, - "list_add_label": "Add More", - "load_from_db": false, - "name": "session_id", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": 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, - "list_add_label": "Add More", - "name": "should_store_message", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "text_color": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Text Color", - "dynamic": false, - "info": "The text color of the name", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "text_color", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "showNode": false, - "type": "ChatOutput" - }, - "dragging": false, - "id": "ChatOutput-0vZ51", - "measured": { - "height": 48, - "width": 192 - }, - "position": { - "x": -729.8594083908358, - "y": 571.9033552511398 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "ChatOutput-C9zZD", - "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", - "should_store_message", - "sender", - "sender_name", - "session_id", - "data_template", - "background_color", - "chat_icon", - "text_color", - "clean_data" - ], - "frozen": false, - "icon": "MessagesSquare", - "legacy": false, - "metadata": {}, - "minimized": true, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Output Message", - "group_outputs": false, - "method": "message_response", - "name": "message", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "Component", - "background_color": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Background Color", - "dynamic": false, - "info": "The background color of the icon.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "background_color", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "chat_icon": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Icon", - "dynamic": false, - "info": "The icon of the message.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "chat_icon", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "clean_data": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Basic Clean Data", - "dynamic": false, - "info": "Whether to clean the data", - "list": false, - "list_add_label": "Add More", - "name": "clean_data", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "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 collections.abc import Generator\nfrom typing import Any\n\nimport orjson\nfrom fastapi.encoders import jsonable_encoder\n\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.helpers.data import safe_convert\nfrom langflow.inputs.inputs import BoolInput, DropdownInput, HandleInput, MessageTextInput\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Source\nfrom langflow.template.field.base import Output\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_AI,\n MESSAGE_SENDER_USER,\n)\n\n\nclass ChatOutput(ChatComponent):\n display_name = \"Chat Output\"\n description = \"Display a chat message in the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatOutput\"\n minimized = True\n\n inputs = [\n HandleInput(\n name=\"input_value\",\n display_name=\"Inputs\",\n info=\"Message to be passed as output.\",\n input_types=[\"Data\", \"DataFrame\", \"Message\"],\n required=True,\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 MessageTextInput(\n name=\"background_color\",\n display_name=\"Background Color\",\n info=\"The background color of the icon.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"chat_icon\",\n display_name=\"Icon\",\n info=\"The icon of the message.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"text_color\",\n display_name=\"Text Color\",\n info=\"The text color of the name\",\n advanced=True,\n ),\n BoolInput(\n name=\"clean_data\",\n display_name=\"Basic Clean Data\",\n value=True,\n info=\"Whether to clean the data\",\n advanced=True,\n ),\n ]\n outputs = [\n Output(\n display_name=\"Output Message\",\n name=\"message\",\n method=\"message_response\",\n ),\n ]\n\n def _build_source(self, id_: str | None, display_name: str | None, source: str | None) -> Source:\n source_dict = {}\n if id_:\n source_dict[\"id\"] = id_\n if display_name:\n source_dict[\"display_name\"] = display_name\n if source:\n # Handle case where source is a ChatOpenAI object\n if hasattr(source, \"model_name\"):\n source_dict[\"source\"] = source.model_name\n elif hasattr(source, \"model\"):\n source_dict[\"source\"] = str(source.model)\n else:\n source_dict[\"source\"] = str(source)\n return Source(**source_dict)\n\n async def message_response(self) -> Message:\n # First convert the input to string if needed\n text = self.convert_to_string()\n\n # Get source properties\n source, icon, display_name, source_id = self.get_properties_from_source_component()\n background_color = self.background_color\n text_color = self.text_color\n if self.chat_icon:\n icon = self.chat_icon\n\n # Create or use existing Message object\n if isinstance(self.input_value, Message):\n message = self.input_value\n # Update message properties\n message.text = text\n else:\n message = Message(text=text)\n\n # Set message properties\n message.sender = self.sender\n message.sender_name = self.sender_name\n message.session_id = self.session_id\n message.flow_id = self.graph.flow_id if hasattr(self, \"graph\") else None\n message.properties.source = self._build_source(source_id, display_name, source)\n message.properties.icon = icon\n message.properties.background_color = background_color\n message.properties.text_color = text_color\n\n # Store message if needed\n if self.session_id and self.should_store_message:\n stored_message = await self.send_message(message)\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n\n def _serialize_data(self, data: Data) -> str:\n \"\"\"Serialize Data object to JSON string.\"\"\"\n # Convert data.data to JSON-serializable format\n serializable_data = jsonable_encoder(data.data)\n # Serialize with orjson, enabling pretty printing with indentation\n json_bytes = orjson.dumps(serializable_data, option=orjson.OPT_INDENT_2)\n # Convert bytes to string and wrap in Markdown code blocks\n return \"```json\\n\" + json_bytes.decode(\"utf-8\") + \"\\n```\"\n\n def _validate_input(self) -> None:\n \"\"\"Validate the input data and raise ValueError if invalid.\"\"\"\n if self.input_value is None:\n msg = \"Input data cannot be None\"\n raise ValueError(msg)\n if isinstance(self.input_value, list) and not all(\n isinstance(item, Message | Data | DataFrame | str) for item in self.input_value\n ):\n invalid_types = [\n type(item).__name__\n for item in self.input_value\n if not isinstance(item, Message | Data | DataFrame | str)\n ]\n msg = f\"Expected Data or DataFrame or Message or str, got {invalid_types}\"\n raise TypeError(msg)\n if not isinstance(\n self.input_value,\n Message | Data | DataFrame | str | list | Generator | type(None),\n ):\n type_name = type(self.input_value).__name__\n msg = f\"Expected Data or DataFrame or Message or str, Generator or None, got {type_name}\"\n raise TypeError(msg)\n\n def convert_to_string(self) -> str | Generator[Any, None, None]:\n \"\"\"Convert input data to string with proper error handling.\"\"\"\n self._validate_input()\n if isinstance(self.input_value, list):\n return \"\\n\".join([safe_convert(item, clean_data=self.clean_data) for item in self.input_value])\n if isinstance(self.input_value, Generator):\n return self.input_value\n return safe_convert(self.input_value)\n" - }, - "data_template": { - "_input_type": "MessageTextInput", - "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, - "list_add_label": "Add More", - "load_from_db": false, - "name": "data_template", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "{text}" - }, - "input_value": { - "_input_type": "HandleInput", - "advanced": false, - "display_name": "Inputs", - "dynamic": false, - "info": "Message to be passed as output.", - "input_types": [ - "Data", - "DataFrame", - "Message" - ], - "list": false, - "list_add_label": "Add More", - "name": "input_value", - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "other", - "value": "" - }, - "sender": { - "_input_type": "DropdownInput", - "advanced": true, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Sender Type", - "dynamic": false, - "info": "Type of sender.", - "name": "sender", - "options": [ - "Machine", - "User" - ], - "options_metadata": [], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "Machine" - }, - "sender_name": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Sender Name", - "dynamic": false, - "info": "Name of the sender.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "sender_name", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "AI" - }, - "session_id": { - "_input_type": "MessageTextInput", - "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, - "list_add_label": "Add More", - "load_from_db": false, - "name": "session_id", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": 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, - "list_add_label": "Add More", - "name": "should_store_message", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "text_color": { - "_input_type": "MessageTextInput", - "advanced": true, - "display_name": "Text Color", - "dynamic": false, - "info": "The text color of the name", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "text_color", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - } - }, - "tool_mode": false - }, - "showNode": false, - "type": "ChatOutput" - }, - "dragging": false, - "id": "ChatOutput-C9zZD", - "measured": { - "height": 48, - "width": 192 - }, - "position": { - "x": -155.57589738233636, - "y": 1127.8168564025477 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "note-DnDfL", - "node": { - "description": "### 💡 Add your OpenAI API key here 👇", - "display_name": "", - "documentation": "", - "template": { - "backgroundColor": "transparent" - } - }, - "type": "note" - }, - "dragging": false, - "height": 326, - "id": "note-DnDfL", - "measured": { - "height": 326, - "width": 352 - }, - "position": { - "x": -1296.8149861584823, - "y": 204.32200781235414 - }, - "resizing": false, - "selected": false, - "type": "noteNode", - "width": 353 - }, - { - "data": { - "id": "note-k5kCc", - "node": { - "description": "### 💡 Add your OpenAI API key here 👇", - "display_name": "", - "documentation": "", - "template": { - "backgroundColor": "transparent" - } - }, - "type": "note" - }, - "dragging": false, - "id": "note-k5kCc", - "measured": { - "height": 324, - "width": 324 - }, - "position": { - "x": -619.6308693439651, - "y": 957.9933268279298 - }, - "selected": false, - "type": "noteNode" - }, - { - "data": { - "id": "note-FFvLx", - "node": { - "description": "### 💡 Add your OpenAI API key here 👇", - "display_name": "", - "documentation": "", - "template": { - "backgroundColor": "transparent" - } - }, - "type": "note" - }, - "dragging": false, - "id": "note-FFvLx", - "measured": { - "height": 324, - "width": 324 - }, - "position": { - "x": -1309.8036340068625, - "y": 925.6888138443481 - }, - "selected": false, - "type": "noteNode" - }, - { - "data": { - "id": "LanguageModelComponent-oBZy3", - "node": { - "base_classes": [ - "LanguageModel", - "Message" - ], - "beta": false, - "category": "models", - "conditional_paths": [], - "custom_fields": {}, - "description": "Runs a language model given a specified provider. ", - "display_name": "Language Model", - "documentation": "", - "edited": false, - "field_order": [ - "provider", - "model_name", - "api_key", - "input_value", - "system_message", - "stream", - "temperature" - ], - "frozen": false, - "icon": "brain-circuit", - "key": "LanguageModelComponent", - "legacy": false, - "metadata": { - "keywords": [ - "model", - "llm", - "language model", - "large language model" - ] - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Model Response", - "group_outputs": false, - "method": "text_response", - "name": "text_output", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Language Model", - "group_outputs": false, - "method": "build_model", - "name": "model_output", - "selected": "LanguageModel", - "tool_mode": true, - "types": [ - "LanguageModel" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "priority": 0, - "score": 0.28173906304863156, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "OpenAI API Key", - "dynamic": false, - "info": "Model Provider API key", - "input_types": [], - "load_from_db": false, - "name": "api_key", - "password": true, - "placeholder": "", - "real_time_refresh": true, - "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": "from typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\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.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"GoogleGenerativeAI\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" - }, - "input_value": { - "_input_type": "MessageInput", - "advanced": false, - "display_name": "Input", - "dynamic": false, - "info": "The input text to send to the model", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "input_value", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "model_name": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model Name", - "dynamic": false, - "info": "Select the model to use", - "name": "model_name", - "options": [ - "gpt-4o-mini", - "gpt-4o", - "gpt-4.1", - "gpt-4.1-mini", - "gpt-4.1-nano", - "gpt-4.5-preview", - "gpt-4-turbo", - "gpt-4-turbo-preview", - "gpt-4", - "gpt-3.5-turbo" - ], - "options_metadata": [], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "gpt-4o-mini" - }, - "provider": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model Provider", - "dynamic": false, - "info": "Select the model provider", - "name": "provider", - "options": [ - "OpenAI", - "Anthropic", - "Google" - ], - "options_metadata": [ - { - "icon": "OpenAI" - }, - { - "icon": "Anthropic" - }, - { - "icon": "Google" - } - ], - "placeholder": "", - "real_time_refresh": true, - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "OpenAI" - }, - "stream": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Stream", - "dynamic": false, - "info": "Whether to stream the response", - "list": false, - "list_add_label": "Add More", - "name": "stream", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "system_message": { - "_input_type": "MultilineInput", - "advanced": true, - "copy_field": false, - "display_name": "System Message", - "dynamic": false, - "info": "A system message that helps set the behavior of the assistant", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "system_message", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "temperature": { - "_input_type": "SliderInput", - "advanced": true, - "display_name": "Temperature", - "dynamic": false, - "info": "Controls randomness in responses", - "max_label": "", - "max_label_icon": "", - "min_label": "", - "min_label_icon": "", - "name": "temperature", - "placeholder": "", - "range_spec": { - "max": 1, - "min": 0, - "step": 0.01, - "step_type": "float" - }, - "required": false, - "show": true, - "slider_buttons": false, - "slider_buttons_options": [], - "slider_input": false, - "title_case": false, - "tool_mode": false, - "type": "slider", - "value": 0.1 - } - }, - "tool_mode": false - }, - "showNode": true, - "type": "LanguageModelComponent" - }, - "dragging": false, - "id": "LanguageModelComponent-oBZy3", - "measured": { - "height": 451, - "width": 320 - }, - "position": { - "x": -1319.462305610481, - "y": 976.7543982544241 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "LanguageModelComponent-k9uIC", - "node": { - "base_classes": [ - "LanguageModel", - "Message" - ], - "beta": false, - "category": "models", - "conditional_paths": [], - "custom_fields": {}, - "description": "Runs a language model given a specified provider. ", - "display_name": "Language Model", - "documentation": "", - "edited": false, - "field_order": [ - "provider", - "model_name", - "api_key", - "input_value", - "system_message", - "stream", - "temperature" - ], - "frozen": false, - "icon": "brain-circuit", - "key": "LanguageModelComponent", - "legacy": false, - "metadata": { - "keywords": [ - "model", - "llm", - "language model", - "large language model" - ] - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Model Response", - "group_outputs": false, - "method": "text_response", - "name": "text_output", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Language Model", - "group_outputs": false, - "method": "build_model", - "name": "model_output", - "selected": "LanguageModel", - "tool_mode": true, - "types": [ - "LanguageModel" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "priority": 0, - "score": 0.28173906304863156, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "OpenAI API Key", - "dynamic": false, - "info": "Model Provider API key", - "input_types": [], - "load_from_db": false, - "name": "api_key", - "password": true, - "placeholder": "", - "real_time_refresh": true, - "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": "from typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\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.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"GoogleGenerativeAI\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" - }, - "input_value": { - "_input_type": "MessageInput", - "advanced": false, - "display_name": "Input", - "dynamic": false, - "info": "The input text to send to the model", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "input_value", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "model_name": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model Name", - "dynamic": false, - "info": "Select the model to use", - "name": "model_name", - "options": [ - "gpt-4o-mini", - "gpt-4o", - "gpt-4.1", - "gpt-4.1-mini", - "gpt-4.1-nano", - "gpt-4.5-preview", - "gpt-4-turbo", - "gpt-4-turbo-preview", - "gpt-4", - "gpt-3.5-turbo" - ], - "options_metadata": [], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "gpt-4o-mini" - }, - "provider": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model Provider", - "dynamic": false, - "info": "Select the model provider", - "name": "provider", - "options": [ - "OpenAI", - "Anthropic", - "Google" - ], - "options_metadata": [ - { - "icon": "OpenAI" - }, - { - "icon": "Anthropic" - }, - { - "icon": "Google" - } - ], - "placeholder": "", - "real_time_refresh": true, - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "OpenAI" - }, - "stream": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Stream", - "dynamic": false, - "info": "Whether to stream the response", - "list": false, - "list_add_label": "Add More", - "name": "stream", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "system_message": { - "_input_type": "MultilineInput", - "advanced": true, - "copy_field": false, - "display_name": "System Message", - "dynamic": false, - "info": "A system message that helps set the behavior of the assistant", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "system_message", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "temperature": { - "_input_type": "SliderInput", - "advanced": true, - "display_name": "Temperature", - "dynamic": false, - "info": "Controls randomness in responses", - "max_label": "", - "max_label_icon": "", - "min_label": "", - "min_label_icon": "", - "name": "temperature", - "placeholder": "", - "range_spec": { - "max": 1, - "min": 0, - "step": 0.01, - "step_type": "float" - }, - "required": false, - "show": true, - "slider_buttons": false, - "slider_buttons_options": [], - "slider_input": false, - "title_case": false, - "tool_mode": false, - "type": "slider", - "value": 0.1 - } - }, - "tool_mode": false - }, - "showNode": true, - "type": "LanguageModelComponent" - }, - "dragging": false, - "id": "LanguageModelComponent-k9uIC", - "measured": { - "height": 451, - "width": 320 - }, - "position": { - "x": -610.7115573962577, - "y": 1009.6862512017519 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "LanguageModelComponent-e7Tpe", - "node": { - "base_classes": [ - "LanguageModel", - "Message" - ], - "beta": false, - "category": "models", - "conditional_paths": [], - "custom_fields": {}, - "description": "Runs a language model given a specified provider. ", - "display_name": "Language Model", - "documentation": "", - "edited": false, - "field_order": [ - "provider", - "model_name", - "api_key", - "input_value", - "system_message", - "stream", - "temperature" - ], - "frozen": false, - "icon": "brain-circuit", - "key": "LanguageModelComponent", - "legacy": false, - "metadata": { - "keywords": [ - "model", - "llm", - "language model", - "large language model" - ] - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Model Response", - "group_outputs": false, - "method": "text_response", - "name": "text_output", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Language Model", - "group_outputs": false, - "method": "build_model", - "name": "model_output", - "selected": "LanguageModel", - "tool_mode": true, - "types": [ - "LanguageModel" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "priority": 0, - "score": 0.28173906304863156, - "template": { - "_type": "Component", - "api_key": { - "_input_type": "SecretStrInput", - "advanced": false, - "display_name": "OpenAI API Key", - "dynamic": false, - "info": "Model Provider API key", - "input_types": [], - "load_from_db": false, - "name": "api_key", - "password": true, - "placeholder": "", - "real_time_refresh": true, - "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": "from typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\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.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"GoogleGenerativeAI\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" - }, - "input_value": { - "_input_type": "MessageInput", - "advanced": false, - "display_name": "Input", - "dynamic": false, - "info": "The input text to send to the model", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "input_value", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "model_name": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model Name", - "dynamic": false, - "info": "Select the model to use", - "name": "model_name", - "options": [ - "gpt-4o-mini", - "gpt-4o", - "gpt-4.1", - "gpt-4.1-mini", - "gpt-4.1-nano", - "gpt-4.5-preview", - "gpt-4-turbo", - "gpt-4-turbo-preview", - "gpt-4", - "gpt-3.5-turbo" - ], - "options_metadata": [], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "gpt-4o-mini" - }, - "provider": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": false, - "dialog_inputs": {}, - "display_name": "Model Provider", - "dynamic": false, - "info": "Select the model provider", - "name": "provider", - "options": [ - "OpenAI", - "Anthropic", - "Google" - ], - "options_metadata": [ - { - "icon": "OpenAI" - }, - { - "icon": "Anthropic" - }, - { - "icon": "Google" - } - ], - "placeholder": "", - "real_time_refresh": true, - "required": false, - "show": true, - "title_case": false, - "toggle": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "OpenAI" - }, - "stream": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Stream", - "dynamic": false, - "info": "Whether to stream the response", - "list": false, - "list_add_label": "Add More", - "name": "stream", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "system_message": { - "_input_type": "MultilineInput", - "advanced": false, - "copy_field": false, - "display_name": "System Message", - "dynamic": false, - "info": "A system message that helps set the behavior of the assistant", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "system_message", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "temperature": { - "_input_type": "SliderInput", - "advanced": true, - "display_name": "Temperature", - "dynamic": false, - "info": "Controls randomness in responses", - "max_label": "", - "max_label_icon": "", - "min_label": "", - "min_label_icon": "", - "name": "temperature", - "placeholder": "", - "range_spec": { - "max": 1, - "min": 0, - "step": 0.01, - "step_type": "float" - }, - "required": false, - "show": true, - "slider_buttons": false, - "slider_buttons_options": [], - "slider_input": false, - "title_case": false, - "tool_mode": false, - "type": "slider", - "value": 0.1 - } - }, - "tool_mode": false - }, - "showNode": true, - "type": "LanguageModelComponent" - }, - "dragging": false, - "id": "LanguageModelComponent-e7Tpe", - "measured": { - "height": 534, - "width": 320 - }, - "position": { - "x": -1273.6440754228947, - "y": 303.799142374253 + "x": -2084.702607272029, + "y": 876.5761449064685 }, "selected": false, "type": "genericNode" } ], "viewport": { - "x": 1080.3151798224526, - "y": 124.02808893189399, - "zoom": 0.5594767841952385 + "x": 1531.333069410271, + "y": -51.51956711530852, + "zoom": 0.5298593037007338 } }, "description": "Load text data from various file formats, process it into structured messages, and analyze sentiment using AI-powered classification.", "endpoint_name": null, - "id": "42caf1cb-f860-4ce1-badc-644cf4ba8556", + "id": "dc9f3c5d-b43a-493e-9dee-8e6d0c55a9ca", "is_component": false, "last_tested_version": "1.4.3", "name": "Text Sentiment Analysis", 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 f2cdee768..bdd8f5357 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 @@ -7,7 +7,7 @@ "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-1w4cJ", + "id": "ChatInput-g8ha3", "name": "message", "output_types": [ "Message" @@ -15,7 +15,7 @@ }, "targetHandle": { "fieldName": "question", - "id": "Prompt-I9T0w", + "id": "Prompt-HCuAj", "inputTypes": [ "Message", "Text" @@ -23,70 +23,12 @@ "type": "str" } }, - "id": "reactflow__edge-ChatInput-1w4cJ{œdataTypeœ:œChatInputœ,œidœ:œChatInput-1w4cJœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-I9T0w{œfieldNameœ:œquestionœ,œidœ:œPrompt-I9T0wœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-ChatInput-g8ha3{œdataTypeœ:œChatInputœ,œidœ:œChatInput-g8ha3œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-HCuAj{œfieldNameœ:œquestionœ,œidœ:œPrompt-HCuAjœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", "selected": false, - "source": "ChatInput-1w4cJ", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-1w4cJœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-I9T0w", - "targetHandle": "{œfieldNameœ: œquestionœ, œidœ: œPrompt-I9T0wœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "Prompt", - "id": "Prompt-I9T0w", - "name": "prompt", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "input_value", - "id": "OpenAIModel-GczRI", - "inputTypes": [ - "Message" - ], - "type": "str" - } - }, - "id": "reactflow__edge-Prompt-I9T0w{œdataTypeœ:œPromptœ,œidœ:œPrompt-I9T0wœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-GczRI{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-GczRIœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "selected": false, - "source": "Prompt-I9T0w", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-I9T0wœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "OpenAIModel-GczRI", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-GczRIœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "OpenAIModel", - "id": "OpenAIModel-GczRI", - "name": "text_output", - "output_types": [ - "Message" - ] - }, - "targetHandle": { - "fieldName": "input_value", - "id": "ChatOutput-Dlahs", - "inputTypes": [ - "Data", - "DataFrame", - "Message" - ], - "type": "str" - } - }, - "id": "reactflow__edge-OpenAIModel-GczRI{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-GczRIœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-Dlahs{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Dlahsœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", - "selected": false, - "source": "OpenAIModel-GczRI", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-GczRIœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-Dlahs", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-Dlahsœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + "source": "ChatInput-g8ha3", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-g8ha3œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-HCuAj", + "targetHandle": "{œfieldNameœ: œquestionœ, œidœ: œPrompt-HCuAjœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -94,7 +36,7 @@ "data": { "sourceHandle": { "dataType": "parser", - "id": "parser-IEn6I", + "id": "parser-ehrBO", "name": "parsed_text", "output_types": [ "Message" @@ -102,7 +44,7 @@ }, "targetHandle": { "fieldName": "context", - "id": "Prompt-I9T0w", + "id": "Prompt-HCuAj", "inputTypes": [ "Message", "Text" @@ -110,12 +52,12 @@ "type": "str" } }, - "id": "reactflow__edge-parser-IEn6I{œdataTypeœ:œparserœ,œidœ:œparser-IEn6Iœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-I9T0w{œfieldNameœ:œcontextœ,œidœ:œPrompt-I9T0wœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-parser-ehrBO{œdataTypeœ:œparserœ,œidœ:œparser-ehrBOœ,œnameœ:œparsed_textœ,œoutput_typesœ:[œMessageœ]}-Prompt-HCuAj{œfieldNameœ:œcontextœ,œidœ:œPrompt-HCuAjœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", "selected": false, - "source": "parser-IEn6I", - "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-IEn6Iœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-I9T0w", - "targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-I9T0wœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" + "source": "parser-ehrBO", + "sourceHandle": "{œdataTypeœ: œparserœ, œidœ: œparser-ehrBOœ, œnameœ: œparsed_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-HCuAj", + "targetHandle": "{œfieldNameœ: œcontextœ, œidœ: œPrompt-HCuAjœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" }, { "animated": false, @@ -123,7 +65,7 @@ "data": { "sourceHandle": { "dataType": "OpenAIEmbeddings", - "id": "OpenAIEmbeddings-OlQ6R", + "id": "OpenAIEmbeddings-EjaQd", "name": "embeddings", "output_types": [ "Embeddings" @@ -131,19 +73,19 @@ }, "targetHandle": { "fieldName": "embedding_model", - "id": "AstraDB-8AuVs", + "id": "AstraDB-H2sZ0", "inputTypes": [ "Embeddings" ], "type": "other" } }, - "id": "reactflow__edge-OpenAIEmbeddings-OlQ6R{œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-OlQ6Rœ,œnameœ:œembeddingsœ,œoutput_typesœ:[œEmbeddingsœ]}-AstraDB-8AuVs{œfieldNameœ:œembedding_modelœ,œidœ:œAstraDB-8AuVsœ,œinputTypesœ:[œEmbeddingsœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-OpenAIEmbeddings-EjaQd{œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-EjaQdœ,œnameœ:œembeddingsœ,œoutput_typesœ:[œEmbeddingsœ]}-AstraDB-H2sZ0{œfieldNameœ:œembedding_modelœ,œidœ:œAstraDB-H2sZ0œ,œinputTypesœ:[œEmbeddingsœ],œtypeœ:œotherœ}", "selected": false, - "source": "OpenAIEmbeddings-OlQ6R", - "sourceHandle": "{œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-OlQ6Rœ, œnameœ: œembeddingsœ, œoutput_typesœ: [œEmbeddingsœ]}", - "target": "AstraDB-8AuVs", - "targetHandle": "{œfieldNameœ: œembedding_modelœ, œidœ: œAstraDB-8AuVsœ, œinputTypesœ: [œEmbeddingsœ], œtypeœ: œotherœ}" + "source": "OpenAIEmbeddings-EjaQd", + "sourceHandle": "{œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-EjaQdœ, œnameœ: œembeddingsœ, œoutput_typesœ: [œEmbeddingsœ]}", + "target": "AstraDB-H2sZ0", + "targetHandle": "{œfieldNameœ: œembedding_modelœ, œidœ: œAstraDB-H2sZ0œ, œinputTypesœ: [œEmbeddingsœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -151,7 +93,7 @@ "data": { "sourceHandle": { "dataType": "OpenAIEmbeddings", - "id": "OpenAIEmbeddings-ulN0y", + "id": "OpenAIEmbeddings-LqNei", "name": "embeddings", "output_types": [ "Embeddings" @@ -159,19 +101,19 @@ }, "targetHandle": { "fieldName": "embedding_model", - "id": "AstraDB-Wep9C", + "id": "AstraDB-qCu1f", "inputTypes": [ "Embeddings" ], "type": "other" } }, - "id": "reactflow__edge-OpenAIEmbeddings-ulN0y{œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-ulN0yœ,œnameœ:œembeddingsœ,œoutput_typesœ:[œEmbeddingsœ]}-AstraDB-Wep9C{œfieldNameœ:œembedding_modelœ,œidœ:œAstraDB-Wep9Cœ,œinputTypesœ:[œEmbeddingsœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-OpenAIEmbeddings-LqNei{œdataTypeœ:œOpenAIEmbeddingsœ,œidœ:œOpenAIEmbeddings-LqNeiœ,œnameœ:œembeddingsœ,œoutput_typesœ:[œEmbeddingsœ]}-AstraDB-qCu1f{œfieldNameœ:œembedding_modelœ,œidœ:œAstraDB-qCu1fœ,œinputTypesœ:[œEmbeddingsœ],œtypeœ:œotherœ}", "selected": false, - "source": "OpenAIEmbeddings-ulN0y", - "sourceHandle": "{œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-ulN0yœ, œnameœ: œembeddingsœ, œoutput_typesœ: [œEmbeddingsœ]}", - "target": "AstraDB-Wep9C", - "targetHandle": "{œfieldNameœ: œembedding_modelœ, œidœ: œAstraDB-Wep9Cœ, œinputTypesœ: [œEmbeddingsœ], œtypeœ: œotherœ}" + "source": "OpenAIEmbeddings-LqNei", + "sourceHandle": "{œdataTypeœ: œOpenAIEmbeddingsœ, œidœ: œOpenAIEmbeddings-LqNeiœ, œnameœ: œembeddingsœ, œoutput_typesœ: [œEmbeddingsœ]}", + "target": "AstraDB-qCu1f", + "targetHandle": "{œfieldNameœ: œembedding_modelœ, œidœ: œAstraDB-qCu1fœ, œinputTypesœ: [œEmbeddingsœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -179,7 +121,7 @@ "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-1w4cJ", + "id": "ChatInput-g8ha3", "name": "message", "output_types": [ "Message" @@ -187,19 +129,19 @@ }, "targetHandle": { "fieldName": "search_query", - "id": "AstraDB-Wep9C", + "id": "AstraDB-qCu1f", "inputTypes": [ "Message" ], "type": "query" } }, - "id": "reactflow__edge-ChatInput-1w4cJ{œdataTypeœ:œChatInputœ,œidœ:œChatInput-1w4cJœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-AstraDB-Wep9C{œfieldNameœ:œsearch_queryœ,œidœ:œAstraDB-Wep9Cœ,œinputTypesœ:[œMessageœ],œtypeœ:œqueryœ}", + "id": "reactflow__edge-ChatInput-g8ha3{œdataTypeœ:œChatInputœ,œidœ:œChatInput-g8ha3œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-AstraDB-qCu1f{œfieldNameœ:œsearch_queryœ,œidœ:œAstraDB-qCu1fœ,œinputTypesœ:[œMessageœ],œtypeœ:œqueryœ}", "selected": false, - "source": "ChatInput-1w4cJ", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-1w4cJœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "AstraDB-Wep9C", - "targetHandle": "{œfieldNameœ: œsearch_queryœ, œidœ: œAstraDB-Wep9Cœ, œinputTypesœ: [œMessageœ], œtypeœ: œqueryœ}" + "source": "ChatInput-g8ha3", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-g8ha3œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "AstraDB-qCu1f", + "targetHandle": "{œfieldNameœ: œsearch_queryœ, œidœ: œAstraDB-qCu1fœ, œinputTypesœ: [œMessageœ], œtypeœ: œqueryœ}" }, { "animated": false, @@ -207,7 +149,7 @@ "data": { "sourceHandle": { "dataType": "AstraDB", - "id": "AstraDB-Wep9C", + "id": "AstraDB-qCu1f", "name": "dataframe", "output_types": [ "DataFrame" @@ -215,7 +157,7 @@ }, "targetHandle": { "fieldName": "input_data", - "id": "parser-IEn6I", + "id": "parser-ehrBO", "inputTypes": [ "DataFrame", "Data" @@ -223,41 +165,12 @@ "type": "other" } }, - "id": "reactflow__edge-AstraDB-Wep9C{œdataTypeœ:œAstraDBœ,œidœ:œAstraDB-Wep9Cœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-parser-IEn6I{œfieldNameœ:œinput_dataœ,œidœ:œparser-IEn6Iœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-AstraDB-qCu1f{œdataTypeœ:œAstraDBœ,œidœ:œAstraDB-qCu1fœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-parser-ehrBO{œfieldNameœ:œinput_dataœ,œidœ:œparser-ehrBOœ,œinputTypesœ:[œDataFrameœ,œDataœ],œtypeœ:œotherœ}", "selected": false, - "source": "AstraDB-Wep9C", - "sourceHandle": "{œdataTypeœ: œAstraDBœ, œidœ: œAstraDB-Wep9Cœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", - "target": "parser-IEn6I", - "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-IEn6Iœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" - }, - { - "animated": false, - "className": "", - "data": { - "sourceHandle": { - "dataType": "File", - "id": "File-C3stS", - "name": "dataframe", - "output_types": [ - "DataFrame" - ] - }, - "targetHandle": { - "fieldName": "data_inputs", - "id": "SplitText-5RcUo", - "inputTypes": [ - "Data", - "DataFrame" - ], - "type": "other" - } - }, - "id": "reactflow__edge-File-C3stS{œdataTypeœ:œFileœ,œidœ:œFile-C3stSœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-SplitText-5RcUo{œfieldNameœ:œdata_inputsœ,œidœ:œSplitText-5RcUoœ,œinputTypesœ:[œDataœ,œDataFrameœ],œtypeœ:œotherœ}", - "selected": false, - "source": "File-C3stS", - "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-C3stSœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", - "target": "SplitText-5RcUo", - "targetHandle": "{œfieldNameœ: œdata_inputsœ, œidœ: œSplitText-5RcUoœ, œinputTypesœ: [œDataœ, œDataFrameœ], œtypeœ: œotherœ}" + "source": "AstraDB-qCu1f", + "sourceHandle": "{œdataTypeœ: œAstraDBœ, œidœ: œAstraDB-qCu1fœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "parser-ehrBO", + "targetHandle": "{œfieldNameœ: œinput_dataœ, œidœ: œparser-ehrBOœ, œinputTypesœ: [œDataFrameœ, œDataœ], œtypeœ: œotherœ}" }, { "animated": false, @@ -265,7 +178,7 @@ "data": { "sourceHandle": { "dataType": "SplitText", - "id": "SplitText-5RcUo", + "id": "SplitText-iHZQk", "name": "dataframe", "output_types": [ "DataFrame" @@ -273,7 +186,7 @@ }, "targetHandle": { "fieldName": "ingest_data", - "id": "AstraDB-8AuVs", + "id": "AstraDB-H2sZ0", "inputTypes": [ "Data", "DataFrame" @@ -281,12 +194,96 @@ "type": "other" } }, - "id": "reactflow__edge-SplitText-5RcUo{œdataTypeœ:œSplitTextœ,œidœ:œSplitText-5RcUoœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-AstraDB-8AuVs{œfieldNameœ:œingest_dataœ,œidœ:œAstraDB-8AuVsœ,œinputTypesœ:[œDataœ,œDataFrameœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-SplitText-iHZQk{œdataTypeœ:œSplitTextœ,œidœ:œSplitText-iHZQkœ,œnameœ:œdataframeœ,œoutput_typesœ:[œDataFrameœ]}-AstraDB-H2sZ0{œfieldNameœ:œingest_dataœ,œidœ:œAstraDB-H2sZ0œ,œinputTypesœ:[œDataœ,œDataFrameœ],œtypeœ:œotherœ}", "selected": false, - "source": "SplitText-5RcUo", - "sourceHandle": "{œdataTypeœ: œSplitTextœ, œidœ: œSplitText-5RcUoœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", - "target": "AstraDB-8AuVs", - "targetHandle": "{œfieldNameœ: œingest_dataœ, œidœ: œAstraDB-8AuVsœ, œinputTypesœ: [œDataœ, œDataFrameœ], œtypeœ: œotherœ}" + "source": "SplitText-iHZQk", + "sourceHandle": "{œdataTypeœ: œSplitTextœ, œidœ: œSplitText-iHZQkœ, œnameœ: œdataframeœ, œoutput_typesœ: [œDataFrameœ]}", + "target": "AstraDB-H2sZ0", + "targetHandle": "{œfieldNameœ: œingest_dataœ, œidœ: œAstraDB-H2sZ0œ, œinputTypesœ: [œDataœ, œDataFrameœ], œtypeœ: œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "File", + "id": "File-J8GVA", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "data_inputs", + "id": "SplitText-iHZQk", + "inputTypes": [ + "Data", + "DataFrame", + "Message" + ], + "type": "other" + } + }, + "id": "xy-edge__File-J8GVA{œdataTypeœ:œFileœ,œidœ:œFile-J8GVAœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-SplitText-iHZQk{œfieldNameœ:œdata_inputsœ,œidœ:œSplitText-iHZQkœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œotherœ}", + "selected": false, + "source": "File-J8GVA", + "sourceHandle": "{œdataTypeœ: œFileœ, œidœ: œFile-J8GVAœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "SplitText-iHZQk", + "targetHandle": "{œfieldNameœ: œdata_inputsœ, œidœ: œSplitText-iHZQkœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œotherœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "Prompt", + "id": "Prompt-HCuAj", + "name": "prompt", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "LanguageModelComponent-cmjdt", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "xy-edge__Prompt-HCuAj{œdataTypeœ:œPromptœ,œidœ:œPrompt-HCuAjœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-LanguageModelComponent-cmjdt{œfieldNameœ:œinput_valueœ,œidœ:œLanguageModelComponent-cmjdtœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "Prompt-HCuAj", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-HCuAjœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "LanguageModelComponent-cmjdt", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œLanguageModelComponent-cmjdtœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "LanguageModelComponent", + "id": "LanguageModelComponent-cmjdt", + "name": "text_output", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "ChatOutput-mfQ3f", + "inputTypes": [ + "Data", + "DataFrame", + "Message" + ], + "type": "str" + } + }, + "id": "xy-edge__LanguageModelComponent-cmjdt{œdataTypeœ:œLanguageModelComponentœ,œidœ:œLanguageModelComponent-cmjdtœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-mfQ3f{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-mfQ3fœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "source": "LanguageModelComponent-cmjdt", + "sourceHandle": "{œdataTypeœ: œLanguageModelComponentœ, œidœ: œLanguageModelComponent-cmjdtœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-mfQ3f", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-mfQ3fœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" } ], "nodes": [ @@ -294,7 +291,7 @@ "data": { "description": "Get chat inputs from the Playground.", "display_name": "Chat Input", - "id": "ChatInput-1w4cJ", + "id": "ChatInput-g8ha3", "node": { "base_classes": [ "Message" @@ -561,7 +558,7 @@ }, "dragging": false, "height": 234, - "id": "ChatInput-1w4cJ", + "id": "ChatInput-g8ha3", "measured": { "height": 234, "width": 320 @@ -582,7 +579,7 @@ "data": { "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", - "id": "Prompt-I9T0w", + "id": "Prompt-HCuAj", "node": { "base_classes": [ "Message" @@ -743,7 +740,7 @@ }, "dragging": false, "height": 433, - "id": "Prompt-I9T0w", + "id": "Prompt-HCuAj", "measured": { "height": 433, "width": 320 @@ -764,7 +761,7 @@ "data": { "description": "Split text into chunks based on specified criteria.", "display_name": "Split Text", - "id": "SplitText-5RcUo", + "id": "SplitText-iHZQk", "node": { "base_classes": [ "Data" @@ -853,16 +850,17 @@ "show": true, "title_case": false, "type": "code", - "value": "from langchain_text_splitters import CharacterTextSplitter\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.io import DropdownInput, HandleInput, IntInput, MessageTextInput, Output\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.utils.util import unescape_string\n\n\nclass SplitTextComponent(Component):\n display_name: str = \"Split Text\"\n description: str = \"Split text into chunks based on specified criteria.\"\n icon = \"scissors-line-dashed\"\n name = \"SplitText\"\n\n inputs = [\n HandleInput(\n name=\"data_inputs\",\n display_name=\"Data or DataFrame\",\n info=\"The data with texts to split in chunks.\",\n input_types=[\"Data\", \"DataFrame\"],\n required=True,\n ),\n IntInput(\n name=\"chunk_overlap\",\n display_name=\"Chunk Overlap\",\n info=\"Number of characters to overlap between chunks.\",\n value=200,\n ),\n IntInput(\n name=\"chunk_size\",\n display_name=\"Chunk Size\",\n info=(\n \"The maximum length of each chunk. Text is first split by separator, \"\n \"then chunks are merged up to this size. \"\n \"Individual splits larger than this won't be further divided.\"\n ),\n value=1000,\n ),\n MessageTextInput(\n name=\"separator\",\n display_name=\"Separator\",\n info=(\n \"The character to split on. Use \\\\n for newline. \"\n \"Examples: \\\\n\\\\n for paragraphs, \\\\n for lines, . for sentences\"\n ),\n value=\"\\n\",\n ),\n MessageTextInput(\n name=\"text_key\",\n display_name=\"Text Key\",\n info=\"The key to use for the text column.\",\n value=\"text\",\n advanced=True,\n ),\n DropdownInput(\n name=\"keep_separator\",\n display_name=\"Keep Separator\",\n info=\"Whether to keep the separator in the output chunks and where to place it.\",\n options=[\"False\", \"True\", \"Start\", \"End\"],\n value=\"False\",\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Chunks\", name=\"dataframe\", method=\"split_text\"),\n ]\n\n def _docs_to_data(self, docs) -> list[Data]:\n return [Data(text=doc.page_content, data=doc.metadata) for doc in docs]\n\n def _fix_separator(self, separator: str) -> str:\n \"\"\"Fix common separator issues and convert to proper format.\"\"\"\n if separator == \"/n\":\n return \"\\n\"\n if separator == \"/t\":\n return \"\\t\"\n return separator\n\n def split_text_base(self):\n separator = self._fix_separator(self.separator)\n separator = unescape_string(separator)\n\n if isinstance(self.data_inputs, DataFrame):\n if not len(self.data_inputs):\n msg = \"DataFrame is empty\"\n raise TypeError(msg)\n\n self.data_inputs.text_key = self.text_key\n try:\n documents = self.data_inputs.to_lc_documents()\n except Exception as e:\n msg = f\"Error converting DataFrame to documents: {e}\"\n raise TypeError(msg) from e\n else:\n if not self.data_inputs:\n msg = \"No data inputs provided\"\n raise TypeError(msg)\n\n documents = []\n if isinstance(self.data_inputs, Data):\n self.data_inputs.text_key = self.text_key\n documents = [self.data_inputs.to_lc_document()]\n else:\n try:\n documents = [input_.to_lc_document() for input_ in self.data_inputs if isinstance(input_, Data)]\n if not documents:\n msg = f\"No valid Data inputs found in {type(self.data_inputs)}\"\n raise TypeError(msg)\n except AttributeError as e:\n msg = f\"Invalid input type in collection: {e}\"\n raise TypeError(msg) from e\n try:\n # Convert string 'False'/'True' to boolean\n keep_sep = self.keep_separator\n if isinstance(keep_sep, str):\n if keep_sep.lower() == \"false\":\n keep_sep = False\n elif keep_sep.lower() == \"true\":\n keep_sep = True\n # 'start' and 'end' are kept as strings\n\n splitter = CharacterTextSplitter(\n chunk_overlap=self.chunk_overlap,\n chunk_size=self.chunk_size,\n separator=separator,\n keep_separator=keep_sep,\n )\n return splitter.split_documents(documents)\n except Exception as e:\n msg = f\"Error splitting text: {e}\"\n raise TypeError(msg) from e\n\n def split_text(self) -> DataFrame:\n return DataFrame(self._docs_to_data(self.split_text_base()))\n" + "value": "from langchain_text_splitters import CharacterTextSplitter\n\nfrom langflow.custom.custom_component.component import Component\nfrom langflow.io import DropdownInput, HandleInput, IntInput, MessageTextInput, Output\nfrom langflow.schema.data import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\nfrom langflow.utils.util import unescape_string\n\n\nclass SplitTextComponent(Component):\n display_name: str = \"Split Text\"\n description: str = \"Split text into chunks based on specified criteria.\"\n icon = \"scissors-line-dashed\"\n name = \"SplitText\"\n\n inputs = [\n HandleInput(\n name=\"data_inputs\",\n display_name=\"Input\",\n info=\"The data with texts to split in chunks.\",\n input_types=[\"Data\", \"DataFrame\", \"Message\"],\n required=True,\n ),\n IntInput(\n name=\"chunk_overlap\",\n display_name=\"Chunk Overlap\",\n info=\"Number of characters to overlap between chunks.\",\n value=200,\n ),\n IntInput(\n name=\"chunk_size\",\n display_name=\"Chunk Size\",\n info=(\n \"The maximum length of each chunk. Text is first split by separator, \"\n \"then chunks are merged up to this size. \"\n \"Individual splits larger than this won't be further divided.\"\n ),\n value=1000,\n ),\n MessageTextInput(\n name=\"separator\",\n display_name=\"Separator\",\n info=(\n \"The character to split on. Use \\\\n for newline. \"\n \"Examples: \\\\n\\\\n for paragraphs, \\\\n for lines, . for sentences\"\n ),\n value=\"\\n\",\n ),\n MessageTextInput(\n name=\"text_key\",\n display_name=\"Text Key\",\n info=\"The key to use for the text column.\",\n value=\"text\",\n advanced=True,\n ),\n DropdownInput(\n name=\"keep_separator\",\n display_name=\"Keep Separator\",\n info=\"Whether to keep the separator in the output chunks and where to place it.\",\n options=[\"False\", \"True\", \"Start\", \"End\"],\n value=\"False\",\n advanced=True,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Chunks\", name=\"dataframe\", method=\"split_text\"),\n ]\n\n def _docs_to_data(self, docs) -> list[Data]:\n return [Data(text=doc.page_content, data=doc.metadata) for doc in docs]\n\n def _fix_separator(self, separator: str) -> str:\n \"\"\"Fix common separator issues and convert to proper format.\"\"\"\n if separator == \"/n\":\n return \"\\n\"\n if separator == \"/t\":\n return \"\\t\"\n return separator\n\n def split_text_base(self):\n separator = self._fix_separator(self.separator)\n separator = unescape_string(separator)\n\n if isinstance(self.data_inputs, DataFrame):\n if not len(self.data_inputs):\n msg = \"DataFrame is empty\"\n raise TypeError(msg)\n\n self.data_inputs.text_key = self.text_key\n try:\n documents = self.data_inputs.to_lc_documents()\n except Exception as e:\n msg = f\"Error converting DataFrame to documents: {e}\"\n raise TypeError(msg) from e\n elif isinstance(self.data_inputs, Message):\n self.data_inputs = [self.data_inputs.to_data()]\n return self.split_text_base()\n else:\n if not self.data_inputs:\n msg = \"No data inputs provided\"\n raise TypeError(msg)\n\n documents = []\n if isinstance(self.data_inputs, Data):\n self.data_inputs.text_key = self.text_key\n documents = [self.data_inputs.to_lc_document()]\n else:\n try:\n documents = [input_.to_lc_document() for input_ in self.data_inputs if isinstance(input_, Data)]\n if not documents:\n msg = f\"No valid Data inputs found in {type(self.data_inputs)}\"\n raise TypeError(msg)\n except AttributeError as e:\n msg = f\"Invalid input type in collection: {e}\"\n raise TypeError(msg) from e\n try:\n # Convert string 'False'/'True' to boolean\n keep_sep = self.keep_separator\n if isinstance(keep_sep, str):\n if keep_sep.lower() == \"false\":\n keep_sep = False\n elif keep_sep.lower() == \"true\":\n keep_sep = True\n # 'start' and 'end' are kept as strings\n\n splitter = CharacterTextSplitter(\n chunk_overlap=self.chunk_overlap,\n chunk_size=self.chunk_size,\n separator=separator,\n keep_separator=keep_sep,\n )\n return splitter.split_documents(documents)\n except Exception as e:\n msg = f\"Error splitting text: {e}\"\n raise TypeError(msg) from e\n\n def split_text(self) -> DataFrame:\n return DataFrame(self._docs_to_data(self.split_text_base()))\n" }, "data_inputs": { "advanced": false, - "display_name": "Data or DataFrame", + "display_name": "Input", "dynamic": false, "info": "The data with texts to split in chunks.", "input_types": [ "Data", - "DataFrame" + "DataFrame", + "Message" ], "list": false, "name": "data_inputs", @@ -949,7 +947,7 @@ }, "dragging": false, "height": 475, - "id": "SplitText-5RcUo", + "id": "SplitText-iHZQk", "measured": { "height": 475, "width": 320 @@ -968,7 +966,7 @@ }, { "data": { - "id": "note-BG8Kh", + "id": "note-B9a7p", "node": { "description": "## 🐕 2. Retriever Flow\n\nThis flow answers your questions with contextual data retrieved from your vector database.\n\nOpen the **Playground** and ask, \n\n```\nWhat is this document about?\n```\n", "display_name": "", @@ -981,10 +979,10 @@ }, "dragging": false, "height": 324, - "id": "note-BG8Kh", + "id": "note-B9a7p", "measured": { "height": 324, - "width": 325 + "width": 324 }, "position": { "x": 374.388314931542, @@ -1005,7 +1003,7 @@ }, { "data": { - "id": "note-IF0Cy", + "id": "note-zlGxX", "node": { "description": "## 📖 README\n\nLoad your data into a vector database with the 📚 **Load Data** flow, and then use your data as chat context with the 🐕 **Retriever** flow.\n\n**🚨 Add your OpenAI API key as a global variable to easily add it to all of the OpenAI components in this flow.** \n\n**Quick start**\n1. Run the 📚 **Load Data** flow.\n2. Run the 🐕 **Retriever** flow.\n\n**Next steps** \n\n- Experiment by changing the prompt and the loaded data to see how the bot's responses change. \n\nFor more info, see the [Langflow docs](https://docs.langflow.org/starter-projects-vector-store-rag).", "display_name": "Read Me", @@ -1018,10 +1016,10 @@ }, "dragging": false, "height": 324, - "id": "note-IF0Cy", + "id": "note-zlGxX", "measured": { "height": 324, - "width": 325 + "width": 324 }, "position": { "x": 191.12162720143235, @@ -1044,7 +1042,7 @@ "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-Dlahs", + "id": "ChatOutput-mfQ3f", "node": { "base_classes": [ "Message" @@ -1327,7 +1325,7 @@ }, "dragging": false, "height": 234, - "id": "ChatOutput-Dlahs", + "id": "ChatOutput-mfQ3f", "measured": { "height": 234, "width": 320 @@ -1346,7 +1344,7 @@ }, { "data": { - "id": "OpenAIEmbeddings-ulN0y", + "id": "OpenAIEmbeddings-LqNei", "node": { "base_classes": [ "Embeddings" @@ -1823,7 +1821,7 @@ }, "dragging": false, "height": 320, - "id": "OpenAIEmbeddings-ulN0y", + "id": "OpenAIEmbeddings-LqNei", "measured": { "height": 320, "width": 320 @@ -1842,7 +1840,7 @@ }, { "data": { - "id": "note-odwIB", + "id": "note-qlZFl", "node": { "description": "## 📚 1. Load Data Flow\n\nRun this first! Load data from a local file and embed it into the vector database.\n\nSelect a Database and a Collection, or create new ones. \n\nClick ▶️ **Run component** on the **Astra DB** component to load your data.\n\n* If you're using OSS Langflow, add your Astra DB Application Token to the Astra DB component.\n\n#### Next steps:\n Experiment by changing the prompt and the contextual data to see how the retrieval flow's responses change.", "display_name": "", @@ -1855,10 +1853,10 @@ }, "dragging": false, "height": 324, - "id": "note-odwIB", + "id": "note-qlZFl", "measured": { "height": 324, - "width": 325 + "width": 324 }, "position": { "x": 955.3277857006676, @@ -1879,7 +1877,7 @@ }, { "data": { - "id": "OpenAIEmbeddings-OlQ6R", + "id": "OpenAIEmbeddings-EjaQd", "node": { "base_classes": [ "Embeddings" @@ -2356,7 +2354,7 @@ }, "dragging": false, "height": 320, - "id": "OpenAIEmbeddings-OlQ6R", + "id": "OpenAIEmbeddings-EjaQd", "measured": { "height": 320, "width": 320 @@ -2375,287 +2373,7 @@ }, { "data": { - "id": "File-C3stS", - "node": { - "base_classes": [ - "Data" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": {}, - "description": "Loads content from one or more files as a DataFrame.", - "display_name": "File", - "documentation": "", - "edited": false, - "field_order": [ - "path", - "silent_errors", - "use_multithreading", - "concurrency_multithreading" - ], - "frozen": false, - "icon": "file-text", - "legacy": false, - "lf_version": "1.1.1", - "metadata": {}, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Loaded Files", - "group_outputs": false, - "method": "load_files", - "name": "dataframe", - "selected": "DataFrame", - "tool_mode": true, - "types": [ - "DataFrame" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Raw Content", - "group_outputs": false, - "method": "load_files_message", - "name": "message", - "selected": "Message", - "tool_mode": true, - "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.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, IntInput\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n inputs = [\n *BaseFileComponent._base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n *BaseFileComponent._base_outputs,\n ]\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" - }, - "concurrency_multithreading": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Processing Concurrency", - "dynamic": false, - "info": "When multiple files are being processed, the number of files to process concurrently.", - "list": false, - "name": "concurrency_multithreading", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "int", - "value": 4 - }, - "delete_server_file_after_processing": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Delete Server File After Processing", - "dynamic": false, - "info": "If true, the Server File Path will be deleted after processing.", - "list": false, - "name": "delete_server_file_after_processing", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "file_path": { - "_input_type": "HandleInput", - "advanced": true, - "display_name": "Server File Path", - "dynamic": false, - "info": "Data object with a 'file_path' property pointing to server file or a Message object with a path to the file. Supercedes 'Path' but supports same file types.", - "input_types": [ - "Data", - "Message" - ], - "list": true, - "name": "file_path", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "other", - "value": "" - }, - "ignore_unspecified_files": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Ignore Unspecified Files", - "dynamic": false, - "info": "If true, Data with no 'file_path' property will be ignored.", - "list": false, - "name": "ignore_unspecified_files", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "ignore_unsupported_extensions": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Ignore Unsupported Extensions", - "dynamic": false, - "info": "If true, files with unsupported extensions will not be processed.", - "list": false, - "name": "ignore_unsupported_extensions", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "bool", - "value": true - }, - "path": { - "_input_type": "FileInput", - "advanced": false, - "display_name": "Files", - "dynamic": false, - "fileTypes": [ - "txt", - "md", - "mdx", - "csv", - "json", - "yaml", - "yml", - "xml", - "html", - "htm", - "pdf", - "docx", - "py", - "sh", - "sql", - "js", - "ts", - "tsx", - "zip", - "tar", - "tgz", - "bz2", - "gz" - ], - "file_path": [], - "info": "Supported file extensions: txt, md, mdx, csv, json, yaml, yml, xml, html, htm, pdf, docx, py, sh, sql, js, ts, tsx; optionally bundled in file extensions: zip, tar, tgz, bz2, gz", - "list": true, - "name": "path", - "placeholder": "", - "required": false, - "show": true, - "temp_file": false, - "title_case": false, - "trace_as_metadata": true, - "type": "file", - "value": "" - }, - "separator": { - "_input_type": "StrInput", - "advanced": true, - "display_name": "Separator", - "dynamic": false, - "info": "Specify the separator to use between multiple outputs in Message format.", - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "separator", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "\n\n" - }, - "silent_errors": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Silent Errors", - "dynamic": false, - "info": "If true, errors will not raise an exception.", - "list": false, - "name": "silent_errors", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "use_multithreading": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "[Deprecated] Use Multithreading", - "dynamic": false, - "info": "Set 'Processing Concurrency' greater than 1 to enable multithreading.", - "list": false, - "name": "use_multithreading", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - } - }, - "tool_mode": false - }, - "selected_output": "dataframe", - "type": "File" - }, - "dragging": false, - "height": 367, - "id": "File-C3stS", - "measured": { - "height": 367, - "width": 320 - }, - "position": { - "x": 1314.0643184619548, - "y": 1508.2155181023356 - }, - "positionAbsolute": { - "x": 1318.9043936921921, - "y": 1484.0151419511485 - }, - "selected": false, - "type": "genericNode", - "width": 320 - }, - { - "data": { - "id": "note-aHmST", + "id": "note-p3NKF", "node": { "description": "### 💡 Add your OpenAI API key here 👇", "display_name": "", @@ -2668,7 +2386,7 @@ }, "dragging": false, "height": 324, - "id": "note-aHmST", + "id": "note-p3NKF", "measured": { "height": 324, "width": 324 @@ -2687,7 +2405,7 @@ }, { "data": { - "id": "note-QvcXX", + "id": "note-msH3w", "node": { "description": "### 💡 Add your OpenAI API key here 👇", "display_name": "", @@ -2700,7 +2418,7 @@ }, "dragging": false, "height": 324, - "id": "note-QvcXX", + "id": "note-msH3w", "measured": { "height": 324, "width": 324 @@ -2719,7 +2437,7 @@ }, { "data": { - "id": "note-xLYll", + "id": "note-5lklz", "node": { "description": "### 💡 Add your OpenAI API key here 👇", "display_name": "", @@ -2732,14 +2450,14 @@ }, "dragging": false, "height": 324, - "id": "note-xLYll", + "id": "note-5lklz", "measured": { "height": 324, "width": 324 }, "position": { "x": 2350.297636215281, - "y": 525.0687902842766 + "y": 577.4592910079571 }, "positionAbsolute": { "x": 2350.297636215281, @@ -2751,397 +2469,7 @@ }, { "data": { - "id": "OpenAIModel-GczRI", - "node": { - "base_classes": [ - "LanguageModel", - "Message" - ], - "beta": false, - "category": "models", - "conditional_paths": [], - "custom_fields": {}, - "description": "Generates text using OpenAI LLMs.", - "display_name": "OpenAI", - "documentation": "", - "edited": false, - "field_order": [ - "input_value", - "system_message", - "stream", - "max_tokens", - "model_kwargs", - "json_mode", - "model_name", - "openai_api_base", - "api_key", - "temperature", - "seed" - ], - "frozen": false, - "icon": "OpenAI", - "key": "OpenAIModel", - "legacy": false, - "metadata": { - "keywords": [ - "model", - "llm", - "language model", - "large language model" - ] - }, - "minimized": false, - "output_types": [], - "outputs": [ - { - "allows_loop": false, - "cache": true, - "display_name": "Model Response", - "group_outputs": false, - "method": "text_response", - "name": "text_output", - "selected": "Message", - "tool_mode": true, - "types": [ - "Message" - ], - "value": "__UNDEFINED__" - }, - { - "allows_loop": false, - "cache": true, - "display_name": "Language Model", - "group_outputs": false, - "method": "build_model", - "name": "model_output", - "selected": "LanguageModel", - "tool_mode": true, - "types": [ - "LanguageModel" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "score": 0.14285714285714285, - "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": [], - "load_from_db": true, - "name": "api_key", - "password": true, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "str", - "value": "OPENAI_API_KEY" - }, - "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 typing import Any\n\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 (\n OPENAI_MODEL_NAMES,\n OPENAI_REASONING_MODEL_NAMES,\n)\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput, DictInput, DropdownInput, IntInput, SecretStrInput, SliderInput, StrInput\nfrom langflow.logging import logger\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 = [\n *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(\n name=\"model_kwargs\",\n display_name=\"Model Kwargs\",\n advanced=True,\n info=\"Additional keyword arguments to pass to the model.\",\n ),\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 DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n advanced=False,\n options=OPENAI_MODEL_NAMES + OPENAI_REASONING_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[1],\n combobox=True,\n real_time_refresh=True,\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. \"\n \"Defaults to https://api.openai.com/v1. \"\n \"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 required=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n show=True,\n ),\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 IntInput(\n name=\"max_retries\",\n display_name=\"Max Retries\",\n info=\"The maximum number of retries to make when generating.\",\n advanced=True,\n value=5,\n ),\n IntInput(\n name=\"timeout\",\n display_name=\"Timeout\",\n info=\"The timeout for requests to OpenAI completion API.\",\n advanced=True,\n value=700,\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n parameters = {\n \"api_key\": SecretStr(self.api_key).get_secret_value() if self.api_key else None,\n \"model_name\": self.model_name,\n \"max_tokens\": self.max_tokens or None,\n \"model_kwargs\": self.model_kwargs or {},\n \"base_url\": self.openai_api_base or \"https://api.openai.com/v1\",\n \"max_retries\": self.max_retries,\n \"timeout\": self.timeout,\n }\n\n logger.info(f\"Model name: {self.model_name}\")\n if self.model_name not in OPENAI_REASONING_MODEL_NAMES:\n parameters[\"temperature\"] = self.temperature if self.temperature is not None else 0.1\n parameters[\"seed\"] = self.seed\n\n output = ChatOpenAI(**parameters)\n if self.json_mode:\n output = output.bind(response_format={\"type\": \"json_object\"})\n\n return output\n\n def _get_exception_message(self, e: Exception):\n \"\"\"Get a message from an OpenAI exception.\n\n Args:\n e (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n try:\n from openai import BadRequestError\n except ImportError:\n return None\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\")\n if message:\n return message\n return None\n\n def update_build_config(self, build_config: dict, field_value: Any, field_name: str | None = None) -> dict:\n if field_name in {\"base_url\", \"model_name\", \"api_key\"} and field_value in OPENAI_REASONING_MODEL_NAMES:\n build_config[\"temperature\"][\"show\"] = False\n build_config[\"seed\"][\"show\"] = False\n if field_name in {\"base_url\", \"model_name\", \"api_key\"} and field_value in OPENAI_MODEL_NAMES:\n build_config[\"temperature\"][\"show\"] = True\n build_config[\"seed\"][\"show\"] = True\n return build_config\n" - }, - "input_value": { - "_input_type": "MessageInput", - "advanced": false, - "display_name": "Input", - "dynamic": false, - "info": "", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "name": "input_value", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "json_mode": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "JSON Mode", - "dynamic": false, - "info": "If True, it will output JSON regardless of passing a schema.", - "list": false, - "list_add_label": "Add More", - "name": "json_mode", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "max_retries": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Max Retries", - "dynamic": false, - "info": "The maximum number of retries to make when generating.", - "list": false, - "list_add_label": "Add More", - "name": "max_retries", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": 5 - }, - "max_tokens": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Max Tokens", - "dynamic": false, - "info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.", - "list": false, - "list_add_label": "Add More", - "name": "max_tokens", - "placeholder": "", - "range_spec": { - "max": 128000, - "min": 0, - "step": 0.1, - "step_type": "float" - }, - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": "" - }, - "model_kwargs": { - "_input_type": "DictInput", - "advanced": true, - "display_name": "Model Kwargs", - "dynamic": false, - "info": "Additional keyword arguments to pass to the model.", - "list": false, - "list_add_label": "Add More", - "name": "model_kwargs", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "type": "dict", - "value": {} - }, - "model_name": { - "_input_type": "DropdownInput", - "advanced": false, - "combobox": true, - "dialog_inputs": {}, - "display_name": "Model Name", - "dynamic": false, - "info": "", - "name": "model_name", - "options": [ - "gpt-4o-mini", - "gpt-4o", - "gpt-4.1", - "gpt-4.1-mini", - "gpt-4.1-nano", - "gpt-4.5-preview", - "gpt-4-turbo", - "gpt-4-turbo-preview", - "gpt-4", - "gpt-3.5-turbo", - "o1" - ], - "options_metadata": [], - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "gpt-4.1-mini" - }, - "openai_api_base": { - "_input_type": "StrInput", - "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, - "list_add_label": "Add More", - "load_from_db": false, - "name": "openai_api_base", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "seed": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Seed", - "dynamic": false, - "info": "The seed controls the reproducibility of the job.", - "list": false, - "list_add_label": "Add More", - "name": "seed", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": 1 - }, - "stream": { - "_input_type": "BoolInput", - "advanced": true, - "display_name": "Stream", - "dynamic": false, - "info": "Stream the response from the model. Streaming works only in Chat.", - "list": false, - "list_add_label": "Add More", - "name": "stream", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "bool", - "value": false - }, - "system_message": { - "_input_type": "MultilineInput", - "advanced": false, - "display_name": "System Message", - "dynamic": false, - "info": "System message to pass to the model.", - "input_types": [ - "Message" - ], - "list": false, - "list_add_label": "Add More", - "load_from_db": false, - "multiline": true, - "name": "system_message", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_input": true, - "trace_as_metadata": true, - "type": "str", - "value": "" - }, - "temperature": { - "_input_type": "SliderInput", - "advanced": false, - "display_name": "Temperature", - "dynamic": false, - "info": "", - "max_label": "", - "max_label_icon": "", - "min_label": "", - "min_label_icon": "", - "name": "temperature", - "placeholder": "", - "range_spec": { - "max": 1, - "min": 0, - "step": 0.01, - "step_type": "float" - }, - "required": false, - "show": true, - "slider_buttons": false, - "slider_buttons_options": [], - "slider_input": false, - "title_case": false, - "tool_mode": false, - "type": "slider", - "value": 0.1 - }, - "timeout": { - "_input_type": "IntInput", - "advanced": true, - "display_name": "Timeout", - "dynamic": false, - "info": "The timeout for requests to OpenAI completion API.", - "list": false, - "list_add_label": "Add More", - "name": "timeout", - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "tool_mode": false, - "trace_as_metadata": true, - "type": "int", - "value": 700 - } - }, - "tool_mode": false - }, - "selected_output": "text_output", - "showNode": true, - "type": "OpenAIModel" - }, - "dragging": false, - "id": "OpenAIModel-GczRI", - "measured": { - "height": 539, - "width": 320 - }, - "position": { - "x": 2365.714820732046, - "y": 600.6979286268308 - }, - "selected": false, - "type": "genericNode" - }, - { - "data": { - "id": "parser-IEn6I", + "id": "parser-ehrBO", "node": { "base_classes": [ "Message" @@ -3303,7 +2631,7 @@ "type": "parser" }, "dragging": false, - "id": "parser-IEn6I", + "id": "parser-ehrBO", "measured": { "height": 360, "width": 320 @@ -3317,7 +2645,7 @@ }, { "data": { - "id": "AstraDB-Wep9C", + "id": "AstraDB-qCu1f", "node": { "base_classes": [ "Data", @@ -4065,7 +3393,7 @@ "type": "AstraDB" }, "dragging": false, - "id": "AstraDB-Wep9C", + "id": "AstraDB-qCu1f", "measured": { "height": 501, "width": 320 @@ -4074,12 +3402,12 @@ "x": 1206.2272993725155, "y": 491.41485400844977 }, - "selected": true, + "selected": false, "type": "genericNode" }, { "data": { - "id": "AstraDB-8AuVs", + "id": "AstraDB-H2sZ0", "node": { "base_classes": [ "Data", @@ -4825,7 +4153,7 @@ "type": "AstraDB" }, "dragging": false, - "id": "AstraDB-8AuVs", + "id": "AstraDB-H2sZ0", "measured": { "height": 501, "width": 320 @@ -4836,17 +4164,589 @@ }, "selected": false, "type": "genericNode" + }, + { + "data": { + "id": "File-J8GVA", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Loads content from one or more files as a DataFrame.", + "display_name": "File", + "documentation": "", + "edited": false, + "field_order": [ + "path", + "file_path", + "separator", + "silent_errors", + "delete_server_file_after_processing", + "ignore_unsupported_extensions", + "ignore_unspecified_files", + "use_multithreading", + "concurrency_multithreading" + ], + "frozen": false, + "icon": "file-text", + "legacy": false, + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Raw Content", + "group_outputs": false, + "method": "load_files_message", + "name": "message", + "selected": "Message", + "tool_mode": true, + "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 copy import deepcopy\nfrom typing import Any\n\nfrom langflow.base.data.base_file import BaseFileComponent\nfrom langflow.base.data.utils import TEXT_FILE_TYPES, parallel_load_data, parse_text_file_to_data\nfrom langflow.io import BoolInput, FileInput, IntInput, Output\nfrom langflow.schema.data import Data\n\n\nclass FileComponent(BaseFileComponent):\n \"\"\"Handles loading and processing of individual or zipped text files.\n\n This component supports processing multiple valid files within a zip archive,\n resolving paths, validating file types, and optionally using multithreading for processing.\n \"\"\"\n\n display_name = \"File\"\n description = \"Loads content from one or more files as a DataFrame.\"\n icon = \"file-text\"\n name = \"File\"\n\n VALID_EXTENSIONS = TEXT_FILE_TYPES\n\n _base_inputs = deepcopy(BaseFileComponent._base_inputs)\n\n for input_item in _base_inputs:\n if isinstance(input_item, FileInput) and input_item.name == \"path\":\n input_item.real_time_refresh = True\n break\n\n inputs = [\n *_base_inputs,\n BoolInput(\n name=\"use_multithreading\",\n display_name=\"[Deprecated] Use Multithreading\",\n advanced=True,\n value=True,\n info=\"Set 'Processing Concurrency' greater than 1 to enable multithreading.\",\n ),\n IntInput(\n name=\"concurrency_multithreading\",\n display_name=\"Processing Concurrency\",\n advanced=True,\n info=\"When multiple files are being processed, the number of files to process concurrently.\",\n value=1,\n ),\n ]\n\n outputs = [\n Output(display_name=\"Raw Content\", name=\"message\", method=\"load_files_message\"),\n ]\n\n def update_outputs(self, frontend_node: dict, field_name: str, field_value: Any) -> dict:\n \"\"\"Dynamically show only the relevant output based on the number of files processed.\"\"\"\n if field_name == \"path\":\n # Add outputs based on the number of files in the path\n if len(field_value) == 0:\n return frontend_node\n\n frontend_node[\"outputs\"] = []\n\n if len(field_value) == 1:\n # We need to check if the file is structured content\n file_path = frontend_node[\"template\"][\"path\"][\"file_path\"][0]\n if file_path.endswith((\".csv\", \".xlsx\", \".parquet\")):\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Structured Content\", name=\"dataframe\", method=\"load_files_structured\"),\n )\n elif file_path.endswith(\".json\"):\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Structured Content\", name=\"json\", method=\"load_files_json\"),\n )\n\n # All files get the raw content and path outputs\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Raw Content\", name=\"message\", method=\"load_files_message\"),\n )\n frontend_node[\"outputs\"].append(\n Output(display_name=\"File Path\", name=\"path\", method=\"load_files_path\"),\n )\n else:\n # For multiple files, we only show the files output\n frontend_node[\"outputs\"].append(\n Output(display_name=\"Files\", name=\"dataframe\", method=\"load_files\"),\n )\n\n return frontend_node\n\n def process_files(self, file_list: list[BaseFileComponent.BaseFile]) -> list[BaseFileComponent.BaseFile]:\n \"\"\"Processes files either sequentially or in parallel, depending on concurrency settings.\n\n Args:\n file_list (list[BaseFileComponent.BaseFile]): List of files to process.\n\n Returns:\n list[BaseFileComponent.BaseFile]: Updated list of files with merged data.\n \"\"\"\n\n def process_file(file_path: str, *, silent_errors: bool = False) -> Data | None:\n \"\"\"Processes a single file and returns its Data object.\"\"\"\n try:\n return parse_text_file_to_data(file_path, silent_errors=silent_errors)\n except FileNotFoundError as e:\n msg = f\"File not found: {file_path}. Error: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n except Exception as e:\n msg = f\"Unexpected error processing {file_path}: {e}\"\n self.log(msg)\n if not silent_errors:\n raise\n return None\n\n if not file_list:\n msg = \"No files to process.\"\n raise ValueError(msg)\n\n concurrency = 1 if not self.use_multithreading else max(1, self.concurrency_multithreading)\n file_count = len(file_list)\n\n parallel_processing_threshold = 2\n if concurrency < parallel_processing_threshold or file_count < parallel_processing_threshold:\n if file_count > 1:\n self.log(f\"Processing {file_count} files sequentially.\")\n processed_data = [process_file(str(file.path), silent_errors=self.silent_errors) for file in file_list]\n else:\n self.log(f\"Starting parallel processing of {file_count} files with concurrency: {concurrency}.\")\n file_paths = [str(file.path) for file in file_list]\n processed_data = parallel_load_data(\n file_paths,\n silent_errors=self.silent_errors,\n load_function=process_file,\n max_concurrency=concurrency,\n )\n\n # Use rollup_basefile_data to merge processed data with BaseFile objects\n return self.rollup_data(file_list, processed_data)\n" + }, + "concurrency_multithreading": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Processing Concurrency", + "dynamic": false, + "info": "When multiple files are being processed, the number of files to process concurrently.", + "list": false, + "list_add_label": "Add More", + "name": "concurrency_multithreading", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 1 + }, + "delete_server_file_after_processing": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Delete Server File After Processing", + "dynamic": false, + "info": "If true, the Server File Path will be deleted after processing.", + "list": false, + "list_add_label": "Add More", + "name": "delete_server_file_after_processing", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "file_path": { + "_input_type": "HandleInput", + "advanced": true, + "display_name": "Server File Path", + "dynamic": false, + "info": "Data object with a 'file_path' property pointing to server file or a Message object with a path to the file. Supercedes 'Path' but supports same file types.", + "input_types": [ + "Data", + "Message" + ], + "list": true, + "list_add_label": "Add More", + "name": "file_path", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "ignore_unspecified_files": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Ignore Unspecified Files", + "dynamic": false, + "info": "If true, Data with no 'file_path' property will be ignored.", + "list": false, + "list_add_label": "Add More", + "name": "ignore_unspecified_files", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "ignore_unsupported_extensions": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Ignore Unsupported Extensions", + "dynamic": false, + "info": "If true, files with unsupported extensions will not be processed.", + "list": false, + "list_add_label": "Add More", + "name": "ignore_unsupported_extensions", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "path": { + "_input_type": "FileInput", + "advanced": false, + "display_name": "Files", + "dynamic": false, + "fileTypes": [ + "txt", + "md", + "mdx", + "csv", + "json", + "yaml", + "yml", + "xml", + "html", + "htm", + "pdf", + "docx", + "py", + "sh", + "sql", + "js", + "ts", + "tsx", + "zip", + "tar", + "tgz", + "bz2", + "gz" + ], + "file_path": [], + "info": "Supported file extensions: txt, md, mdx, csv, json, yaml, yml, xml, html, htm, pdf, docx, py, sh, sql, js, ts, tsx; optionally bundled in file extensions: zip, tar, tgz, bz2, gz", + "list": true, + "list_add_label": "Add More", + "name": "path", + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "temp_file": false, + "title_case": false, + "trace_as_metadata": true, + "type": "file", + "value": "" + }, + "separator": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "Separator", + "dynamic": false, + "info": "Specify the separator to use between multiple outputs in Message format.", + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "separator", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "\n\n" + }, + "silent_errors": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Silent Errors", + "dynamic": false, + "info": "If true, errors will not raise an exception.", + "list": false, + "list_add_label": "Add More", + "name": "silent_errors", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "use_multithreading": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "[Deprecated] Use Multithreading", + "dynamic": false, + "info": "Set 'Processing Concurrency' greater than 1 to enable multithreading.", + "list": false, + "list_add_label": "Add More", + "name": "use_multithreading", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "File" + }, + "dragging": false, + "id": "File-J8GVA", + "measured": { + "height": 229, + "width": 320 + }, + "position": { + "x": 1330.7650978046952, + "y": 1431.5905495627503 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "LanguageModelComponent-cmjdt", + "node": { + "base_classes": [ + "LanguageModel", + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Runs a language model given a specified provider. ", + "display_name": "Language Model", + "documentation": "", + "edited": false, + "field_order": [ + "provider", + "model_name", + "api_key", + "input_value", + "system_message", + "stream", + "temperature" + ], + "frozen": false, + "icon": "brain-circuit", + "legacy": false, + "metadata": { + "keywords": [ + "model", + "llm", + "language model", + "large language model" + ] + }, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Model Response", + "group_outputs": false, + "method": "text_response", + "name": "text_output", + "options": null, + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Language Model", + "group_outputs": false, + "method": "build_model", + "name": "model_output", + "options": null, + "required_inputs": null, + "selected": "LanguageModel", + "tool_mode": true, + "types": [ + "LanguageModel" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "priority": 0, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "Model Provider API key", + "input_types": [], + "load_from_db": true, + "name": "api_key", + "password": true, + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "OPENAI_API_KEY" + }, + "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 typing import Any\n\nfrom langchain_anthropic import ChatAnthropic\nfrom langchain_google_genai import ChatGoogleGenerativeAI\nfrom langchain_openai import ChatOpenAI\n\nfrom langflow.base.models.anthropic_constants import ANTHROPIC_MODELS\nfrom langflow.base.models.google_generative_ai_constants import GOOGLE_GENERATIVE_AI_MODELS\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.field_typing.range_spec import RangeSpec\nfrom langflow.inputs.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MultilineInput, SecretStrInput, SliderInput\nfrom langflow.schema.dotdict import dotdict\n\n\nclass LanguageModelComponent(LCModelComponent):\n display_name = \"Language Model\"\n description = \"Runs a language model given a specified provider. \"\n icon = \"brain-circuit\"\n category = \"models\"\n priority = 0 # Set priority to 0 to make it appear first\n\n inputs = [\n DropdownInput(\n name=\"provider\",\n display_name=\"Model Provider\",\n options=[\"OpenAI\", \"Anthropic\", \"Google\"],\n value=\"OpenAI\",\n info=\"Select the model provider\",\n real_time_refresh=True,\n options_metadata=[{\"icon\": \"OpenAI\"}, {\"icon\": \"Anthropic\"}, {\"icon\": \"GoogleGenerativeAI\"}],\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n info=\"Select the model to use\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"Model Provider API key\",\n required=False,\n show=True,\n real_time_refresh=True,\n ),\n MessageInput(\n name=\"input_value\",\n display_name=\"Input\",\n info=\"The input text to send to the model\",\n ),\n MultilineInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"A system message that helps set the behavior of the assistant\",\n advanced=True,\n ),\n BoolInput(\n name=\"stream\",\n display_name=\"Stream\",\n info=\"Whether to stream the response\",\n value=False,\n advanced=True,\n ),\n SliderInput(\n name=\"temperature\",\n display_name=\"Temperature\",\n value=0.1,\n info=\"Controls randomness in responses\",\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n advanced=True,\n ),\n ]\n\n def build_model(self) -> LanguageModel:\n provider = self.provider\n model_name = self.model_name\n temperature = self.temperature\n stream = self.stream\n\n if provider == \"OpenAI\":\n if not self.api_key:\n msg = \"OpenAI API key is required when using OpenAI provider\"\n raise ValueError(msg)\n return ChatOpenAI(\n model_name=model_name,\n temperature=temperature,\n streaming=stream,\n openai_api_key=self.api_key,\n )\n if provider == \"Anthropic\":\n if not self.api_key:\n msg = \"Anthropic API key is required when using Anthropic provider\"\n raise ValueError(msg)\n return ChatAnthropic(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n anthropic_api_key=self.api_key,\n )\n if provider == \"Google\":\n if not self.api_key:\n msg = \"Google API key is required when using Google provider\"\n raise ValueError(msg)\n return ChatGoogleGenerativeAI(\n model=model_name,\n temperature=temperature,\n streaming=stream,\n google_api_key=self.api_key,\n )\n msg = f\"Unknown provider: {provider}\"\n raise ValueError(msg)\n\n def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None) -> dotdict:\n if field_name == \"provider\":\n if field_value == \"OpenAI\":\n build_config[\"model_name\"][\"options\"] = OPENAI_MODEL_NAMES\n build_config[\"model_name\"][\"value\"] = OPENAI_MODEL_NAMES[0]\n build_config[\"api_key\"][\"display_name\"] = \"OpenAI API Key\"\n elif field_value == \"Anthropic\":\n build_config[\"model_name\"][\"options\"] = ANTHROPIC_MODELS\n build_config[\"model_name\"][\"value\"] = ANTHROPIC_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Anthropic API Key\"\n elif field_value == \"Google\":\n build_config[\"model_name\"][\"options\"] = GOOGLE_GENERATIVE_AI_MODELS\n build_config[\"model_name\"][\"value\"] = GOOGLE_GENERATIVE_AI_MODELS[0]\n build_config[\"api_key\"][\"display_name\"] = \"Google API Key\"\n return build_config\n" + }, + "input_value": { + "_input_type": "MessageInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "The input text to send to the model", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "Select the model to use", + "name": "model_name", + "options": [ + "gpt-4o-mini", + "gpt-4o", + "gpt-4.1", + "gpt-4.1-mini", + "gpt-4.1-nano", + "gpt-4.5-preview", + "gpt-4-turbo", + "gpt-4-turbo-preview", + "gpt-4", + "gpt-3.5-turbo" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "gpt-4o-mini" + }, + "provider": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", + "dynamic": false, + "info": "Select the model provider", + "name": "provider", + "options": [ + "OpenAI", + "Anthropic", + "Google" + ], + "options_metadata": [ + { + "icon": "OpenAI" + }, + { + "icon": "Anthropic" + }, + { + "icon": "GoogleGenerativeAI" + } + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "toggle": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "OpenAI" + }, + "stream": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Stream", + "dynamic": false, + "info": "Whether to stream the response", + "list": false, + "list_add_label": "Add More", + "name": "stream", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": false + }, + "system_message": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "display_name": "System Message", + "dynamic": false, + "info": "A system message that helps set the behavior of the assistant", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "system_message", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "temperature": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "Temperature", + "dynamic": false, + "info": "Controls randomness in responses", + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "temperature", + "placeholder": "", + "range_spec": { + "max": 1, + "min": 0, + "step": 0.01, + "step_type": "float" + }, + "required": false, + "show": true, + "slider_buttons": false, + "slider_buttons_options": [], + "slider_input": false, + "title_case": false, + "tool_mode": false, + "type": "slider", + "value": 0.1 + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "LanguageModelComponent" + }, + "dragging": false, + "id": "LanguageModelComponent-cmjdt", + "measured": { + "height": 450, + "width": 320 + }, + "position": { + "x": 2354.7612483129965, + "y": 633.8261067248878 + }, + "selected": false, + "type": "genericNode" } ], "viewport": { - "x": -218.5246908040316, - "y": -275.7883822982229, - "zoom": 0.5382133171428375 + "x": -15.798686238525761, + "y": -165.1033705691603, + "zoom": 0.4569209426006492 } }, "description": "Load your data for chat context with Retrieval Augmented Generation.", "endpoint_name": null, - "id": "20809df7-902f-4ea8-b4e5-c395b171633b", + "id": "769ebe18-11b2-4cb3-96a3-eef00d86578e", "is_component": false, "last_tested_version": "1.4.3", "name": "Vector Store RAG", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/document_qa.py b/src/backend/base/langflow/initial_setup/starter_projects/document_qa.py index c73271536..ff40357d4 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/document_qa.py +++ b/src/backend/base/langflow/initial_setup/starter_projects/document_qa.py @@ -1,7 +1,6 @@ from langflow.components.data import FileComponent from langflow.components.input_output import ChatInput, ChatOutput -from langflow.components.languagemodels import OpenAIModelComponent -from langflow.components.processing import ParserComponent +from langflow.components.models import LanguageModelComponent from langflow.components.prompts import PromptComponent from langflow.graph import Graph @@ -22,19 +21,17 @@ Question: Answer: """ file_component = FileComponent() - parse_data_component = ParserComponent() - parse_data_component.set(input_data=file_component.load_files) chat_input = ChatInput() prompt_component = PromptComponent() prompt_component.set( template=template, - context=parse_data_component.parse_combined_text, + context=file_component.load_files_message, question=chat_input.message_response, ) - openai_component = OpenAIModelComponent() - openai_component.set(input_value=prompt_component.build_prompt) + openai_component = LanguageModelComponent() + openai_component.set(input_value=chat_input.message_response, system_message=prompt_component.build_prompt) chat_output = ChatOutput() chat_output.set(input_value=openai_component.text_response) diff --git a/src/backend/base/langflow/initial_setup/starter_projects/vector_store_rag.py b/src/backend/base/langflow/initial_setup/starter_projects/vector_store_rag.py index 437dbee6f..eec1be483 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/vector_store_rag.py +++ b/src/backend/base/langflow/initial_setup/starter_projects/vector_store_rag.py @@ -3,7 +3,7 @@ from textwrap import dedent from langflow.components.data import FileComponent from langflow.components.embeddings import OpenAIEmbeddingsComponent from langflow.components.input_output import ChatInput, ChatOutput -from langflow.components.languagemodels import OpenAIModelComponent +from langflow.components.models import LanguageModelComponent from langflow.components.processing import ParserComponent from langflow.components.processing.split_text import SplitTextComponent from langflow.components.prompts import PromptComponent @@ -15,7 +15,7 @@ def ingestion_graph(): # Ingestion Graph file_component = FileComponent() text_splitter = SplitTextComponent() - text_splitter.set(data_inputs=file_component.load_files) + text_splitter.set(data_inputs=file_component.load_files_message) openai_embeddings = OpenAIEmbeddingsComponent() vector_store = AstraDBVectorStoreComponent() vector_store.set( @@ -49,7 +49,7 @@ def rag_graph(): question=chat_input.message_response, ) - openai_component = OpenAIModelComponent() + openai_component = LanguageModelComponent() openai_component.set(input_value=prompt_component.build_prompt) chat_output = ChatOutput() diff --git a/src/backend/base/langflow/services/storage/local.py b/src/backend/base/langflow/services/storage/local.py index 428593f81..56f026b3f 100644 --- a/src/backend/base/langflow/services/storage/local.py +++ b/src/backend/base/langflow/services/storage/local.py @@ -113,7 +113,7 @@ class LocalStorageService(StorageService): """Perform any cleanup operations when the service is being torn down.""" # No specific teardown actions required for local - async def get_file_size(self, flow_id: str, file_name: str) -> None: + async def get_file_size(self, flow_id: str, file_name: str): """Get the size of a file in the local storage.""" # Get the file size from the file path file_path = self.data_dir / flow_id / file_name diff --git a/src/backend/base/langflow/services/storage/s3.py b/src/backend/base/langflow/services/storage/s3.py index abddfbe91..591622fa3 100644 --- a/src/backend/base/langflow/services/storage/s3.py +++ b/src/backend/base/langflow/services/storage/s3.py @@ -99,3 +99,6 @@ class S3StorageService(StorageService): async def teardown(self) -> None: """Perform any cleanup operations when the service is being torn down.""" # No specific teardown actions required for S3 storage at the moment. + + async def get_file_size(self, flow_id: str, file_name: str): + raise NotImplementedError diff --git a/src/backend/base/langflow/services/storage/service.py b/src/backend/base/langflow/services/storage/service.py index 4cdd2696b..5a1fcef84 100644 --- a/src/backend/base/langflow/services/storage/service.py +++ b/src/backend/base/langflow/services/storage/service.py @@ -39,6 +39,10 @@ class StorageService(Service): async def list_files(self, flow_id: str) -> list[str]: raise NotImplementedError + @abstractmethod + async def get_file_size(self, flow_id: str, file_name: str): + raise NotImplementedError + @abstractmethod async def delete_file(self, flow_id: str, file_name: str) -> None: raise NotImplementedError diff --git a/src/backend/tests/unit/components/data/test_file_component.py b/src/backend/tests/unit/components/data/test_file_component.py new file mode 100644 index 000000000..8666851f2 --- /dev/null +++ b/src/backend/tests/unit/components/data/test_file_component.py @@ -0,0 +1,60 @@ +from langflow.components.data import FileComponent +from langflow.io import Output + + +class TestFileComponentDynamicOutputs: + def test_update_outputs_single_csv_file(self): + """Test single CSV file shows structured + raw outputs.""" + component = FileComponent() + frontend_node = {"outputs": [], "template": {"path": {"file_path": ["test.csv"]}}} + + result = component.update_outputs(frontend_node, "path", ["test.csv"]) + + assert len(result["outputs"]) == 3 + output_names = [output.name for output in result["outputs"]] + assert "dataframe" in output_names # Structured content + assert "message" in output_names # Raw content + assert "path" in output_names # File path + + def test_update_outputs_single_json_file(self): + """Test single JSON file shows JSON + raw outputs.""" + component = FileComponent() + frontend_node = {"outputs": [], "template": {"path": {"file_path": ["data.json"]}}} + + result = component.update_outputs(frontend_node, "path", ["data.json"]) + + assert len(result["outputs"]) == 3 + output_names = [output.name for output in result["outputs"]] + assert "json" in output_names # JSON content + assert "message" in output_names # Raw content + assert "path" in output_names # File path + + def test_update_outputs_multiple_files(self): + """Test multiple files show only Files output.""" + component = FileComponent() + frontend_node = {"outputs": [], "template": {"path": {"file_path": ["file1.txt", "file2.txt"]}}} + + result = component.update_outputs(frontend_node, "path", ["file1.txt", "file2.txt"]) + + assert len(result["outputs"]) == 1 + assert result["outputs"][0].name == "dataframe" + assert result["outputs"][0].display_name == "Files" + + def test_update_outputs_empty_path(self): + """Test empty path results in no outputs.""" + component = FileComponent() + frontend_node = {"outputs": [], "template": {"path": {"file_path": []}}} + + result = component.update_outputs(frontend_node, "path", []) + + assert len(result["outputs"]) == 0 + + def test_update_outputs_non_path_field(self): + """Test non-path fields don't affect outputs.""" + component = FileComponent() + original_outputs = [Output(display_name="Test", name="test", method="test_method")] + frontend_node = {"outputs": original_outputs, "template": {"path": {"file_path": ["value"]}}} + + result = component.update_outputs(frontend_node, "other_field", "value") + + assert result["outputs"] == original_outputs diff --git a/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py b/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py index 9c16f8899..ff6c29a2b 100644 --- a/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py +++ b/src/backend/tests/unit/initial_setup/starter_projects/test_vector_store_rag.py @@ -15,6 +15,7 @@ from langflow.graph.graph.base import Graph from langflow.graph.graph.constants import Finish from langflow.schema import Data from langflow.schema.dataframe import DataFrame +from langflow.schema.message import Message @pytest.fixture @@ -22,9 +23,9 @@ def ingestion_graph(): # Ingestion Graph file_component = FileComponent(_id="file-123") file_component.set(path="test.txt") - file_component.set_on_output(name="dataframe", value=Data(text="This is a test file."), cache=True) + file_component.set_on_output(name="message", value=Message(text="This is a test file."), cache=True) text_splitter = SplitTextComponent(_id="text-splitter-123") - text_splitter.set(data_inputs=file_component.load_files) + text_splitter.set(data_inputs=file_component.load_files_message) openai_embeddings = OpenAIEmbeddingsComponent(_id="openai-embeddings-123") openai_embeddings.set( openai_api_key="sk-123", openai_api_base="https://api.openai.com/v1", openai_api_type="openai" diff --git a/src/frontend/tests/core/features/stop-building.spec.ts b/src/frontend/tests/core/features/stop-building.spec.ts index ddbfba910..e8bdf6818 100644 --- a/src/frontend/tests/core/features/stop-building.spec.ts +++ b/src/frontend/tests/core/features/stop-building.spec.ts @@ -82,9 +82,7 @@ test( await page .getByTestId("handle-urlcomponent-shownode-extracted pages-right") .click(); - await page - .getByTestId("handle-splittext-shownode-data or dataframe-left") - .click(); + await page.getByTestId("handle-splittext-shownode-input-left").click(); //connection 2 await page diff --git a/src/frontend/tests/core/unit/fileUploadComponent.spec.ts b/src/frontend/tests/core/unit/fileUploadComponent.spec.ts index cc26002ce..354e6c6d7 100644 --- a/src/frontend/tests/core/unit/fileUploadComponent.spec.ts +++ b/src/frontend/tests/core/unit/fileUploadComponent.spec.ts @@ -250,33 +250,8 @@ test( await adjustScreenView(page); - await page - .getByTestId("handle-file-shownode-loaded files-right") - .first() - .click(); + await page.getByTestId("handle-file-shownode-files-right").first().click(); - await page - .getByTestId("processingParser") - .hover() - .then(async () => { - await page.getByTestId("add-component-button-parser").click(); - }); - - await adjustScreenView(page); - await page - .getByTestId("handle-file-shownode-loaded files-right") - .first() - .click(); - - await page - .getByTestId("handle-parsercomponent-shownode-data or dataframe-left") - .first() - .click(); - - await page - .getByTestId("handle-parsercomponent-shownode-parsed text-right") - .first() - .click(); await page .getByTestId("handle-chatoutput-noshownode-inputs-target") .first() @@ -348,6 +323,17 @@ test( timeout: 1000, }); await page.getByTestId(`remove-file-button-${renamedTxtFile}`).click(); + + await page + .getByTestId("handle-file-shownode-raw content-right") + .first() + .click(); + + await page + .getByTestId("handle-chatoutput-noshownode-inputs-target") + .first() + .click(); + await page .getByRole("button", { name: "Playground", exact: true }) .click();