langflow/src/backend/base/langflow/initial_setup/starter_projects/Blog Writer.json
Gabriel Luiz Freitas Almeida 0dc6cce8dc
tests: Enhance component testing and error handling with dynamic retrieval (#4526)
* Add module parameter to build_component_instance_for_tests function for dynamic component retrieval

* Enhance component test base with detailed version mapping and error handling

- Introduced `VersionComponentMapping` TypedDict for structured version mapping.
- Updated `FILE_NAMES_MAPPING` to use a list of `VersionComponentMapping`.
- Added comprehensive error messages for missing or invalid mappings in `test_all_versions_have_a_file_name_defined`.
- Improved `test_component_versions` with detailed exception handling and error reporting.
- Ensured `component_class` is defined before running tests.

* Refactor FILE_NAMES_MAPPING to use a list of dictionaries for better structure and readability in test_prompt_component.py

* refactor: Enhance ComponentTestBase with fixture validation and improved version handling

* Refactor test setup in `test_prompt_component.py` to use fixtures for improved modularity and readability

* fix: Add PlaceholderGraph NamedTuple and handle 'graph' attribute in Component class

* Add attribute checks for 'graph' and 'vertex' to prevent errors

* Handle missing 'graph' attribute in 'store_message' method to prevent errors.

* Handle missing 'graph' attribute in Message creation to prevent errors

* Handle missing 'graph' attribute in chat message flow ID assignment

* Add component code to test instance creation and error logging

* Update SUPPORTED_VERSIONS to remove older versions

* test: add unit tests for ChatInput and TextInputComponent

Implement comprehensive tests for both ChatInput and TextInputComponent to ensure proper functionality, including message responses and handling of various input scenarios. This enhances reliability and aids in future development.

* test: add unit tests for ChatOutput and TextOutputComponent

Implement comprehensive tests for ChatOutput and TextOutputComponent, validating message responses, source properties, and behavior with various input types to ensure reliability and consistency across output components.

* Update JSON files to improve code readability and add missing info fields

- Added missing `info` fields to various input components to provide better context and descriptions.
- Improved code readability by ensuring consistent formatting and structure across JSON files.
- Updated `message_response` method to handle cases where `graph` attribute might not be present.
- Enhanced `build_vectorize_options` method to set `authentication` and `parameters` to `None` if no values are provided.
- Refined `AgentComponent` to include `info` for `agent_llm` and other fields, improving clarity on their purpose.

* Refactor: update attribute access to use private `_vertex` attribute

* test: enhance TextInputComponent tests and update properties assertions

* Remove redundant unit tests for output components in test_output_components.py

* feat: add PlaceholderGraph for backwards compatibility and enhance Component attributes

* fix: improve run_id assignment and ensure user_id is a string in PlaceholderGraph

* Add check for non-empty incoming_edges in get_properties_from_source_component
2024-11-12 10:50:56 -08:00

1244 lines
No EOL
58 KiB
JSON

{
"data": {
"edges": [
{
"className": "",
"data": {
"sourceHandle": {
"dataType": "URL",
"id": "URL-v3FkJ",
"name": "data",
"output_types": [
"Data"
]
},
"targetHandle": {
"fieldName": "data",
"id": "ParseData-qffHj",
"inputTypes": [
"Data"
],
"type": "other"
}
},
"id": "reactflow__edge-URL-v3FkJ{œdataTypeœ:œURLœ,œidœ:œURL-v3FkJœ,œnameœ:œdataœ,œoutput_typesœ:[œDataœ]}-ParseData-qffHj{œfieldNameœ:œdataœ,œidœ:œParseData-qffHjœ,œinputTypesœ:[œDataœ],œtypeœ:œotherœ}",
"source": "URL-v3FkJ",
"sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-v3FkJœ, œnameœ: œdataœ, œoutput_typesœ: [œDataœ]}",
"target": "ParseData-qffHj",
"targetHandle": "{œfieldNameœ: œdataœ, œidœ: œParseData-qffHjœ, œinputTypesœ: [œDataœ], œtypeœ: œotherœ}"
},
{
"className": "",
"data": {
"sourceHandle": {
"dataType": "ParseData",
"id": "ParseData-qffHj",
"name": "text",
"output_types": [
"Message"
]
},
"targetHandle": {
"fieldName": "references",
"id": "Prompt-ztjwI",
"inputTypes": [
"Message",
"Text"
],
"type": "str"
}
},
"id": "reactflow__edge-ParseData-qffHj{œdataTypeœ:œParseDataœ,œidœ:œParseData-qffHjœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-ztjwI{œfieldNameœ:œreferencesœ,œidœ:œPrompt-ztjwIœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
"source": "ParseData-qffHj",
"sourceHandle": "{œdataTypeœ: œParseDataœ, œidœ: œParseData-qffHjœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
"target": "Prompt-ztjwI",
"targetHandle": "{œfieldNameœ: œreferencesœ, œidœ: œPrompt-ztjwIœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
},
{
"className": "",
"data": {
"sourceHandle": {
"dataType": "TextInput",
"id": "TextInput-A7goL",
"name": "text",
"output_types": [
"Message"
]
},
"targetHandle": {
"fieldName": "instructions",
"id": "Prompt-ztjwI",
"inputTypes": [
"Message",
"Text"
],
"type": "str"
}
},
"id": "reactflow__edge-TextInput-A7goL{œdataTypeœ:œTextInputœ,œidœ:œTextInput-A7goLœ,œnameœ:œtextœ,œoutput_typesœ:[œMessageœ]}-Prompt-ztjwI{œfieldNameœ:œinstructionsœ,œidœ:œPrompt-ztjwIœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}",
"source": "TextInput-A7goL",
"sourceHandle": "{œdataTypeœ: œTextInputœ, œidœ: œTextInput-A7goLœ, œnameœ: œtextœ, œoutput_typesœ: [œMessageœ]}",
"target": "Prompt-ztjwI",
"targetHandle": "{œfieldNameœ: œinstructionsœ, œidœ: œPrompt-ztjwIœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}"
},
{
"className": "",
"data": {
"sourceHandle": {
"dataType": "Prompt",
"id": "Prompt-ztjwI",
"name": "prompt",
"output_types": [
"Message"
]
},
"targetHandle": {
"fieldName": "input_value",
"id": "OpenAIModel-Y3GUP",
"inputTypes": [
"Message"
],
"type": "str"
}
},
"id": "reactflow__edge-Prompt-ztjwI{œdataTypeœ:œPromptœ,œidœ:œPrompt-ztjwIœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-OpenAIModel-Y3GUP{œfieldNameœ:œinput_valueœ,œidœ:œOpenAIModel-Y3GUPœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
"source": "Prompt-ztjwI",
"sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-ztjwIœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}",
"target": "OpenAIModel-Y3GUP",
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œOpenAIModel-Y3GUPœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
},
{
"className": "",
"data": {
"sourceHandle": {
"dataType": "OpenAIModel",
"id": "OpenAIModel-Y3GUP",
"name": "text_output",
"output_types": [
"Message"
]
},
"targetHandle": {
"fieldName": "input_value",
"id": "ChatOutput-Y7FC3",
"inputTypes": [
"Message"
],
"type": "str"
}
},
"id": "reactflow__edge-OpenAIModel-Y3GUP{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Y3GUPœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-Y7FC3{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-Y7FC3œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}",
"source": "OpenAIModel-Y3GUP",
"sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-Y3GUPœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}",
"target": "ChatOutput-Y7FC3",
"targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-Y7FC3œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}"
}
],
"nodes": [
{
"data": {
"description": "Fetch content from one or more URLs.",
"display_name": "URL",
"id": "URL-v3FkJ",
"node": {
"base_classes": [
"Data"
],
"beta": false,
"conditional_paths": [],
"custom_fields": {},
"description": "Fetch content from one or more URLs.",
"display_name": "URL",
"documentation": "",
"edited": false,
"field_order": [
"urls"
],
"frozen": false,
"icon": "layout-template",
"legacy": false,
"metadata": {},
"output_types": [],
"outputs": [
{
"cache": true,
"display_name": "Data",
"method": "fetch_content",
"name": "data",
"selected": "Data",
"types": [
"Data"
],
"value": "__UNDEFINED__"
},
{
"cache": true,
"display_name": "Text",
"method": "fetch_content_text",
"name": "text",
"selected": "Message",
"types": [
"Message"
],
"value": "__UNDEFINED__"
}
],
"pinned": false,
"template": {
"_type": "Component",
"code": {
"advanced": true,
"dynamic": true,
"fileTypes": [],
"file_path": "",
"info": "",
"list": false,
"load_from_db": false,
"multiline": true,
"name": "code",
"password": false,
"placeholder": "",
"required": true,
"show": true,
"title_case": false,
"type": "code",
"value": "import re\n\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DropdownInput, MessageTextInput, Output\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = \"Fetch content from one or more URLs.\"\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n info=\"Enter one or more URLs, by clicking the '+' button.\",\n is_list=True,\n tool_mode=True,\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output format\",\n info=\"Output format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.\",\n options=[\"Text\", \"Raw HTML\"],\n value=\"Text\",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n ]\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a URL by adding 'http://' if it doesn't start with 'http://' or 'https://'.\n\n Raises an error if the string is not a valid URL.\n\n Parameters:\n string (str): The string to be checked and possibly modified.\n\n Returns:\n str: The modified string that is ensured to be a URL.\n\n Raises:\n ValueError: If the string is not a valid URL.\n \"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n # Basic URL validation regex\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\" # optional protocol\n r\"(www\\.)?\" # optional www\n r\"([a-zA-Z0-9.-]+)\" # domain\n r\"(\\.[a-zA-Z]{2,})?\" # top-level domain\n r\"(:\\d+)?\" # optional port\n r\"(\\/[^\\s]*)?$\", # optional path\n re.IGNORECASE,\n )\n\n if not url_regex.match(string):\n msg = f\"Invalid URL: {string}\"\n raise ValueError(msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n urls = [self.ensure_url(url.strip()) for url in self.urls if url.strip()]\n if self.format == \"Raw HTML\":\n loader = AsyncHtmlLoader(web_path=urls, encoding=\"utf-8\")\n else:\n loader = WebBaseLoader(web_paths=urls, encoding=\"utf-8\")\n docs = loader.load()\n data = [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n self.status = data\n return data\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n"
},
"format": {
"_input_type": "DropdownInput",
"advanced": false,
"combobox": false,
"display_name": "Output format",
"dynamic": false,
"info": "Output format. Use 'Text' to extract the text from the HTML or 'Raw HTML' for the raw HTML content.",
"name": "format",
"options": [
"Text",
"Raw HTML"
],
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "str",
"value": "Text"
},
"urls": {
"advanced": false,
"display_name": "URLs",
"dynamic": false,
"info": "Enter one or more URLs, by clicking the '+' button.",
"input_types": [
"Message"
],
"list": true,
"load_from_db": false,
"name": "urls",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "str",
"value": [
"langflow.org/",
"docs.langflow.org/"
]
}
}
},
"type": "URL"
},
"dragging": false,
"height": 465,
"id": "URL-v3FkJ",
"position": {
"x": 220.79156431407534,
"y": 498.8186168722667
},
"positionAbsolute": {
"x": 220.79156431407534,
"y": 498.8186168722667
},
"selected": false,
"type": "genericNode",
"width": 384
},
{
"data": {
"description": "Convert Data into plain text following a specified template.",
"display_name": "Parse Data",
"id": "ParseData-qffHj",
"node": {
"base_classes": [
"Message"
],
"beta": false,
"conditional_paths": [],
"custom_fields": {},
"description": "Convert Data into plain text following a specified template.",
"display_name": "Parse Data",
"documentation": "",
"edited": false,
"field_order": [
"data",
"template",
"sep"
],
"frozen": false,
"icon": "braces",
"legacy": false,
"metadata": {},
"output_types": [],
"outputs": [
{
"cache": true,
"display_name": "Text",
"method": "parse_data",
"name": "text",
"selected": "Message",
"types": [
"Message"
],
"value": "__UNDEFINED__"
}
],
"pinned": false,
"template": {
"_type": "Component",
"code": {
"advanced": true,
"dynamic": true,
"fileTypes": [],
"file_path": "",
"info": "",
"list": false,
"load_from_db": false,
"multiline": true,
"name": "code",
"password": false,
"placeholder": "",
"required": true,
"show": true,
"title_case": false,
"type": "code",
"value": "from langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import DataInput, MultilineInput, Output, StrInput\nfrom langflow.schema.message import Message\n\n\nclass ParseDataComponent(Component):\n display_name = \"Parse Data\"\n description = \"Convert Data into plain text following a specified template.\"\n icon = \"braces\"\n name = \"ParseData\"\n\n inputs = [\n DataInput(name=\"data\", display_name=\"Data\", info=\"The data to convert to text.\"),\n MultilineInput(\n name=\"template\",\n display_name=\"Template\",\n info=\"The template to use for formatting the data. \"\n \"It can contain the keys {text}, {data} or any other key in the Data.\",\n value=\"{text}\",\n ),\n StrInput(name=\"sep\", display_name=\"Separator\", advanced=True, value=\"\\n\"),\n ]\n\n outputs = [\n Output(display_name=\"Text\", name=\"text\", method=\"parse_data\"),\n ]\n\n def parse_data(self) -> Message:\n data = self.data if isinstance(self.data, list) else [self.data]\n template = self.template\n\n result_string = data_to_text(template, data, sep=self.sep)\n self.status = result_string\n return Message(text=result_string)\n"
},
"data": {
"advanced": false,
"display_name": "Data",
"dynamic": false,
"info": "The data to convert to text.",
"input_types": [
"Data"
],
"list": false,
"name": "data",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "other",
"value": ""
},
"sep": {
"advanced": true,
"display_name": "Separator",
"dynamic": false,
"info": "",
"list": false,
"load_from_db": false,
"name": "sep",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "str",
"value": "\n"
},
"template": {
"advanced": false,
"display_name": "Template",
"dynamic": false,
"info": "The template to use for formatting the data. It can contain the keys {text}, {data} or any other key in the Data.",
"input_types": [
"Message"
],
"list": false,
"load_from_db": false,
"multiline": true,
"name": "template",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "str",
"value": "{text}"
}
}
},
"type": "ParseData"
},
"dragging": false,
"height": 353,
"id": "ParseData-qffHj",
"position": {
"x": 754.3607306709101,
"y": 736.8516961537598
},
"positionAbsolute": {
"x": 754.3607306709101,
"y": 736.8516961537598
},
"selected": false,
"type": "genericNode",
"width": 384
},
{
"data": {
"description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt",
"id": "Prompt-ztjwI",
"node": {
"base_classes": [
"Message"
],
"beta": false,
"conditional_paths": [],
"custom_fields": {
"template": [
"references",
"instructions"
]
},
"description": "Create a prompt template with dynamic variables.",
"display_name": "Prompt",
"documentation": "",
"edited": false,
"field_order": [
"template"
],
"frozen": false,
"icon": "prompts",
"legacy": false,
"metadata": {},
"output_types": [],
"outputs": [
{
"cache": true,
"display_name": "Prompt Message",
"method": "build_prompt",
"name": "prompt",
"selected": "Message",
"types": [
"Message"
],
"value": "__UNDEFINED__"
}
],
"pinned": false,
"template": {
"_type": "Component",
"code": {
"advanced": true,
"dynamic": true,
"fileTypes": [],
"file_path": "",
"info": "",
"list": false,
"load_from_db": false,
"multiline": true,
"name": "code",
"password": false,
"placeholder": "",
"required": true,
"show": true,
"title_case": false,
"type": "code",
"value": "from langflow.base.prompts.api_utils import process_prompt_template\nfrom langflow.custom import Component\nfrom langflow.inputs.inputs import DefaultPromptField\nfrom langflow.io import Output, PromptInput\nfrom langflow.schema.message import Message\nfrom langflow.template.utils import update_template_values\n\n\nclass PromptComponent(Component):\n display_name: str = \"Prompt\"\n description: str = \"Create a prompt template with dynamic variables.\"\n icon = \"prompts\"\n trace_type = \"prompt\"\n name = \"Prompt\"\n\n inputs = [\n PromptInput(name=\"template\", display_name=\"Template\"),\n ]\n\n outputs = [\n Output(display_name=\"Prompt Message\", name=\"prompt\", method=\"build_prompt\"),\n ]\n\n async def build_prompt(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 def post_code_processing(self, new_frontend_node: dict, current_frontend_node: dict):\n \"\"\"This function is called after the code validation is done.\"\"\"\n frontend_node = super().post_code_processing(new_frontend_node, current_frontend_node)\n template = frontend_node[\"template\"][\"template\"][\"value\"]\n # Kept it duplicated for backwards compatibility\n _ = process_prompt_template(\n template=template,\n name=\"template\",\n custom_fields=frontend_node[\"custom_fields\"],\n frontend_node_template=frontend_node[\"template\"],\n )\n # Now that template is updated, we need to grab any values that were set in the current_frontend_node\n # and update the frontend_node with those values\n update_template_values(new_template=frontend_node, previous_template=current_frontend_node[\"template\"])\n return frontend_node\n\n def _get_fallback_input(self, **kwargs):\n return DefaultPromptField(**kwargs)\n"
},
"instructions": {
"advanced": false,
"display_name": "instructions",
"dynamic": false,
"field_type": "str",
"fileTypes": [],
"file_path": "",
"info": "",
"input_types": [
"Message",
"Text"
],
"list": false,
"load_from_db": false,
"multiline": true,
"name": "instructions",
"password": false,
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"type": "str",
"value": ""
},
"references": {
"advanced": false,
"display_name": "references",
"dynamic": false,
"field_type": "str",
"fileTypes": [],
"file_path": "",
"info": "",
"input_types": [
"Message",
"Text"
],
"list": false,
"load_from_db": false,
"multiline": true,
"name": "references",
"password": false,
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"type": "str",
"value": ""
},
"template": {
"advanced": false,
"display_name": "Template",
"dynamic": false,
"info": "",
"list": false,
"load_from_db": false,
"name": "template",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"type": "prompt",
"value": "Reference 1:\n\n{references}\n\n---\n\n{instructions}\n\nBlog: \n\n"
}
}
},
"type": "Prompt"
},
"dragging": false,
"height": 477,
"id": "Prompt-ztjwI",
"position": {
"x": 1368.0633591447076,
"y": 467.19448061224284
},
"positionAbsolute": {
"x": 1368.0633591447076,
"y": 467.19448061224284
},
"selected": false,
"type": "genericNode",
"width": 384
},
{
"data": {
"description": "Get text inputs from the Playground.",
"display_name": "Instructions",
"id": "TextInput-A7goL",
"node": {
"base_classes": [
"Message"
],
"beta": false,
"conditional_paths": [],
"custom_fields": {},
"description": "Get text inputs from the Playground.",
"display_name": "Instructions",
"documentation": "",
"edited": false,
"field_order": [
"input_value"
],
"frozen": false,
"icon": "type",
"metadata": {},
"output_types": [],
"outputs": [
{
"cache": true,
"display_name": "Text",
"method": "text_response",
"name": "text",
"selected": "Message",
"types": [
"Message"
],
"value": "__UNDEFINED__"
}
],
"pinned": false,
"template": {
"_type": "Component",
"code": {
"advanced": true,
"dynamic": true,
"fileTypes": [],
"file_path": "",
"info": "",
"list": false,
"load_from_db": false,
"multiline": true,
"name": "code",
"password": false,
"placeholder": "",
"required": true,
"show": true,
"title_case": false,
"type": "code",
"value": "from langflow.base.io.text import TextComponent\nfrom langflow.io import MultilineInput, Output\nfrom langflow.schema.message import Message\n\n\nclass TextInputComponent(TextComponent):\n display_name = \"Text Input\"\n description = \"Get text inputs from the Playground.\"\n icon = \"type\"\n name = \"TextInput\"\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Text to be passed as input.\",\n ),\n ]\n outputs = [\n Output(display_name=\"Text\", name=\"text\", method=\"text_response\"),\n ]\n\n def text_response(self) -> Message:\n return Message(\n text=self.input_value,\n )\n"
},
"input_value": {
"_input_type": "MultilineInput",
"advanced": false,
"display_name": "Text",
"dynamic": false,
"info": "Text to be passed as input.",
"input_types": [
"Message"
],
"list": false,
"load_from_db": false,
"multiline": true,
"name": "input_value",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "str",
"value": "Use the references above for style to write a new blog/tutorial about Langflow and AI. Suggest non-covered topics."
}
}
},
"type": "TextInput"
},
"dragging": false,
"height": 289,
"id": "TextInput-A7goL",
"position": {
"x": 743.7338453293725,
"y": 301.58775454952183
},
"positionAbsolute": {
"x": 743.7338453293725,
"y": 301.58775454952183
},
"selected": false,
"type": "genericNode",
"width": 384
},
{
"data": {
"description": "Display a chat message in the Playground.",
"display_name": "Chat Output",
"id": "ChatOutput-Y7FC3",
"node": {
"base_classes": [
"Message"
],
"beta": false,
"conditional_paths": [],
"custom_fields": {},
"description": "Display a chat message in the Playground.",
"display_name": "Chat Output",
"documentation": "",
"edited": false,
"field_order": [
"input_value",
"store_message",
"sender",
"sender_name",
"session_id",
"data_template"
],
"frozen": false,
"icon": "MessagesSquare",
"legacy": false,
"metadata": {},
"output_types": [],
"outputs": [
{
"cache": true,
"display_name": "Message",
"method": "message_response",
"name": "message",
"selected": "Message",
"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,
"load_from_db": false,
"name": "background_color",
"placeholder": "",
"required": false,
"show": true,
"title_case": 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,
"load_from_db": false,
"name": "chat_icon",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"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 langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import DropdownInput, MessageInput, MessageTextInput, Output\nfrom langflow.schema.message import Message\nfrom langflow.schema.properties import Source\nfrom langflow.utils.constants import MESSAGE_SENDER_AI, MESSAGE_SENDER_NAME_AI, MESSAGE_SENDER_USER\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\n inputs = [\n MessageInput(\n name=\"input_value\",\n display_name=\"Text\",\n info=\"Message to be passed as output.\",\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_AI,\n advanced=True,\n info=\"Type of sender.\",\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_AI,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"data_template\",\n display_name=\"Data Template\",\n value=\"{text}\",\n advanced=True,\n info=\"Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.\",\n ),\n 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 ]\n outputs = [\n Output(\n display_name=\"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 source_dict[\"source\"] = source\n return Source(**source_dict)\n\n def message_response(self) -> Message:\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 message = self.input_value if isinstance(self.input_value, Message) else Message(text=self.input_value)\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 if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = self.send_message(\n message,\n )\n self.message.value = stored_message\n message = stored_message\n\n self.status = message\n return message\n"
},
"data_template": {
"advanced": true,
"display_name": "Data Template",
"dynamic": false,
"info": "Template to convert Data to Text. If left empty, it will be dynamically set to the Data's text key.",
"input_types": [
"Message"
],
"list": false,
"load_from_db": false,
"name": "data_template",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "str",
"value": "{text}"
},
"input_value": {
"advanced": false,
"display_name": "Text",
"dynamic": false,
"info": "Message to be passed as output.",
"input_types": [
"Message"
],
"list": false,
"load_from_db": false,
"name": "input_value",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "str",
"value": ""
},
"sender": {
"advanced": true,
"display_name": "Sender Type",
"dynamic": false,
"info": "Type of sender.",
"name": "sender",
"options": [
"Machine",
"User"
],
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "str",
"value": "Machine"
},
"sender_name": {
"advanced": true,
"display_name": "Sender Name",
"dynamic": false,
"info": "Name of the sender.",
"input_types": [
"Message"
],
"list": false,
"load_from_db": false,
"name": "sender_name",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "str",
"value": "AI"
},
"session_id": {
"advanced": true,
"display_name": "Session ID",
"dynamic": false,
"info": "The session ID of the chat. If empty, the current session ID parameter will be used.",
"input_types": [
"Message"
],
"list": false,
"load_from_db": false,
"name": "session_id",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "str",
"value": ""
},
"should_store_message": {
"_input_type": "BoolInput",
"advanced": true,
"display_name": "Store Messages",
"dynamic": false,
"info": "Store the message in the history.",
"list": false,
"name": "should_store_message",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "bool",
"value": true
},
"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,
"load_from_db": false,
"name": "text_color",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "str",
"value": ""
}
}
},
"type": "ChatOutput"
},
"dragging": false,
"height": 289,
"id": "ChatOutput-Y7FC3",
"position": {
"x": 2449.3489426461606,
"y": 571.2449700910389
},
"positionAbsolute": {
"x": 2449.3489426461606,
"y": 571.2449700910389
},
"selected": false,
"type": "genericNode",
"width": 384
},
{
"data": {
"description": "Generates text using OpenAI LLMs.",
"display_name": "OpenAI",
"id": "OpenAIModel-Y3GUP",
"node": {
"base_classes": [
"LanguageModel",
"Message"
],
"beta": false,
"conditional_paths": [],
"custom_fields": {},
"description": "Generates text using OpenAI LLMs.",
"display_name": "OpenAI",
"documentation": "",
"edited": false,
"field_order": [
"input_value",
"max_tokens",
"model_kwargs",
"json_mode",
"output_schema",
"model_name",
"openai_api_base",
"openai_api_key",
"temperature",
"stream",
"system_message",
"seed"
],
"frozen": false,
"icon": "OpenAI",
"legacy": false,
"metadata": {},
"output_types": [],
"outputs": [
{
"cache": true,
"display_name": "Text",
"method": "text_response",
"name": "text_output",
"required_inputs": [],
"selected": "Message",
"types": [
"Message"
],
"value": "__UNDEFINED__"
},
{
"cache": true,
"display_name": "Language Model",
"method": "build_model",
"name": "model_output",
"required_inputs": [],
"selected": "LanguageModel",
"types": [
"LanguageModel"
],
"value": "__UNDEFINED__"
}
],
"pinned": false,
"template": {
"_type": "Component",
"api_key": {
"_input_type": "SecretStrInput",
"advanced": false,
"display_name": "OpenAI API Key",
"dynamic": false,
"info": "The OpenAI API Key to use for the OpenAI model.",
"input_types": [
"Message"
],
"load_from_db": true,
"name": "api_key",
"password": true,
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"type": "str",
"value": "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": "import operator\nfrom functools import reduce\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 OPENAI_MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.inputs import BoolInput, DictInput, DropdownInput, FloatInput, IntInput, SecretStrInput, StrInput\nfrom langflow.inputs.inputs import HandleInput\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 DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. \"\n \"You must pass the word JSON in the prompt. \"\n \"If left blank, JSON mode will be disabled. [DEPRECATED]\",\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n advanced=False,\n options=OPENAI_MODEL_NAMES,\n value=OPENAI_MODEL_NAMES[0],\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n info=\"The base URL of the OpenAI API. \"\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 ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n HandleInput(\n name=\"output_parser\",\n display_name=\"Output Parser\",\n info=\"The parser to use to parse the output of the model\",\n advanced=True,\n input_types=[\"OutputParser\"],\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n # self.output_schema is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n\n api_key = SecretStr(openai_api_key).get_secret_value() if openai_api_key else None\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature if temperature is not None else 0.1,\n seed=seed,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\")\n else:\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"
},
"input_value": {
"advanced": false,
"display_name": "Input",
"dynamic": false,
"info": "",
"input_types": [
"Message"
],
"list": false,
"load_from_db": false,
"name": "input_value",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"trace_as_metadata": true,
"type": "str",
"value": ""
},
"json_mode": {
"advanced": true,
"display_name": "JSON Mode",
"dynamic": false,
"info": "If True, it will output JSON regardless of passing a schema.",
"list": false,
"name": "json_mode",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "bool",
"value": false
},
"max_tokens": {
"advanced": true,
"display_name": "Max Tokens",
"dynamic": false,
"info": "The maximum number of tokens to generate. Set to 0 for unlimited tokens.",
"list": false,
"name": "max_tokens",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "int",
"value": ""
},
"model_kwargs": {
"advanced": true,
"display_name": "Model Kwargs",
"dynamic": false,
"info": "Additional keyword arguments to pass to the model.",
"list": false,
"name": "model_kwargs",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"type": "dict",
"value": {}
},
"model_name": {
"advanced": false,
"display_name": "Model Name",
"dynamic": false,
"info": "",
"name": "model_name",
"options": [
"gpt-4o-mini",
"gpt-4o",
"gpt-4-turbo",
"gpt-4-turbo-preview",
"gpt-4",
"gpt-3.5-turbo",
"gpt-3.5-turbo-0125"
],
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "str",
"value": "gpt-4o"
},
"openai_api_base": {
"advanced": true,
"display_name": "OpenAI API Base",
"dynamic": false,
"info": "The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.",
"list": false,
"load_from_db": false,
"name": "openai_api_base",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "str",
"value": ""
},
"output_parser": {
"_input_type": "HandleInput",
"advanced": true,
"display_name": "Output Parser",
"dynamic": false,
"info": "The parser to use to parse the output of the model",
"input_types": [
"OutputParser"
],
"list": false,
"name": "output_parser",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "other",
"value": ""
},
"output_schema": {
"advanced": true,
"display_name": "Schema",
"dynamic": false,
"info": "The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled. [DEPRECATED]",
"list": true,
"name": "output_schema",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_input": true,
"type": "dict",
"value": {}
},
"seed": {
"advanced": true,
"display_name": "Seed",
"dynamic": false,
"info": "The seed controls the reproducibility of the job.",
"list": false,
"name": "seed",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "int",
"value": 1
},
"stream": {
"advanced": true,
"display_name": "Stream",
"dynamic": false,
"info": "Stream the response from the model. Streaming works only in Chat.",
"list": false,
"name": "stream",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "bool",
"value": false
},
"system_message": {
"advanced": true,
"display_name": "System Message",
"dynamic": false,
"info": "System message to pass to the model.",
"list": false,
"load_from_db": false,
"name": "system_message",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "str",
"value": ""
},
"temperature": {
"advanced": false,
"display_name": "Temperature",
"dynamic": false,
"info": "",
"list": false,
"name": "temperature",
"placeholder": "",
"required": false,
"show": true,
"title_case": false,
"trace_as_metadata": true,
"type": "float",
"value": 0.1
}
}
},
"type": "OpenAIModel"
},
"dragging": false,
"height": 587,
"id": "OpenAIModel-Y3GUP",
"position": {
"x": 1950.3830456413473,
"y": 380.8161704718418
},
"positionAbsolute": {
"x": 1950.3830456413473,
"y": 380.8161704718418
},
"selected": true,
"type": "genericNode",
"width": 384
}
],
"viewport": {
"x": -46.935859383438924,
"y": 110.83424076132906,
"zoom": 0.5205627295612754
}
},
"description": "This flow can be used to create a blog post following instructions from the user, using two other blogs as reference.",
"endpoint_name": null,
"icon": "FileText",
"id": "5e9b5662-0985-4d40-8ffe-b7f42fa86421",
"is_component": false,
"last_tested_version": "1.0.19.post1",
"name": "Blog Writer",
"tags": [
"chatbots"
]
}