From 932aee9cffe401c131bc72dc77c388455f9b5e60 Mon Sep 17 00:00:00 2001 From: Jorge Piedrahita Ortiz Date: Mon, 17 Mar 2025 10:05:08 -0500 Subject: [PATCH] feat: financial agent template (#6246) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * add finacial assistant template * fix typos * Update tags Financial Agent.json * fix typo * template updated * Update sambanova_constants.py * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes * updated Financial Agent Template * [autofix.ci] apply automated fixes * [autofix.ci] apply automated fixes * ✨ (Financial Agent.spec.ts): Add integration test for Financial Agent functionality ♻️ (select-gpt-model.ts): Refactor selectGptModel function to handle multiple dropdown options for GPT model selection * Update Financial Agent.json * Update Financial Agent.json * Update Financial Agent.json * notes-style * Update Financial Agent.json * Update Financial Agent.json * minor fix in notes --------- Co-authored-by: Edwin Jose Co-authored-by: Jorge Piedrahita Ortiz <166410071+snova-jorgep@users.noreply.github.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: cristhianzl Co-authored-by: Mendon Kissling <59585235+mendonk@users.noreply.github.com> --- .../base/models/sambanova_constants.py | 2 + .../starter_projects/Financial Agent.json | 3845 +++++++++++++++++ .../core/integrations/Financial Agent.spec.ts | 78 + src/frontend/tests/utils/select-gpt-model.ts | 6 +- 4 files changed, 3928 insertions(+), 3 deletions(-) create mode 100644 src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json create mode 100644 src/frontend/tests/core/integrations/Financial Agent.spec.ts diff --git a/src/backend/base/langflow/base/models/sambanova_constants.py b/src/backend/base/langflow/base/models/sambanova_constants.py index 23d7f385b..0537c22a6 100644 --- a/src/backend/base/langflow/base/models/sambanova_constants.py +++ b/src/backend/base/langflow/base/models/sambanova_constants.py @@ -3,6 +3,8 @@ SAMBANOVA_MODEL_NAMES = [ "Meta-Llama-3.1-8B-Instruct", "Meta-Llama-3.1-70B-Instruct", "Meta-Llama-3.1-405B-Instruct", + "DeepSeek-R1-Distill-Llama-70B", + "DeepSeek-R1", "Meta-Llama-3.2-1B-Instruct", "Meta-Llama-3.2-3B-Instruct", "Llama-3.2-11B-Vision-Instruct", diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json new file mode 100644 index 000000000..66c93bbcd --- /dev/null +++ b/src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json @@ -0,0 +1,3845 @@ +{ + "data": { + "edges": [ + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "CombineText", + "id": "CombineText-B7MqV", + "name": "combined_text", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "text2", + "id": "CombineText-2Ncws", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-CombineText-B7MqV{œdataTypeœ:œCombineTextœ,œidœ:œCombineText-B7MqVœ,œnameœ:œcombined_textœ,œoutput_typesœ:[œMessageœ]}-CombineText-2Ncws{œfieldNameœ:œtext2œ,œidœ:œCombineText-2Ncwsœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "CombineText-B7MqV", + "sourceHandle": "{œdataTypeœ: œCombineTextœ, œidœ: œCombineText-B7MqVœ, œnameœ: œcombined_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "CombineText-2Ncws", + "targetHandle": "{œfieldNameœ: œtext2œ, œidœ: œCombineText-2Ncwsœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-bSTG4", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "text1", + "id": "CombineText-2Ncws", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-ChatInput-bSTG4{œdataTypeœ:œChatInputœ,œidœ:œChatInput-bSTG4œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-CombineText-2Ncws{œfieldNameœ:œtext1œ,œidœ:œCombineText-2Ncwsœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "ChatInput-bSTG4", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-bSTG4œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "CombineText-2Ncws", + "targetHandle": "{œfieldNameœ: œtext1œ, œidœ: œCombineText-2Ncwsœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "CombineText", + "id": "CombineText-2Ncws", + "name": "combined_text", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "SambaNovaModel-67RaX", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-CombineText-2Ncws{œdataTypeœ:œCombineTextœ,œidœ:œCombineText-2Ncwsœ,œnameœ:œcombined_textœ,œoutput_typesœ:[œMessageœ]}-SambaNovaModel-67RaX{œfieldNameœ:œinput_valueœ,œidœ:œSambaNovaModel-67RaXœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "CombineText-2Ncws", + "sourceHandle": "{œdataTypeœ: œCombineTextœ, œidœ: œCombineText-2Ncwsœ, œnameœ: œcombined_textœ, œoutput_typesœ: [œMessageœ]}", + "target": "SambaNovaModel-67RaX", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œSambaNovaModel-67RaXœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "SambaNovaModel", + "id": "SambaNovaModel-67RaX", + "name": "text_output", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "ChatOutput-BjiRE", + "inputTypes": [ + "Data", + "DataFrame", + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-SambaNovaModel-67RaX{œdataTypeœ:œSambaNovaModelœ,œidœ:œSambaNovaModel-67RaXœ,œnameœ:œtext_outputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-BjiRE{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-BjiREœ,œinputTypesœ:[œDataœ,œDataFrameœ,œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "SambaNovaModel-67RaX", + "sourceHandle": "{œdataTypeœ: œSambaNovaModelœ, œidœ: œSambaNovaModel-67RaXœ, œnameœ: œtext_outputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-BjiRE", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-BjiREœ, œinputTypesœ: [œDataœ, œDataFrameœ, œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-bSTG4", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "Agent-8O2Iy", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-ChatInput-bSTG4{œdataTypeœ:œChatInputœ,œidœ:œChatInput-bSTG4œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-8O2Iy{œfieldNameœ:œinput_valueœ,œidœ:œAgent-8O2Iyœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "ChatInput-bSTG4", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-bSTG4œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Agent-8O2Iy", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-8O2Iyœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "Agent", + "id": "Agent-8O2Iy", + "name": "response", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "text2", + "id": "CombineText-B7MqV", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-Agent-8O2Iy{œdataTypeœ:œAgentœ,œidœ:œAgent-8O2Iyœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-CombineText-B7MqV{œfieldNameœ:œtext2œ,œidœ:œCombineText-B7MqVœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "Agent-8O2Iy", + "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-8O2Iyœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", + "target": "CombineText-B7MqV", + "targetHandle": "{œfieldNameœ: œtext2œ, œidœ: œCombineText-B7MqVœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "TavilySearchComponent", + "id": "TavilySearchComponent-yRkVZ", + "name": "component_as_tool", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "Agent-8O2Iy", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-TavilySearchComponent-yRkVZ{œdataTypeœ:œTavilySearchComponentœ,œidœ:œTavilySearchComponent-yRkVZœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-8O2Iy{œfieldNameœ:œtoolsœ,œidœ:œAgent-8O2Iyœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "TavilySearchComponent-yRkVZ", + "sourceHandle": "{œdataTypeœ: œTavilySearchComponentœ, œidœ: œTavilySearchComponent-yRkVZœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", + "target": "Agent-8O2Iy", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-8O2Iyœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "URL", + "id": "URL-1uKdC", + "name": "component_as_tool", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "Agent-fvvwn", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-URL-1uKdC{œdataTypeœ:œURLœ,œidœ:œURL-1uKdCœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-fvvwn{œfieldNameœ:œtoolsœ,œidœ:œAgent-fvvwnœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "URL-1uKdC", + "sourceHandle": "{œdataTypeœ: œURLœ, œidœ: œURL-1uKdCœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", + "target": "Agent-fvvwn", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-fvvwnœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "YfinanceComponent", + "id": "YfinanceComponent-LaBhV", + "name": "component_as_tool", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "Agent-fvvwn", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-YfinanceComponent-LaBhV{œdataTypeœ:œYfinanceComponentœ,œidœ:œYfinanceComponent-LaBhVœ,œnameœ:œcomponent_as_toolœ,œoutput_typesœ:[œToolœ]}-Agent-fvvwn{œfieldNameœ:œtoolsœ,œidœ:œAgent-fvvwnœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "selected": false, + "source": "YfinanceComponent-LaBhV", + "sourceHandle": "{œdataTypeœ: œYfinanceComponentœ, œidœ: œYfinanceComponent-LaBhVœ, œnameœ: œcomponent_as_toolœ, œoutput_typesœ: [œToolœ]}", + "target": "Agent-fvvwn", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œAgent-fvvwnœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "ChatInput", + "id": "ChatInput-bSTG4", + "name": "message", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "input_value", + "id": "Agent-fvvwn", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-ChatInput-bSTG4{œdataTypeœ:œChatInputœ,œidœ:œChatInput-bSTG4œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Agent-fvvwn{œfieldNameœ:œinput_valueœ,œidœ:œAgent-fvvwnœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "ChatInput-bSTG4", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-bSTG4œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Agent-fvvwn", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œAgent-fvvwnœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + }, + { + "animated": false, + "className": "", + "data": { + "sourceHandle": { + "dataType": "Agent", + "id": "Agent-fvvwn", + "name": "response", + "output_types": [ + "Message" + ] + }, + "targetHandle": { + "fieldName": "text1", + "id": "CombineText-B7MqV", + "inputTypes": [ + "Message" + ], + "type": "str" + } + }, + "id": "reactflow__edge-Agent-fvvwn{œdataTypeœ:œAgentœ,œidœ:œAgent-fvvwnœ,œnameœ:œresponseœ,œoutput_typesœ:[œMessageœ]}-CombineText-B7MqV{œfieldNameœ:œtext1œ,œidœ:œCombineText-B7MqVœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "selected": false, + "source": "Agent-fvvwn", + "sourceHandle": "{œdataTypeœ: œAgentœ, œidœ: œAgent-fvvwnœ, œnameœ: œresponseœ, œoutput_typesœ: [œMessageœ]}", + "target": "CombineText-B7MqV", + "targetHandle": "{œfieldNameœ: œtext1œ, œidœ: œCombineText-B7MqVœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + } + ], + "nodes": [ + { + "data": { + "id": "ChatInput-bSTG4", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Get chat inputs from the Playground.", + "display_name": "Chat Input", + "documentation": "", + "edited": false, + "field_order": [ + "input_value", + "should_store_message", + "sender", + "sender_name", + "session_id", + "files", + "background_color", + "chat_icon", + "text_color" + ], + "frozen": false, + "icon": "MessagesSquare", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": true, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Message", + "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": "" + }, + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from langflow.base.data.utils import IMG_FILE_TYPES, TEXT_FILE_TYPES\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.io import (\n DropdownInput,\n FileInput,\n MessageTextInput,\n MultilineInput,\n Output,\n)\nfrom langflow.schema.message import Message\nfrom langflow.utils.constants import (\n MESSAGE_SENDER_AI,\n MESSAGE_SENDER_NAME_USER,\n MESSAGE_SENDER_USER,\n)\n\n\nclass ChatInput(ChatComponent):\n display_name = \"Chat Input\"\n description = \"Get chat inputs from the Playground.\"\n icon = \"MessagesSquare\"\n name = \"ChatInput\"\n minimized = True\n\n inputs = [\n MultilineInput(\n name=\"input_value\",\n display_name=\"Text\",\n value=\"\",\n info=\"Message to be passed as input.\",\n input_types=[],\n ),\n BoolInput(\n name=\"should_store_message\",\n display_name=\"Store Messages\",\n info=\"Store the message in the history.\",\n value=True,\n advanced=True,\n ),\n DropdownInput(\n name=\"sender\",\n display_name=\"Sender Type\",\n options=[MESSAGE_SENDER_AI, MESSAGE_SENDER_USER],\n value=MESSAGE_SENDER_USER,\n info=\"Type of sender.\",\n advanced=True,\n ),\n MessageTextInput(\n name=\"sender_name\",\n display_name=\"Sender Name\",\n info=\"Name of the sender.\",\n value=MESSAGE_SENDER_NAME_USER,\n advanced=True,\n ),\n MessageTextInput(\n name=\"session_id\",\n display_name=\"Session ID\",\n info=\"The session ID of the chat. If empty, the current session ID parameter will be used.\",\n advanced=True,\n ),\n FileInput(\n name=\"files\",\n display_name=\"Files\",\n file_types=TEXT_FILE_TYPES + IMG_FILE_TYPES,\n info=\"Files to be sent with the message.\",\n advanced=True,\n is_list=True,\n ),\n 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(display_name=\"Message\", name=\"message\", method=\"message_response\"),\n ]\n\n async def message_response(self) -> Message:\n background_color = self.background_color\n text_color = self.text_color\n icon = self.chat_icon\n\n message = await Message.create(\n text=self.input_value,\n sender=self.sender,\n sender_name=self.sender_name,\n session_id=self.session_id,\n files=self.files,\n properties={\n \"background_color\": background_color,\n \"text_color\": text_color,\n \"icon\": icon,\n },\n )\n if self.session_id and isinstance(message, Message) and self.should_store_message:\n stored_message = await 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" + }, + "files": { + "_input_type": "FileInput", + "advanced": true, + "display_name": "Files", + "dynamic": false, + "fileTypes": [ + "txt", + "md", + "mdx", + "csv", + "json", + "yaml", + "yml", + "xml", + "html", + "htm", + "pdf", + "docx", + "py", + "sh", + "sql", + "js", + "ts", + "tsx", + "jpg", + "jpeg", + "png", + "bmp", + "image" + ], + "file_path": "", + "info": "Files to be sent with the message.", + "list": true, + "list_add_label": "Add More", + "name": "files", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "file", + "value": "" + }, + "input_value": { + "_input_type": "MultilineInput", + "advanced": false, + "display_name": "Text", + "dynamic": false, + "info": "Message to be passed as input.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "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": "" + }, + "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, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "User" + }, + "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": "User" + }, + "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": "ChatInput" + }, + "dragging": false, + "id": "ChatInput-bSTG4", + "measured": { + "height": 66, + "width": 192 + }, + "position": { + "x": -777.5771293156841, + "y": 305.2723025086492 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "TavilySearchComponent-yRkVZ", + "node": { + "base_classes": [ + "Data", + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "**Tavily AI** is a search engine optimized for LLMs and RAG, aimed at efficient, quick, and persistent search results.", + "display_name": "Tavily AI Search", + "documentation": "", + "edited": false, + "field_order": [ + "api_key", + "query", + "search_depth", + "topic", + "time_range", + "max_results", + "include_images", + "include_answer" + ], + "frozen": false, + "icon": "TavilyIcon", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Toolset", + "hidden": null, + "method": "to_toolkit", + "name": "component_as_tool", + "required_inputs": null, + "selected": "Tool", + "tool_mode": true, + "types": [ + "Tool" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Tavily API Key", + "dynamic": false, + "info": "Your Tavily API Key.", + "input_types": [ + "Message" + ], + "load_from_db": true, + "name": "api_key", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "import httpx\nfrom loguru import logger\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_text\nfrom langflow.io import BoolInput, DropdownInput, IntInput, MessageTextInput, Output, SecretStrInput\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\n\n\nclass TavilySearchComponent(Component):\n display_name = \"Tavily AI Search\"\n description = \"\"\"**Tavily AI** is a search engine optimized for LLMs and RAG, \\\n aimed at efficient, quick, and persistent search results.\"\"\"\n icon = \"TavilyIcon\"\n\n inputs = [\n SecretStrInput(\n name=\"api_key\",\n display_name=\"Tavily API Key\",\n required=True,\n info=\"Your Tavily API Key.\",\n ),\n MessageTextInput(\n name=\"query\",\n display_name=\"Search Query\",\n info=\"The search query you want to execute with Tavily.\",\n tool_mode=True,\n ),\n DropdownInput(\n name=\"search_depth\",\n display_name=\"Search Depth\",\n info=\"The depth of the search.\",\n options=[\"basic\", \"advanced\"],\n value=\"advanced\",\n advanced=True,\n ),\n DropdownInput(\n name=\"topic\",\n display_name=\"Search Topic\",\n info=\"The category of the search.\",\n options=[\"general\", \"news\"],\n value=\"general\",\n advanced=True,\n ),\n DropdownInput(\n name=\"time_range\",\n display_name=\"Time Range\",\n info=\"The time range back from the current date to include in the search results.\",\n options=[\"day\", \"week\", \"month\", \"year\"],\n value=None,\n advanced=True,\n combobox=True,\n ),\n IntInput(\n name=\"max_results\",\n display_name=\"Max Results\",\n info=\"The maximum number of search results to return.\",\n value=5,\n advanced=True,\n ),\n BoolInput(\n name=\"include_images\",\n display_name=\"Include Images\",\n info=\"Include a list of query-related images in the response.\",\n value=True,\n advanced=True,\n ),\n BoolInput(\n name=\"include_answer\",\n display_name=\"Include Answer\",\n info=\"Include a short answer to original query.\",\n value=True,\n advanced=True,\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 fetch_content(self) -> list[Data]:\n try:\n url = \"https://api.tavily.com/search\"\n headers = {\n \"content-type\": \"application/json\",\n \"accept\": \"application/json\",\n }\n payload = {\n \"api_key\": self.api_key,\n \"query\": self.query,\n \"search_depth\": self.search_depth,\n \"topic\": self.topic,\n \"max_results\": self.max_results,\n \"include_images\": self.include_images,\n \"include_answer\": self.include_answer,\n \"time_range\": self.time_range,\n }\n\n with httpx.Client() as client:\n response = client.post(url, json=payload, headers=headers)\n\n response.raise_for_status()\n search_results = response.json()\n\n data_results = []\n\n if self.include_answer and search_results.get(\"answer\"):\n data_results.append(Data(text=search_results[\"answer\"]))\n\n for result in search_results.get(\"results\", []):\n content = result.get(\"content\", \"\")\n data_results.append(\n Data(\n text=content,\n data={\n \"title\": result.get(\"title\"),\n \"url\": result.get(\"url\"),\n \"content\": content,\n \"score\": result.get(\"score\"),\n },\n )\n )\n\n if self.include_images and search_results.get(\"images\"):\n data_results.append(Data(text=\"Images found\", data={\"images\": search_results[\"images\"]}))\n except httpx.HTTPStatusError as exc:\n error_message = f\"HTTP error occurred: {exc.response.status_code} - {exc.response.text}\"\n logger.error(error_message)\n return [Data(text=error_message, data={\"error\": error_message})]\n except httpx.RequestError as exc:\n error_message = f\"Request error occurred: {exc}\"\n logger.error(error_message)\n return [Data(text=error_message, data={\"error\": error_message})]\n except ValueError as exc:\n error_message = f\"Invalid response format: {exc}\"\n logger.error(error_message)\n return [Data(text=error_message, data={\"error\": error_message})]\n else:\n self.status = data_results\n return data_results\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n result_string = data_to_text(\"{text}\", data)\n self.status = result_string\n return Message(text=result_string)\n" + }, + "include_answer": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Include Answer", + "dynamic": false, + "info": "Include a short answer to original query.", + "list": false, + "list_add_label": "Add More", + "name": "include_answer", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "include_images": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Include Images", + "dynamic": false, + "info": "Include a list of query-related images in the response.", + "list": false, + "list_add_label": "Add More", + "name": "include_images", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "max_results": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Results", + "dynamic": false, + "info": "The maximum number of search results to return.", + "list": false, + "list_add_label": "Add More", + "name": "max_results", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 5 + }, + "query": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Search Query", + "dynamic": false, + "info": "The search query you want to execute with Tavily.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "query", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "search_depth": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Search Depth", + "dynamic": false, + "info": "The depth of the search.", + "name": "search_depth", + "options": [ + "basic", + "advanced" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "advanced" + }, + "time_range": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": true, + "dialog_inputs": {}, + "display_name": "Time Range", + "dynamic": false, + "info": "The time range back from the current date to include in the search results.", + "name": "time_range", + "options": [ + "day", + "week", + "month", + "year" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str" + }, + "tools_metadata": { + "_input_type": "TableInput", + "advanced": false, + "display_name": "Edit tools", + "dynamic": false, + "info": "", + "is_list": true, + "list_add_label": "Add More", + "name": "tools_metadata", + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "table_icon": "Hammer", + "table_options": { + "block_add": true, + "block_delete": true, + "block_edit": true, + "block_filter": true, + "block_hide": true, + "block_select": true, + "block_sort": true, + "description": "Modify tool names and descriptions to help agents understand when to use each tool.", + "field_parsers": { + "commands": "commands", + "name": [ + "snake_case", + "no_blank" + ] + }, + "hide_options": true + }, + "table_schema": { + "columns": [ + { + "default": "None", + "description": "Specify the name of the tool.", + "disable_edit": false, + "display_name": "Tool Name", + "edit_mode": "inline", + "filterable": false, + "formatter": "text", + "hidden": false, + "name": "name", + "sortable": false, + "type": "str" + }, + { + "default": "None", + "description": "Describe the purpose of the tool.", + "disable_edit": false, + "display_name": "Tool Description", + "edit_mode": "popover", + "filterable": false, + "formatter": "text", + "hidden": false, + "name": "description", + "sortable": false, + "type": "str" + }, + { + "default": "None", + "description": "The default identifiers for the tools and cannot be changed.", + "disable_edit": true, + "display_name": "Tool Identifiers", + "edit_mode": "inline", + "filterable": false, + "formatter": "text", + "hidden": true, + "name": "tags", + "sortable": false, + "type": "str" + }, + { + "default": true, + "description": "Indicates whether the tool is currently active. Set to True to activate this tool.", + "disable_edit": false, + "display_name": "Enable", + "edit_mode": "popover", + "filterable": true, + "formatter": "boolean", + "hidden": false, + "name": "status", + "sortable": true, + "type": "boolean" + } + ] + }, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "trigger_icon": "Hammer", + "trigger_text": "", + "type": "table", + "value": [ + { + "description": "fetch_content(api_key: Message) - **Tavily AI** is a search engine optimized for LLMs and RAG, aimed at efficient, quick, and persistent search results.", + "name": "TavilySearchComponent-fetch_content", + "status": true, + "tags": [ + "TavilySearchComponent-fetch_content" + ] + }, + { + "description": "fetch_content_text(api_key: Message) - **Tavily AI** is a search engine optimized for LLMs and RAG, aimed at efficient, quick, and persistent search results.", + "name": "TavilySearchComponent-fetch_content_text", + "status": true, + "tags": [ + "TavilySearchComponent-fetch_content_text" + ] + } + ] + }, + "topic": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Search Topic", + "dynamic": false, + "info": "The category of the search.", + "name": "topic", + "options": [ + "general", + "news" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "general" + } + }, + "tool_mode": true + }, + "showNode": true, + "type": "TavilySearchComponent" + }, + "dragging": false, + "id": "TavilySearchComponent-yRkVZ", + "measured": { + "height": 437, + "width": 320 + }, + "position": { + "x": -682.4849400136196, + "y": 584.5279981500064 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "YfinanceComponent-LaBhV", + "node": { + "base_classes": [ + "Data", + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Uses [yfinance](https://pypi.org/project/yfinance/) (unofficial package) to access financial data and market information from Yahoo Finance.", + "display_name": "Yahoo Finance", + "documentation": "", + "edited": false, + "field_order": [ + "symbol", + "method", + "num_news" + ], + "frozen": false, + "icon": "trending-up", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Toolset", + "hidden": null, + "method": "to_toolkit", + "name": "component_as_tool", + "required_inputs": null, + "selected": "Tool", + "tool_mode": true, + "types": [ + "Tool" + ], + "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 ast\nimport pprint\nfrom enum import Enum\n\nimport yfinance as yf\nfrom langchain_core.tools import ToolException\nfrom loguru import logger\nfrom pydantic import BaseModel, Field\n\nfrom langflow.custom import Component\nfrom langflow.inputs import DropdownInput, IntInput, MessageTextInput\nfrom langflow.io import Output\nfrom langflow.schema import Data\nfrom langflow.schema.message import Message\n\n\nclass YahooFinanceMethod(Enum):\n GET_INFO = \"get_info\"\n GET_NEWS = \"get_news\"\n GET_ACTIONS = \"get_actions\"\n GET_ANALYSIS = \"get_analysis\"\n GET_BALANCE_SHEET = \"get_balance_sheet\"\n GET_CALENDAR = \"get_calendar\"\n GET_CASHFLOW = \"get_cashflow\"\n GET_INSTITUTIONAL_HOLDERS = \"get_institutional_holders\"\n GET_RECOMMENDATIONS = \"get_recommendations\"\n GET_SUSTAINABILITY = \"get_sustainability\"\n GET_MAJOR_HOLDERS = \"get_major_holders\"\n GET_MUTUALFUND_HOLDERS = \"get_mutualfund_holders\"\n GET_INSIDER_PURCHASES = \"get_insider_purchases\"\n GET_INSIDER_TRANSACTIONS = \"get_insider_transactions\"\n GET_INSIDER_ROSTER_HOLDERS = \"get_insider_roster_holders\"\n GET_DIVIDENDS = \"get_dividends\"\n GET_CAPITAL_GAINS = \"get_capital_gains\"\n GET_SPLITS = \"get_splits\"\n GET_SHARES = \"get_shares\"\n GET_FAST_INFO = \"get_fast_info\"\n GET_SEC_FILINGS = \"get_sec_filings\"\n GET_RECOMMENDATIONS_SUMMARY = \"get_recommendations_summary\"\n GET_UPGRADES_DOWNGRADES = \"get_upgrades_downgrades\"\n GET_EARNINGS = \"get_earnings\"\n GET_INCOME_STMT = \"get_income_stmt\"\n\n\nclass YahooFinanceSchema(BaseModel):\n symbol: str = Field(..., description=\"The stock symbol to retrieve data for.\")\n method: YahooFinanceMethod = Field(YahooFinanceMethod.GET_INFO, description=\"The type of data to retrieve.\")\n num_news: int | None = Field(5, description=\"The number of news articles to retrieve.\")\n\n\nclass YfinanceComponent(Component):\n display_name = \"Yahoo Finance\"\n description = \"\"\"Uses [yfinance](https://pypi.org/project/yfinance/) (unofficial package) \\\nto access financial data and market information from Yahoo Finance.\"\"\"\n icon = \"trending-up\"\n\n inputs = [\n MessageTextInput(\n name=\"symbol\",\n display_name=\"Stock Symbol\",\n info=\"The stock symbol to retrieve data for (e.g., AAPL, GOOG).\",\n tool_mode=True,\n ),\n DropdownInput(\n name=\"method\",\n display_name=\"Data Method\",\n info=\"The type of data to retrieve.\",\n options=list(YahooFinanceMethod),\n value=\"get_news\",\n ),\n IntInput(\n name=\"num_news\",\n display_name=\"Number of News\",\n info=\"The number of news articles to retrieve (only applicable for get_news).\",\n value=5,\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 run_model(self) -> list[Data]:\n return self.fetch_content()\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n result_string = \"\"\n for item in data:\n result_string += item.text + \"\\n\"\n self.status = result_string\n return Message(text=result_string)\n\n def _fetch_yfinance_data(self, ticker: yf.Ticker, method: YahooFinanceMethod, num_news: int | None) -> str:\n try:\n if method == YahooFinanceMethod.GET_INFO:\n result = ticker.info\n elif method == YahooFinanceMethod.GET_NEWS:\n result = ticker.news[:num_news]\n else:\n result = getattr(ticker, method.value)()\n return pprint.pformat(result)\n except Exception as e:\n error_message = f\"Error retrieving data: {e}\"\n logger.debug(error_message)\n self.status = error_message\n raise ToolException(error_message) from e\n\n def fetch_content(self) -> list[Data]:\n try:\n return self._yahoo_finance_tool(\n self.symbol,\n YahooFinanceMethod(self.method),\n self.num_news,\n )\n except ToolException:\n raise\n except Exception as e:\n error_message = f\"Unexpected error: {e}\"\n logger.debug(error_message)\n self.status = error_message\n raise ToolException(error_message) from e\n\n def _yahoo_finance_tool(\n self,\n symbol: str,\n method: YahooFinanceMethod,\n num_news: int | None = 5,\n ) -> list[Data]:\n ticker = yf.Ticker(symbol)\n result = self._fetch_yfinance_data(ticker, method, num_news)\n\n if method == YahooFinanceMethod.GET_NEWS:\n data_list = [\n Data(text=f\"{article['title']}: {article['link']}\", data=article)\n for article in ast.literal_eval(result)\n ]\n else:\n data_list = [Data(text=result, data={\"result\": result})]\n\n return data_list\n" + }, + "method": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Data Method", + "dynamic": false, + "info": "The type of data to retrieve.", + "name": "method", + "options": [ + "get_info", + "get_news", + "get_actions", + "get_analysis", + "get_balance_sheet", + "get_calendar", + "get_cashflow", + "get_institutional_holders", + "get_recommendations", + "get_sustainability", + "get_major_holders", + "get_mutualfund_holders", + "get_insider_purchases", + "get_insider_transactions", + "get_insider_roster_holders", + "get_dividends", + "get_capital_gains", + "get_splits", + "get_shares", + "get_fast_info", + "get_sec_filings", + "get_recommendations_summary", + "get_upgrades_downgrades", + "get_earnings", + "get_income_stmt" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "get_news" + }, + "num_news": { + "_input_type": "IntInput", + "advanced": false, + "display_name": "Number of News", + "dynamic": false, + "info": "The number of news articles to retrieve (only applicable for get_news).", + "list": false, + "list_add_label": "Add More", + "name": "num_news", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 3 + }, + "symbol": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Stock Symbol", + "dynamic": false, + "info": "The stock symbol to retrieve data for (e.g., AAPL, GOOG).", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "symbol", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "tools_metadata": { + "_input_type": "TableInput", + "advanced": false, + "display_name": "Edit tools", + "dynamic": false, + "info": "", + "is_list": true, + "list_add_label": "Add More", + "name": "tools_metadata", + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "table_icon": "Hammer", + "table_options": { + "block_add": true, + "block_delete": true, + "block_edit": true, + "block_filter": true, + "block_hide": true, + "block_select": true, + "block_sort": true, + "description": "Modify tool names and descriptions to help agents understand when to use each tool.", + "field_parsers": { + "commands": "commands", + "name": [ + "snake_case", + "no_blank" + ] + }, + "hide_options": true + }, + "table_schema": { + "columns": [ + { + "default": "None", + "description": "Specify the name of the tool.", + "disable_edit": false, + "display_name": "Tool Name", + "edit_mode": "inline", + "filterable": false, + "formatter": "text", + "hidden": false, + "name": "name", + "sortable": false, + "type": "str" + }, + { + "default": "None", + "description": "Describe the purpose of the tool.", + "disable_edit": false, + "display_name": "Tool Description", + "edit_mode": "popover", + "filterable": false, + "formatter": "text", + "hidden": false, + "name": "description", + "sortable": false, + "type": "str" + }, + { + "default": "None", + "description": "The default identifiers for the tools and cannot be changed.", + "disable_edit": true, + "display_name": "Tool Identifiers", + "edit_mode": "inline", + "filterable": false, + "formatter": "text", + "hidden": true, + "name": "tags", + "sortable": false, + "type": "str" + }, + { + "default": true, + "description": "Indicates whether the tool is currently active. Set to True to activate this tool.", + "disable_edit": false, + "display_name": "Enable", + "edit_mode": "popover", + "filterable": true, + "formatter": "boolean", + "hidden": false, + "name": "status", + "sortable": true, + "type": "boolean" + } + ] + }, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "trigger_icon": "Hammer", + "trigger_text": "", + "type": "table", + "value": [ + { + "description": "fetch_content() - Uses [yfinance](https://pypi.org/project/yfinance/) (unofficial package) to access financial data and market information from Yahoo Finance.", + "name": "YfinanceComponent-fetch_content", + "status": true, + "tags": [ + "YfinanceComponent-fetch_content" + ] + }, + { + "description": "fetch_content_text() - Uses [yfinance](https://pypi.org/project/yfinance/) (unofficial package) to access financial data and market information from Yahoo Finance.", + "name": "YfinanceComponent-fetch_content_text", + "status": true, + "tags": [ + "YfinanceComponent-fetch_content_text" + ] + } + ] + } + }, + "tool_mode": true + }, + "showNode": true, + "type": "YfinanceComponent" + }, + "dragging": false, + "id": "YfinanceComponent-LaBhV", + "measured": { + "height": 519, + "width": 320 + }, + "position": { + "x": -585.7556355375972, + "y": -373.57917338885125 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "CombineText-B7MqV", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Concatenate two text sources into a single text chunk using a specified delimiter.", + "display_name": "Combine Text", + "documentation": "", + "edited": false, + "field_order": [ + "text1", + "text2", + "delimiter" + ], + "frozen": false, + "icon": "merge", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Combined Text", + "method": "combine_texts", + "name": "combined_text", + "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.custom import Component\nfrom langflow.io import MessageTextInput, Output\nfrom langflow.schema.message import Message\n\n\nclass CombineTextComponent(Component):\n display_name = \"Combine Text\"\n description = \"Concatenate two text sources into a single text chunk using a specified delimiter.\"\n icon = \"merge\"\n name = \"CombineText\"\n\n inputs = [\n MessageTextInput(\n name=\"text1\",\n display_name=\"First Text\",\n info=\"The first text input to concatenate.\",\n ),\n MessageTextInput(\n name=\"text2\",\n display_name=\"Second Text\",\n info=\"The second text input to concatenate.\",\n ),\n MessageTextInput(\n name=\"delimiter\",\n display_name=\"Delimiter\",\n info=\"A string used to separate the two text inputs. Defaults to a whitespace.\",\n value=\" \",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Combined Text\", name=\"combined_text\", method=\"combine_texts\"),\n ]\n\n def combine_texts(self) -> Message:\n combined = self.delimiter.join([self.text1, self.text2])\n self.status = combined\n return Message(text=combined)\n" + }, + "delimiter": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Delimiter", + "dynamic": false, + "info": "A string used to separate the two text inputs. Defaults to a whitespace.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "delimiter", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": " \\n\\n" + }, + "text1": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "First Text", + "dynamic": false, + "info": "The first text input to concatenate.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text1", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "text2": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Second Text", + "dynamic": false, + "info": "The second text input to concatenate.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text2", + "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": true, + "type": "CombineText" + }, + "dragging": false, + "id": "CombineText-B7MqV", + "measured": { + "height": 415, + "width": 320 + }, + "position": { + "x": 340.2356972211369, + "y": 402.33351464112826 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "ChatOutput-BjiRE", + "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" + ], + "frozen": false, + "icon": "MessagesSquare", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": true, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Message", + "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\nfrom langflow.base.io.chat import ChatComponent\nfrom langflow.inputs import BoolInput\nfrom langflow.inputs.inputs import HandleInput\nfrom langflow.io import DropdownInput, MessageTextInput, Output\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.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=\"Text\",\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=\"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 # 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 _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 _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 if data.get_text() is None:\n msg = \"Empty Data object\"\n raise ValueError(msg)\n return data.get_text()\n if isinstance(data, DataFrame):\n if 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\n # Replace pipe characters to avoid markdown table issues\n processed_data = data.replace(r\"\\|\", r\"\\\\|\", regex=True)\n\n processed_data = processed_data.map(\n lambda x: str(x).replace(\"\\n\", \"
\") if isinstance(x, str) else x\n )\n\n return processed_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) -> 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([self._safe_convert(item) for item in self.input_value])\n if isinstance(self.input_value, Generator):\n return self.input_value\n return self._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": "MessageInput", + "advanced": false, + "display_name": "Text", + "dynamic": false, + "info": "Message to be passed as output.", + "input_types": [ + "Data", + "DataFrame", + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "input_value", + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "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, + "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-BjiRE", + "measured": { + "height": 66, + "width": 192 + }, + "position": { + "x": 1915.4342657794896, + "y": 441.2937993426264 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "CombineText-2Ncws", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Concatenate two text sources into a single text chunk using a specified delimiter.", + "display_name": "Combine Text", + "documentation": "", + "edited": false, + "field_order": [ + "text1", + "text2", + "delimiter" + ], + "frozen": false, + "icon": "merge", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Combined Text", + "method": "combine_texts", + "name": "combined_text", + "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.custom import Component\nfrom langflow.io import MessageTextInput, Output\nfrom langflow.schema.message import Message\n\n\nclass CombineTextComponent(Component):\n display_name = \"Combine Text\"\n description = \"Concatenate two text sources into a single text chunk using a specified delimiter.\"\n icon = \"merge\"\n name = \"CombineText\"\n\n inputs = [\n MessageTextInput(\n name=\"text1\",\n display_name=\"First Text\",\n info=\"The first text input to concatenate.\",\n ),\n MessageTextInput(\n name=\"text2\",\n display_name=\"Second Text\",\n info=\"The second text input to concatenate.\",\n ),\n MessageTextInput(\n name=\"delimiter\",\n display_name=\"Delimiter\",\n info=\"A string used to separate the two text inputs. Defaults to a whitespace.\",\n value=\" \",\n ),\n ]\n\n outputs = [\n Output(display_name=\"Combined Text\", name=\"combined_text\", method=\"combine_texts\"),\n ]\n\n def combine_texts(self) -> Message:\n combined = self.delimiter.join([self.text1, self.text2])\n self.status = combined\n return Message(text=combined)\n" + }, + "delimiter": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Delimiter", + "dynamic": false, + "info": "A string used to separate the two text inputs. Defaults to a whitespace.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "delimiter", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "\\n\\nContext: \\n" + }, + "text1": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "First Text", + "dynamic": false, + "info": "The first text input to concatenate.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text1", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "text2": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Second Text", + "dynamic": false, + "info": "The second text input to concatenate.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "text2", + "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": true, + "type": "CombineText" + }, + "dragging": false, + "id": "CombineText-2Ncws", + "measured": { + "height": 415, + "width": 320 + }, + "position": { + "x": 738.8545176038866, + "y": 179.6359034027769 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "URL-1uKdC", + "node": { + "base_classes": [ + "Data", + "DataFrame", + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", + "display_name": "URL", + "documentation": "", + "edited": false, + "field_order": [ + "urls", + "format", + "separator", + "clean_extra_whitespace" + ], + "frozen": false, + "icon": "layout-template", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Toolset", + "hidden": null, + "method": "to_toolkit", + "name": "component_as_tool", + "required_inputs": null, + "selected": "Tool", + "tool_mode": true, + "types": [ + "Tool" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "clean_extra_whitespace": { + "_input_type": "BoolInput", + "advanced": false, + "display_name": "Clean Extra Whitespace", + "dynamic": false, + "info": "Whether to clean excessive blank lines in the text output. Only applies to 'Text' format.", + "list": false, + "list_add_label": "Add More", + "name": "clean_extra_whitespace", + "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": "import asyncio\nimport json\nimport re\n\nimport aiohttp\nfrom langchain_community.document_loaders import AsyncHtmlLoader, WebBaseLoader\n\nfrom langflow.custom import Component\nfrom langflow.io import BoolInput, DropdownInput, MessageTextInput, Output, StrInput\nfrom langflow.schema import Data\nfrom langflow.schema.dataframe import DataFrame\nfrom langflow.schema.message import Message\n\n\nclass URLComponent(Component):\n display_name = \"URL\"\n description = (\n \"Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, \"\n \"or JSON, with options for cleaning and separating multiple outputs.\"\n )\n icon = \"layout-template\"\n name = \"URL\"\n\n inputs = [\n MessageTextInput(\n name=\"urls\",\n display_name=\"URLs\",\n is_list=True,\n tool_mode=True,\n placeholder=\"Enter a URL...\",\n list_add_label=\"Add URL\",\n ),\n DropdownInput(\n name=\"format\",\n display_name=\"Output Format\",\n info=(\n \"Output Format. Use 'Text' to extract text from the HTML, 'Raw HTML' for the raw HTML \"\n \"content, or 'JSON' to extract JSON from the HTML.\"\n ),\n options=[\"Text\", \"Raw HTML\", \"JSON\"],\n value=\"Text\",\n real_time_refresh=True,\n ),\n StrInput(\n name=\"separator\",\n display_name=\"Separator\",\n value=\"\\n\\n\",\n show=True,\n info=(\n \"Specify the separator to use between multiple outputs. Default for Text is '\\\\n\\\\n'. \"\n \"Default for Raw HTML is '\\\\n\\\\n'.\"\n ),\n ),\n BoolInput(\n name=\"clean_extra_whitespace\",\n display_name=\"Clean Extra Whitespace\",\n value=True,\n show=True,\n info=\"Whether to clean excessive blank lines in the text output. Only applies to 'Text' format.\",\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 Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n async def validate_json_content(self, url: str) -> bool:\n \"\"\"Validates if the URL content is actually JSON.\"\"\"\n try:\n async with aiohttp.ClientSession() as session, session.get(url) as response:\n http_ok = 200\n if response.status != http_ok:\n return False\n\n content = await response.text()\n try:\n json.loads(content)\n except json.JSONDecodeError:\n return False\n else:\n return True\n except (aiohttp.ClientError, asyncio.TimeoutError):\n # Log specific error for debugging if needed\n return False\n\n def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None) -> dict:\n \"\"\"Dynamically update fields based on selected format.\"\"\"\n if field_name == \"format\":\n is_text_mode = field_value == \"Text\"\n is_json_mode = field_value == \"JSON\"\n build_config[\"separator\"][\"value\"] = \"\\n\\n\" if is_text_mode else \"\\n\\n\"\n build_config[\"clean_extra_whitespace\"][\"show\"] = is_text_mode\n build_config[\"separator\"][\"show\"] = not is_json_mode\n return build_config\n\n def ensure_url(self, string: str) -> str:\n \"\"\"Ensures the given string is a valid URL.\"\"\"\n if not string.startswith((\"http://\", \"https://\")):\n string = \"http://\" + string\n\n url_regex = re.compile(\n r\"^(https?:\\/\\/)?\"\n r\"(www\\.)?\"\n r\"([a-zA-Z0-9.-]+)\"\n r\"(\\.[a-zA-Z]{2,})?\"\n r\"(:\\d+)?\"\n r\"(\\/[^\\s]*)?$\",\n re.IGNORECASE,\n )\n\n error_msg = \"Invalid URL - \" + string\n if not url_regex.match(string):\n raise ValueError(error_msg)\n\n return string\n\n def fetch_content(self) -> list[Data]:\n \"\"\"Fetch content based on selected format.\"\"\"\n urls = list({self.ensure_url(url.strip()) for url in self.urls if url.strip()})\n\n no_urls_msg = \"No valid URLs provided.\"\n if not urls:\n raise ValueError(no_urls_msg)\n\n # If JSON format is selected, validate JSON content first\n if self.format == \"JSON\":\n for url in urls:\n is_json = asyncio.run(self.validate_json_content(url))\n if not is_json:\n error_msg = \"Invalid JSON content from URL - \" + url\n raise ValueError(error_msg)\n\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\n docs = loader.load()\n\n if self.format == \"JSON\":\n data = []\n for doc in docs:\n try:\n json_content = json.loads(doc.page_content)\n data_dict = {\"text\": json.dumps(json_content, indent=2), **json_content, **doc.metadata}\n data.append(Data(**data_dict))\n except json.JSONDecodeError as err:\n source = doc.metadata.get(\"source\", \"unknown URL\")\n error_msg = \"Invalid JSON content from \" + source\n raise ValueError(error_msg) from err\n return data\n\n return [Data(text=doc.page_content, **doc.metadata) for doc in docs]\n\n def fetch_content_text(self) -> Message:\n \"\"\"Fetch content and return as formatted text.\"\"\"\n data = self.fetch_content()\n\n if self.format == \"JSON\":\n text_list = [item.text for item in data]\n result = \"\\n\".join(text_list)\n else:\n text_list = [item.text for item in data]\n if self.format == \"Text\" and self.clean_extra_whitespace:\n text_list = [re.sub(r\"\\n{3,}\", \"\\n\\n\", text) for text in text_list]\n result = self.separator.join(text_list)\n\n self.status = result\n return Message(text=result)\n\n def as_dataframe(self) -> DataFrame:\n \"\"\"Return fetched content as a DataFrame.\"\"\"\n return DataFrame(self.fetch_content())\n" + }, + "format": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Output Format", + "dynamic": false, + "info": "Output Format. Use 'Text' to extract text from the HTML, 'Raw HTML' for the raw HTML content, or 'JSON' to extract JSON from the HTML.", + "name": "format", + "options": [ + "Text", + "Raw HTML", + "JSON" + ], + "options_metadata": [], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Text" + }, + "separator": { + "_input_type": "StrInput", + "advanced": false, + "display_name": "Separator", + "dynamic": false, + "info": "Specify the separator to use between multiple outputs. Default for Text is '\\n\\n'. Default for Raw HTML is '\\n\\n'.", + "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" + }, + "tools_metadata": { + "_input_type": "TableInput", + "advanced": false, + "display_name": "Edit tools", + "dynamic": false, + "info": "", + "is_list": true, + "list_add_label": "Add More", + "name": "tools_metadata", + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "table_icon": "Hammer", + "table_options": { + "block_add": true, + "block_delete": true, + "block_edit": true, + "block_filter": true, + "block_hide": true, + "block_select": true, + "block_sort": true, + "description": "Modify tool names and descriptions to help agents understand when to use each tool.", + "field_parsers": { + "commands": "commands", + "name": [ + "snake_case", + "no_blank" + ] + }, + "hide_options": true + }, + "table_schema": { + "columns": [ + { + "default": "None", + "description": "Specify the name of the tool.", + "disable_edit": false, + "display_name": "Tool Name", + "edit_mode": "inline", + "filterable": false, + "formatter": "text", + "hidden": false, + "name": "name", + "sortable": false, + "type": "str" + }, + { + "default": "None", + "description": "Describe the purpose of the tool.", + "disable_edit": false, + "display_name": "Tool Description", + "edit_mode": "popover", + "filterable": false, + "formatter": "text", + "hidden": false, + "name": "description", + "sortable": false, + "type": "str" + }, + { + "default": "None", + "description": "The default identifiers for the tools and cannot be changed.", + "disable_edit": true, + "display_name": "Tool Identifiers", + "edit_mode": "inline", + "filterable": false, + "formatter": "text", + "hidden": true, + "name": "tags", + "sortable": false, + "type": "str" + }, + { + "default": true, + "description": "Indicates whether the tool is currently active. Set to True to activate this tool.", + "disable_edit": false, + "display_name": "Enable", + "edit_mode": "popover", + "filterable": true, + "formatter": "boolean", + "hidden": false, + "name": "status", + "sortable": true, + "type": "boolean" + } + ] + }, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "trigger_icon": "Hammer", + "trigger_text": "", + "type": "table", + "value": [ + { + "description": "fetch_content() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", + "name": "URL-fetch_content", + "status": true, + "tags": [ + "URL-fetch_content" + ] + }, + { + "description": "fetch_content_text() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", + "name": "URL-fetch_content_text", + "status": true, + "tags": [ + "URL-fetch_content_text" + ] + }, + { + "description": "as_dataframe() - Load and retrieve data from specified URLs. Supports output in plain text, raw HTML, or JSON, with options for cleaning and separating multiple outputs.", + "name": "URL-as_dataframe", + "status": true, + "tags": [ + "URL-as_dataframe" + ] + } + ] + }, + "urls": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "URLs", + "dynamic": false, + "info": "", + "input_types": [ + "Message" + ], + "list": true, + "list_add_label": "Add URL", + "load_from_db": false, + "name": "urls", + "placeholder": "Enter a URL...", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + } + }, + "tool_mode": true + }, + "showNode": true, + "type": "URL" + }, + "dragging": false, + "id": "URL-1uKdC", + "measured": { + "height": 590, + "width": 320 + }, + "position": { + "x": -572.0477930604186, + "y": -1020.4800468962654 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "note-4TLGl", + "node": { + "description": "# Financial Assistant Agents \n\nThe Financial Assistant Agent retrieves content and writes reports about financial queries.\n\n## Prerequisites\n\n* [Tavily AI Search key](https://docs.tavily.com/welcome)\n* [SambaNova API key](https://cloud.sambanova.ai/) \n\n## Quickstart\n\n1. In both **Agent** components and **SambaNova** component, add your SambaNova API key. \nIn the **Model Provider** field, select **Sambanova**, and select a model.\n3. In the **Tavily Search** component, add your **Tavily API key**.\n4. Click the **Playground** and ask `Why did Nvidia stock drop in January?`", + "display_name": "", + "documentation": "", + "template": { + "backgroundColor": "amber" + } + }, + "type": "note" + }, + "dragging": false, + "height": 630, + "id": "note-4TLGl", + "measured": { + "height": 630, + "width": 479 + }, + "position": { + "x": -1119.9423274254439, + "y": -433.35853649245854 + }, + "resizing": false, + "selected": false, + "type": "noteNode", + "width": 478 + }, + { + "data": { + "id": "SambaNovaModel-67RaX", + "node": { + "base_classes": [ + "LanguageModel", + "Message" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Generate text using Sambanova LLMs.", + "display_name": "SambaNova", + "documentation": "https://cloud.sambanova.ai/", + "edited": false, + "field_order": [ + "input_value", + "system_message", + "stream", + "base_url", + "model_name", + "api_key", + "max_tokens", + "top_p", + "temperature" + ], + "frozen": false, + "icon": "SambaNova", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Message", + "method": "text_response", + "name": "text_output", + "required_inputs": [], + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + }, + { + "allows_loop": false, + "cache": true, + "display_name": "Language Model", + "method": "build_model", + "name": "model_output", + "required_inputs": [ + "api_key" + ], + "selected": "LanguageModel", + "tool_mode": true, + "types": [ + "LanguageModel" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Sambanova API Key", + "dynamic": false, + "info": "The Sambanova API Key to use for the Sambanova model.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "api_key", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "base_url": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "SambaNova Cloud Base Url", + "dynamic": false, + "info": "The base URL of the Sambanova Cloud API. Defaults to https://api.sambanova.ai/v1/chat/completions. You can change this to use other urls like Sambastudio", + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "base_url", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "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 langchain_sambanova import ChatSambaNovaCloud\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.sambanova_constants import SAMBANOVA_MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.field_typing.range_spec import RangeSpec\nfrom langflow.io import DropdownInput, IntInput, SecretStrInput, SliderInput, StrInput\n\n\nclass SambaNovaComponent(LCModelComponent):\n display_name = \"SambaNova\"\n description = \"Generate text using Sambanova LLMs.\"\n documentation = \"https://cloud.sambanova.ai/\"\n icon = \"SambaNova\"\n name = \"SambaNovaModel\"\n\n inputs = [\n *LCModelComponent._base_inputs,\n StrInput(\n name=\"base_url\",\n display_name=\"SambaNova Cloud Base Url\",\n advanced=True,\n info=\"The base URL of the Sambanova Cloud API. \"\n \"Defaults to https://api.sambanova.ai/v1/chat/completions. \"\n \"You can change this to use other urls like Sambastudio\",\n ),\n DropdownInput(\n name=\"model_name\",\n display_name=\"Model Name\",\n advanced=False,\n options=SAMBANOVA_MODEL_NAMES,\n value=SAMBANOVA_MODEL_NAMES[0],\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"Sambanova API Key\",\n info=\"The Sambanova API Key to use for the Sambanova model.\",\n advanced=False,\n value=\"SAMBANOVA_API_KEY\",\n required=True,\n ),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n value=2048,\n info=\"The maximum number of tokens to generate.\",\n ),\n SliderInput(\n name=\"top_p\",\n display_name=\"top_p\",\n advanced=True,\n value=1.0,\n range_spec=RangeSpec(min=0, max=1, step=0.01),\n info=\"Model top_p\",\n ),\n SliderInput(\n name=\"temperature\", display_name=\"Temperature\", value=0.1, range_spec=RangeSpec(min=0, max=2, step=0.01)\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n sambanova_url = self.base_url\n sambanova_api_key = self.api_key\n model_name = self.model_name\n max_tokens = self.max_tokens\n top_p = self.top_p\n temperature = self.temperature\n\n api_key = SecretStr(sambanova_api_key).get_secret_value() if sambanova_api_key else None\n\n return ChatSambaNovaCloud(\n model=model_name,\n max_tokens=max_tokens or 1024,\n temperature=temperature or 0.07,\n top_p=top_p,\n sambanova_url=sambanova_url,\n sambanova_api_key=api_key,\n )\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": "" + }, + "max_tokens": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Tokens", + "dynamic": false, + "info": "The maximum number of tokens to generate.", + "list": false, + "list_add_label": "Add More", + "name": "max_tokens", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 2048 + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "", + "name": "model_name", + "options": [ + "Meta-Llama-3.3-70B-Instruct", + "Meta-Llama-3.1-8B-Instruct", + "Meta-Llama-3.1-70B-Instruct", + "Meta-Llama-3.1-405B-Instruct", + "DeepSeek-R1-Distill-Llama-70B", + "DeepSeek-R1", + "Meta-Llama-3.2-1B-Instruct", + "Meta-Llama-3.2-3B-Instruct", + "Llama-3.2-11B-Vision-Instruct", + "Llama-3.2-90B-Vision-Instruct", + "Qwen2.5-Coder-32B-Instruct", + "Qwen2.5-72B-Instruct", + "QwQ-32B-Preview", + "Qwen2-Audio-7B-Instruct" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Meta-Llama-3.1-405B-Instruct" + }, + "stream": { + "_input_type": "BoolInput", + "advanced": false, + "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": "You are a helpful assistant who writes financial reports based on user query.\nYou will get a financial and research summary.\nPlease write a financial report or answer using the provided context and input text only.\n{input}\nYour answer should be in the same language as the initial query." + }, + "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": 2, + "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 + }, + "top_p": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "top_p", + "dynamic": false, + "info": "Model top_p", + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "top_p", + "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": 1 + } + }, + "tool_mode": false + }, + "showNode": true, + "type": "SambaNovaModel" + }, + "dragging": false, + "id": "SambaNovaModel-67RaX", + "measured": { + "height": 656, + "width": 320 + }, + "position": { + "x": 1198.0223558351377, + "y": 154.90656214876276 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "Agent-8O2Iy", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "category": "agents", + "conditional_paths": [], + "custom_fields": {}, + "description": "Define the agent's instructions, then enter a task to complete using tools.", + "display_name": "Agent", + "documentation": "", + "edited": false, + "field_order": [ + "agent_llm", + "max_tokens", + "model_kwargs", + "json_mode", + "model_name", + "openai_api_base", + "api_key", + "temperature", + "seed", + "max_retries", + "timeout", + "system_prompt", + "tools", + "input_value", + "handle_parsing_errors", + "verbose", + "max_iterations", + "agent_description", + "memory", + "sender", + "sender_name", + "n_messages", + "session_id", + "order", + "template", + "add_current_date_tool" + ], + "frozen": false, + "icon": "bot", + "key": "Agent", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Response", + "hidden": null, + "method": "message_response", + "name": "response", + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 1.1732828199964098e-19, + "template": { + "_type": "Component", + "add_current_date_tool": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Current Date", + "dynamic": false, + "info": "If true, will add a tool to the agent that returns the current date.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "add_current_date_tool", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "agent_description": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "display_name": "Agent Description [Deprecated]", + "dynamic": false, + "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically. This feature is deprecated and will be removed in future versions.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "agent_description", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "A helpful assistant with access to the following tools:" + }, + "agent_llm": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", + "dynamic": false, + "info": "The provider of the language model that the agent will use to generate responses.", + "input_types": [], + "name": "agent_llm", + "options": [ + "Amazon Bedrock", + "Anthropic", + "Azure OpenAI", + "Google Generative AI", + "Groq", + "NVIDIA", + "OpenAI", + "SambaNova", + "Custom" + ], + "options_metadata": [ + { + "icon": "Amazon" + }, + { + "icon": "Anthropic" + }, + { + "icon": "Azure" + }, + { + "icon": "GoogleGenerativeAI" + }, + { + "icon": "Groq" + }, + { + "icon": "NVIDIA" + }, + { + "icon": "OpenAI" + }, + { + "icon": "SambaNova" + }, + { + "icon": "brain" + } + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "SambaNova" + }, + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Sambanova API Key", + "dynamic": false, + "info": "The Sambanova API Key to use for the Sambanova model.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "api_key", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "base_url": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "SambaNova Cloud Base Url", + "dynamic": false, + "info": "The base URL of the Sambanova Cloud API. Defaults to https://api.sambanova.ai/v1/chat/completions. You can change this to use other urls like Sambastudio", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "base_url", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS_DICT,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n\n # Validate tools\n if not self.tools:\n msg = \"Tools are required to run the agent. Please add at least one tool.\"\n raise ValueError(msg)\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools,\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def to_toolkit(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=self.get_tool_name(), tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n" + }, + "handle_parsing_errors": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Handle Parse Errors", + "dynamic": false, + "info": "Should the Agent fix errors when reading user input for better processing?", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "handle_parsing_errors", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "input_value": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "The input provided by the user for the agent to process.", + "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": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "max_iterations": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Iterations", + "dynamic": false, + "info": "The maximum number of attempts the agent can make to complete its task before it stops.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "max_iterations", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 15 + }, + "max_tokens": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Tokens", + "dynamic": false, + "info": "The maximum number of tokens to generate.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "max_tokens", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 2048 + }, + "memory": { + "_input_type": "HandleInput", + "advanced": true, + "display_name": "External Memory", + "dynamic": false, + "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", + "input_types": [ + "Memory" + ], + "list": false, + "list_add_label": "Add More", + "name": "memory", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": true, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "To see the model names, first choose a provider. Then, enter your API key and click the refresh button next to the model name.", + "input_types": [], + "name": "model_name", + "options": [ + "Meta-Llama-3.3-70B-Instruct", + "Meta-Llama-3.1-8B-Instruct", + "Meta-Llama-3.1-70B-Instruct", + "Meta-Llama-3.1-405B-Instruct", + "DeepSeek-R1-Distill-Llama-70B", + "DeepSeek-R1", + "Meta-Llama-3.2-1B-Instruct", + "Meta-Llama-3.2-3B-Instruct", + "Llama-3.2-11B-Vision-Instruct", + "Llama-3.2-90B-Vision-Instruct", + "Qwen2.5-Coder-32B-Instruct", + "Qwen2.5-72B-Instruct", + "QwQ-32B-Preview", + "Qwen2-Audio-7B-Instruct" + ], + "options_metadata": [], + "placeholder": "", + "real_time_refresh": false, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Meta-Llama-3.3-70B-Instruct" + }, + "n_messages": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Number of Messages", + "dynamic": false, + "info": "Number of messages to retrieve.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "n_messages", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 100 + }, + "order": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Order", + "dynamic": false, + "info": "Order of the messages.", + "input_types": [], + "name": "order", + "options": [ + "Ascending", + "Descending" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_metadata": true, + "type": "str", + "value": "Ascending" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Filter by sender type.", + "input_types": [], + "name": "sender", + "options": [ + "Machine", + "User", + "Machine and User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine and User" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Filter by sender name.", + "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": "" + }, + "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": "" + }, + "system_prompt": { + "_input_type": "MultilineInput", + "advanced": false, + "copy_field": false, + "display_name": "Agent Instructions", + "dynamic": false, + "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "system_prompt", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "You are a research agent, expert in web research, you must provide insights given the user question \n\n{input}\n\nThink step by step\nDo not call a tool if the input depends on another tool output that you do not have yet.\nDo not try to answer until you get all the tools output, if you do not have an answer yet, you can continue calling tools until you do.\nYour answer should be in the same language as the initial query.\nensure the tool calls are proper parsable json " + }, + "temperature": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "Temperature", + "dynamic": false, + "info": "", + "input_types": [], + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "temperature", + "placeholder": "", + "range_spec": { + "max": 2, + "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 + }, + "template": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "display_name": "Template", + "dynamic": false, + "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{sender_name}: {text}" + }, + "tools": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Tools", + "dynamic": false, + "info": "These are the tools that the agent can use to help with tasks.", + "input_types": [ + "Tool" + ], + "list": true, + "list_add_label": "Add More", + "name": "tools", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "top_p": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "top_p", + "dynamic": false, + "info": "Model top_p", + "input_types": [], + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "top_p", + "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": 1 + }, + "verbose": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Verbose", + "dynamic": false, + "info": "", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "verbose", + "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": "Agent" + }, + "dragging": false, + "id": "Agent-8O2Iy", + "measured": { + "height": 624, + "width": 320 + }, + "position": { + "x": -114.7705455447527, + "y": 395.9726937371793 + }, + "selected": false, + "type": "genericNode" + }, + { + "data": { + "id": "Agent-fvvwn", + "node": { + "base_classes": [ + "Message" + ], + "beta": false, + "category": "agents", + "conditional_paths": [], + "custom_fields": {}, + "description": "Define the agent's instructions, then enter a task to complete using tools.", + "display_name": "Agent", + "documentation": "", + "edited": false, + "field_order": [ + "agent_llm", + "max_tokens", + "model_kwargs", + "json_mode", + "model_name", + "openai_api_base", + "api_key", + "temperature", + "seed", + "max_retries", + "timeout", + "system_prompt", + "tools", + "input_value", + "handle_parsing_errors", + "verbose", + "max_iterations", + "agent_description", + "memory", + "sender", + "sender_name", + "n_messages", + "session_id", + "order", + "template", + "add_current_date_tool" + ], + "frozen": false, + "icon": "bot", + "key": "Agent", + "legacy": false, + "lf_version": "1.2.0", + "metadata": {}, + "minimized": false, + "output_types": [], + "outputs": [ + { + "allows_loop": false, + "cache": true, + "display_name": "Response", + "hidden": null, + "method": "message_response", + "name": "response", + "required_inputs": null, + "selected": "Message", + "tool_mode": true, + "types": [ + "Message" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "score": 5.283996070936036e-7, + "template": { + "_type": "Component", + "add_current_date_tool": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Current Date", + "dynamic": false, + "info": "If true, will add a tool to the agent that returns the current date.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "add_current_date_tool", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "agent_description": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "display_name": "Agent Description [Deprecated]", + "dynamic": false, + "info": "The description of the agent. This is only used when in Tool Mode. Defaults to 'A helpful assistant with access to the following tools:' and tools are added dynamically. This feature is deprecated and will be removed in future versions.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "agent_description", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "A helpful assistant with access to the following tools:" + }, + "agent_llm": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Model Provider", + "dynamic": false, + "info": "The provider of the language model that the agent will use to generate responses.", + "input_types": [], + "name": "agent_llm", + "options": [ + "Amazon Bedrock", + "Anthropic", + "Azure OpenAI", + "Google Generative AI", + "Groq", + "NVIDIA", + "OpenAI", + "SambaNova", + "Custom" + ], + "options_metadata": [ + { + "icon": "Amazon" + }, + { + "icon": "Anthropic" + }, + { + "icon": "Azure" + }, + { + "icon": "GoogleGenerativeAI" + }, + { + "icon": "Groq" + }, + { + "icon": "NVIDIA" + }, + { + "icon": "OpenAI" + }, + { + "icon": "SambaNova" + }, + { + "icon": "brain" + } + ], + "placeholder": "", + "real_time_refresh": true, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "SambaNova" + }, + "api_key": { + "_input_type": "SecretStrInput", + "advanced": false, + "display_name": "Sambanova API Key", + "dynamic": false, + "info": "The Sambanova API Key to use for the Sambanova model.", + "input_types": [ + "Message" + ], + "load_from_db": false, + "name": "api_key", + "password": true, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, + "base_url": { + "_input_type": "StrInput", + "advanced": true, + "display_name": "SambaNova Cloud Base Url", + "dynamic": false, + "info": "The base URL of the Sambanova Cloud API. Defaults to https://api.sambanova.ai/v1/chat/completions. You can change this to use other urls like Sambastudio", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "name": "base_url", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "code": { + "advanced": true, + "dynamic": true, + "fileTypes": [], + "file_path": "", + "info": "", + "input_types": [], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "code", + "password": false, + "placeholder": "", + "required": true, + "show": true, + "title_case": false, + "type": "code", + "value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS_DICT,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n\n # Validate tools\n if not self.tools:\n msg = \"Tools are required to run the agent. Please add at least one tool.\"\n raise ValueError(msg)\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools,\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def to_toolkit(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=self.get_tool_name(), tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n" + }, + "handle_parsing_errors": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Handle Parse Errors", + "dynamic": false, + "info": "Should the Agent fix errors when reading user input for better processing?", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "handle_parsing_errors", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "bool", + "value": true + }, + "input_value": { + "_input_type": "MessageTextInput", + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "The input provided by the user for the agent to process.", + "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": true, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "max_iterations": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Iterations", + "dynamic": false, + "info": "The maximum number of attempts the agent can make to complete its task before it stops.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "max_iterations", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 15 + }, + "max_tokens": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Max Tokens", + "dynamic": false, + "info": "The maximum number of tokens to generate.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "max_tokens", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 2048 + }, + "memory": { + "_input_type": "HandleInput", + "advanced": true, + "display_name": "External Memory", + "dynamic": false, + "info": "Retrieve messages from an external memory. If empty, it will use the Langflow tables.", + "input_types": [ + "Memory" + ], + "list": false, + "list_add_label": "Add More", + "name": "memory", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "model_name": { + "_input_type": "DropdownInput", + "advanced": false, + "combobox": true, + "dialog_inputs": {}, + "display_name": "Model Name", + "dynamic": false, + "info": "To see the model names, first choose a provider. Then, enter your API key and click the refresh button next to the model name.", + "input_types": [], + "name": "model_name", + "options": [ + "Meta-Llama-3.3-70B-Instruct", + "Meta-Llama-3.1-8B-Instruct", + "Meta-Llama-3.1-70B-Instruct", + "Meta-Llama-3.1-405B-Instruct", + "DeepSeek-R1-Distill-Llama-70B", + "DeepSeek-R1", + "Meta-Llama-3.2-1B-Instruct", + "Meta-Llama-3.2-3B-Instruct", + "Llama-3.2-11B-Vision-Instruct", + "Llama-3.2-90B-Vision-Instruct", + "Qwen2.5-Coder-32B-Instruct", + "Qwen2.5-72B-Instruct", + "QwQ-32B-Preview", + "Qwen2-Audio-7B-Instruct" + ], + "options_metadata": [], + "placeholder": "", + "real_time_refresh": false, + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Meta-Llama-3.3-70B-Instruct" + }, + "n_messages": { + "_input_type": "IntInput", + "advanced": true, + "display_name": "Number of Messages", + "dynamic": false, + "info": "Number of messages to retrieve.", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "n_messages", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "int", + "value": 100 + }, + "order": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Order", + "dynamic": false, + "info": "Order of the messages.", + "input_types": [], + "name": "order", + "options": [ + "Ascending", + "Descending" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": true, + "trace_as_metadata": true, + "type": "str", + "value": "Ascending" + }, + "sender": { + "_input_type": "DropdownInput", + "advanced": true, + "combobox": false, + "dialog_inputs": {}, + "display_name": "Sender Type", + "dynamic": false, + "info": "Filter by sender type.", + "input_types": [], + "name": "sender", + "options": [ + "Machine", + "User", + "Machine and User" + ], + "options_metadata": [], + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_metadata": true, + "type": "str", + "value": "Machine and User" + }, + "sender_name": { + "_input_type": "MessageTextInput", + "advanced": true, + "display_name": "Sender Name", + "dynamic": false, + "info": "Filter by sender name.", + "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": "" + }, + "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": "" + }, + "system_prompt": { + "_input_type": "MultilineInput", + "advanced": false, + "copy_field": false, + "display_name": "Agent Instructions", + "dynamic": false, + "info": "System Prompt: Initial instructions and context provided to guide the agent's behavior.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "system_prompt", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "You are a financial agent, \n\nYou are a helpful assistant, and you have access to several tools\n\nYou must provide insights given the user question, you can get links to the latest news, and you must open each link and research relevant information\n\n{input}\n\nThink step by step\nDo not call a tool if the input depends on another tool output you do not yet have.\nDo not try to answer until you get all the tools' output. If you do not have an answer yet, you can continue calling tools until you do.\nYour answer should be in the same language as the initial query.\nensure the tool calls are proper parsable json \nreturn the URLs used to create your response in the final answer" + }, + "temperature": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "Temperature", + "dynamic": false, + "info": "", + "input_types": [], + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "temperature", + "placeholder": "", + "range_spec": { + "max": 2, + "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 + }, + "template": { + "_input_type": "MultilineInput", + "advanced": true, + "copy_field": false, + "display_name": "Template", + "dynamic": false, + "info": "The template to use for formatting the data. It can contain the keys {text}, {sender} or any other key in the message data.", + "input_types": [ + "Message" + ], + "list": false, + "list_add_label": "Add More", + "load_from_db": false, + "multiline": true, + "name": "template", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "tool_mode": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "{sender_name}: {text}" + }, + "tools": { + "_input_type": "HandleInput", + "advanced": false, + "display_name": "Tools", + "dynamic": false, + "info": "These are the tools that the agent can use to help with tasks.", + "input_types": [ + "Tool" + ], + "list": true, + "list_add_label": "Add More", + "name": "tools", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_metadata": true, + "type": "other", + "value": "" + }, + "top_p": { + "_input_type": "SliderInput", + "advanced": true, + "display_name": "top_p", + "dynamic": false, + "info": "Model top_p", + "input_types": [], + "max_label": "", + "max_label_icon": "", + "min_label": "", + "min_label_icon": "", + "name": "top_p", + "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": 1 + }, + "verbose": { + "_input_type": "BoolInput", + "advanced": true, + "display_name": "Verbose", + "dynamic": false, + "info": "", + "input_types": [], + "list": false, + "list_add_label": "Add More", + "name": "verbose", + "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": "Agent" + }, + "dragging": false, + "id": "Agent-fvvwn", + "measured": { + "height": 624, + "width": 320 + }, + "position": { + "x": -77.82588111053366, + "y": -413.29696550729983 + }, + "selected": false, + "type": "genericNode" + } + ], + "viewport": { + "x": 874.6841085054481, + "y": 289.5843457571535, + "zoom": 0.7065898257947951 + } + }, + "description": "Financial assistant chatbot that uses specialized agents to research for financial information", + "endpoint_name": null, + "id": "a4bf0c39-af63-4590-8a17-398938518fe3", + "is_component": false, + "last_tested_version": "1.2.0", + "name": "Financial Agent", + "tags": [ + "assistants", + "agents", + "q-a", + "web-scraping", + "sambanova" + ] +} \ No newline at end of file diff --git a/src/frontend/tests/core/integrations/Financial Agent.spec.ts b/src/frontend/tests/core/integrations/Financial Agent.spec.ts new file mode 100644 index 000000000..dbcb33001 --- /dev/null +++ b/src/frontend/tests/core/integrations/Financial Agent.spec.ts @@ -0,0 +1,78 @@ +import { expect, test } from "@playwright/test"; +import * as dotenv from "dotenv"; +import path from "path"; +import { awaitBootstrapTest } from "../../utils/await-bootstrap-test"; +import { withEventDeliveryModes } from "../../utils/withEventDeliveryModes"; + +withEventDeliveryModes( + "Financial Agent", + { tag: ["@release", "@starter-projects"] }, + async ({ page }) => { + test.skip( + !process?.env?.TAVILY_API_KEY, + "TAVILY_API_KEY required to run this test", + ); + + test.skip( + !process?.env?.SAMBANOVA_API_KEY, + "SAMBANOVA_API_KEY required to run this test", + ); + + await page.goto("/"); + await awaitBootstrapTest(page); + + await page.getByTestId("side_nav_options_all-templates").click(); + await page.getByRole("heading", { name: "Financial Agent" }).click(); + + await page + .getByTestId("popover-anchor-input-api_key") + .nth(0) + .fill(process.env.TAVILY_API_KEY ?? ""); + + for (let i = 0; i < 2; i++) { + await page.getByTestId("dropdown_str_agent_llm").nth(i).click(); + await page.waitForTimeout(500); + await page.getByRole("option", { name: "SambaNova" }).click(); + } + + for (let i = 0; i < 3; i++) { + await page + .getByTestId("value-dropdown-dropdown_str_model_name") + .nth(i) + .click(); + await page.waitForTimeout(500); + + await page.getByRole("option").first().click(); + } + + for (let i = 1; i <= 3; i++) { + await page + .getByTestId("popover-anchor-input-api_key") + .nth(i) + .fill(process.env.SAMBANOVA_API_KEY ?? ""); + } + + await page.getByTestId("playground-btn-flow-io").click(); + + await page + .getByTestId("input-chat-playground") + .last() + .fill("Why did Nvidia stock drop in January?"); + + await page.getByTestId("button-send").last().click(); + + const stopButton = page.getByRole("button", { name: "Stop" }); + await stopButton.waitFor({ state: "visible", timeout: 30000 }); + + if (await stopButton.isVisible()) { + await expect(stopButton).toBeHidden({ timeout: 120000 }); + } + + const output = await page + .getByTestId("div-chat-message") + .last() + .innerText(); + expect(output.toLowerCase()).toContain("nvidia"); + expect(output.length).toBeGreaterThan(100); + }, +); diff --git a/src/frontend/tests/utils/select-gpt-model.ts b/src/frontend/tests/utils/select-gpt-model.ts index 0d6c6eecd..89fa06cb3 100644 --- a/src/frontend/tests/utils/select-gpt-model.ts +++ b/src/frontend/tests/utils/select-gpt-model.ts @@ -5,8 +5,8 @@ export const selectGptModel = async (page: Page) => { .getByTestId("dropdown_str_model_name") .count(); - if (gptModelDropdownCount > 0) { - await page.getByTestId("dropdown_str_model_name").nth(0).click(); - await page.getByTestId("gpt-4o-1-option").click(); + for (let i = 0; i < gptModelDropdownCount; i++) { + await page.getByTestId("dropdown_str_model_name").nth(i).click(); + await page.getByRole("option").first().click(); } };