From ae096a31678e56c49839f4448cc9e3861accae88 Mon Sep 17 00:00:00 2001 From: Cristhian Zanforlin Lousa <72977554+Cristhianzl@users.noreply.github.com> Date: Mon, 22 Jul 2024 18:30:30 -0300 Subject: [PATCH] test: add new tests based on new basic examples + vector store tests (#2879) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔧 (.github/workflows/typescript_test.yml): add BRAVE_SEARCH_API_KEY secret to workflow environment variables ✨ (frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx): add data-testid attribute to div element for testing purposes ✨ (frontend/tests/end-to-end/Hierarchical Tasks Agent.spec.ts): add end-to-end test for Hierarchical Tasks Agent ✨ (frontend/tests/end-to-end/Sequential Tasks Agent.spec.ts): add end-to-end test for Sequential Tasks Agent * updating hierarchical task json * 🔧 (.github/workflows/typescript_test.yml): add secrets for ASTRA_DB_APPLICATION_TOKEN and ASTRA_DB_API_ENDPOINT ♻️ (playwright.config.ts): refactor timeout value to 3 minutes 📝 (Complex Agent.spec.ts): update test to use BRAVE_SEARCH_API_KEY instead of OPENAI_API_KEY 📝 (Complex Agent.spec.ts): update test to use "apple" instead of "agile" 📝 (Hierarchical Tasks Agent.spec.ts): update test to use "langflow" instead of "agile" 📝 (Sequential Tasks Agent.spec.ts): update test to use waitForTimeout instead of isVisible ✨ (Vector Store.spec.ts): Add checks to skip tests if required environment variables are not set 🔧 (Vector Store.spec.ts): Update test logic to fill in required environment variables before running tests to ensure proper test execution --- .github/workflows/typescript_test.yml | 3 + .../starter_projects/Hierarchical Agent.json | 675 ++++++++++-------- src/frontend/playwright.config.ts | 2 +- .../components/chatView/chatMessage/index.tsx | 5 +- .../tests/end-to-end/Complex Agent.spec.ts | 102 +++ .../Hierarchical Tasks Agent.spec.ts | 106 +++ .../end-to-end/Sequential Tasks Agent.spec.ts | 106 +++ .../tests/end-to-end/Vector Store.spec.ts | 163 +++-- 8 files changed, 775 insertions(+), 387 deletions(-) create mode 100644 src/frontend/tests/end-to-end/Complex Agent.spec.ts create mode 100644 src/frontend/tests/end-to-end/Hierarchical Tasks Agent.spec.ts create mode 100644 src/frontend/tests/end-to-end/Sequential Tasks Agent.spec.ts diff --git a/.github/workflows/typescript_test.yml b/.github/workflows/typescript_test.yml index 4803762b1..c8c395ab0 100644 --- a/.github/workflows/typescript_test.yml +++ b/.github/workflows/typescript_test.yml @@ -56,6 +56,9 @@ jobs: env: OPENAI_API_KEY: ${{ inputs.openai_api_key || secrets.OPENAI_API_KEY }} STORE_API_KEY: ${{ inputs.store_api_key || secrets.STORE_API_KEY }} + BRAVE_SEARCH_API_KEY: "${{ secrets.BRAVE_SEARCH_API_KEY }}" + ASTRA_DB_APPLICATION_TOKEN: "${{ secrets.ASTRA_DB_APPLICATION_TOKEN }}" + ASTRA_DB_API_ENDPOINT: "${{ secrets.ASTRA_DB_API_ENDPOINT }}" outputs: failed: ${{ steps.check-failure.outputs.failed }} steps: diff --git a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json index ddf73aafd..daa00bdd7 100644 --- a/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json +++ b/src/backend/base/langflow/initial_setup/starter_projects/Hierarchical Agent.json @@ -6,7 +6,7 @@ "data": { "sourceHandle": { "dataType": "HierarchicalCrewComponent", - "id": "HierarchicalCrewComponent-JNKGT", + "id": "HierarchicalCrewComponent-Y0Uvf", "name": "output", "output_types": [ "Message" @@ -14,26 +14,26 @@ }, "targetHandle": { "fieldName": "input_value", - "id": "ChatOutput-IACYe", + "id": "ChatOutput-VzVJK", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-HierarchicalCrewComponent-JNKGT{œdataTypeœ:œHierarchicalCrewComponentœ,œidœ:œHierarchicalCrewComponent-JNKGTœ,œnameœ:œoutputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-IACYe{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-IACYeœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "id": "reactflow__edge-HierarchicalCrewComponent-Y0Uvf{œdataTypeœ:œHierarchicalCrewComponentœ,œidœ:œHierarchicalCrewComponent-Y0Uvfœ,œnameœ:œoutputœ,œoutput_typesœ:[œMessageœ]}-ChatOutput-VzVJK{œfieldNameœ:œinput_valueœ,œidœ:œChatOutput-VzVJKœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", "selected": false, - "source": "HierarchicalCrewComponent-JNKGT", - "sourceHandle": "{œdataTypeœ: œHierarchicalCrewComponentœ, œidœ: œHierarchicalCrewComponent-JNKGTœ, œnameœ: œoutputœ, œoutput_typesœ: [œMessageœ]}", - "target": "ChatOutput-IACYe", - "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-IACYeœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "source": "HierarchicalCrewComponent-Y0Uvf", + "sourceHandle": "{œdataTypeœ: œHierarchicalCrewComponentœ, œidœ: œHierarchicalCrewComponent-Y0Uvfœ, œnameœ: œoutputœ, œoutput_typesœ: [œMessageœ]}", + "target": "ChatOutput-VzVJK", + "targetHandle": "{œfieldNameœ: œinput_valueœ, œidœ: œChatOutput-VzVJKœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "className": "", "data": { "sourceHandle": { "dataType": "HierarchicalTaskComponent", - "id": "HierarchicalTaskComponent-vvJXu", + "id": "HierarchicalTaskComponent-hE8H5", "name": "task_output", "output_types": [ "HierarchicalTask" @@ -41,26 +41,26 @@ }, "targetHandle": { "fieldName": "tasks", - "id": "HierarchicalCrewComponent-JNKGT", + "id": "HierarchicalCrewComponent-Y0Uvf", "inputTypes": [ "HierarchicalTask" ], "type": "other" } }, - "id": "reactflow__edge-HierarchicalTaskComponent-vvJXu{œdataTypeœ:œHierarchicalTaskComponentœ,œidœ:œHierarchicalTaskComponent-vvJXuœ,œnameœ:œtask_outputœ,œoutput_typesœ:[œHierarchicalTaskœ]}-HierarchicalCrewComponent-JNKGT{œfieldNameœ:œtasksœ,œidœ:œHierarchicalCrewComponent-JNKGTœ,œinputTypesœ:[œHierarchicalTaskœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-HierarchicalTaskComponent-hE8H5{œdataTypeœ:œHierarchicalTaskComponentœ,œidœ:œHierarchicalTaskComponent-hE8H5œ,œnameœ:œtask_outputœ,œoutput_typesœ:[œHierarchicalTaskœ]}-HierarchicalCrewComponent-Y0Uvf{œfieldNameœ:œtasksœ,œidœ:œHierarchicalCrewComponent-Y0Uvfœ,œinputTypesœ:[œHierarchicalTaskœ],œtypeœ:œotherœ}", "selected": false, - "source": "HierarchicalTaskComponent-vvJXu", - "sourceHandle": "{œdataTypeœ: œHierarchicalTaskComponentœ, œidœ: œHierarchicalTaskComponent-vvJXuœ, œnameœ: œtask_outputœ, œoutput_typesœ: [œHierarchicalTaskœ]}", - "target": "HierarchicalCrewComponent-JNKGT", - "targetHandle": "{œfieldNameœ: œtasksœ, œidœ: œHierarchicalCrewComponent-JNKGTœ, œinputTypesœ: [œHierarchicalTaskœ], œtypeœ: œotherœ}" + "source": "HierarchicalTaskComponent-hE8H5", + "sourceHandle": "{œdataTypeœ: œHierarchicalTaskComponentœ, œidœ: œHierarchicalTaskComponent-hE8H5œ, œnameœ: œtask_outputœ, œoutput_typesœ: [œHierarchicalTaskœ]}", + "target": "HierarchicalCrewComponent-Y0Uvf", + "targetHandle": "{œfieldNameœ: œtasksœ, œidœ: œHierarchicalCrewComponent-Y0Uvfœ, œinputTypesœ: [œHierarchicalTaskœ], œtypeœ: œotherœ}" }, { "className": "", "data": { "sourceHandle": { "dataType": "CrewAIAgentComponent", - "id": "CrewAIAgentComponent-XU5MB", + "id": "CrewAIAgentComponent-EbpXd", "name": "output", "output_types": [ "Agent" @@ -68,26 +68,26 @@ }, "targetHandle": { "fieldName": "agents", - "id": "HierarchicalCrewComponent-JNKGT", + "id": "HierarchicalCrewComponent-Y0Uvf", "inputTypes": [ "Agent" ], "type": "other" } }, - "id": "reactflow__edge-CrewAIAgentComponent-XU5MB{œdataTypeœ:œCrewAIAgentComponentœ,œidœ:œCrewAIAgentComponent-XU5MBœ,œnameœ:œoutputœ,œoutput_typesœ:[œAgentœ]}-HierarchicalCrewComponent-JNKGT{œfieldNameœ:œagentsœ,œidœ:œHierarchicalCrewComponent-JNKGTœ,œinputTypesœ:[œAgentœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-CrewAIAgentComponent-EbpXd{œdataTypeœ:œCrewAIAgentComponentœ,œidœ:œCrewAIAgentComponent-EbpXdœ,œnameœ:œoutputœ,œoutput_typesœ:[œAgentœ]}-HierarchicalCrewComponent-Y0Uvf{œfieldNameœ:œagentsœ,œidœ:œHierarchicalCrewComponent-Y0Uvfœ,œinputTypesœ:[œAgentœ],œtypeœ:œotherœ}", "selected": false, - "source": "CrewAIAgentComponent-XU5MB", - "sourceHandle": "{œdataTypeœ: œCrewAIAgentComponentœ, œidœ: œCrewAIAgentComponent-XU5MBœ, œnameœ: œoutputœ, œoutput_typesœ: [œAgentœ]}", - "target": "HierarchicalCrewComponent-JNKGT", - "targetHandle": "{œfieldNameœ: œagentsœ, œidœ: œHierarchicalCrewComponent-JNKGTœ, œinputTypesœ: [œAgentœ], œtypeœ: œotherœ}" + "source": "CrewAIAgentComponent-EbpXd", + "sourceHandle": "{œdataTypeœ: œCrewAIAgentComponentœ, œidœ: œCrewAIAgentComponent-EbpXdœ, œnameœ: œoutputœ, œoutput_typesœ: [œAgentœ]}", + "target": "HierarchicalCrewComponent-Y0Uvf", + "targetHandle": "{œfieldNameœ: œagentsœ, œidœ: œHierarchicalCrewComponent-Y0Uvfœ, œinputTypesœ: [œAgentœ], œtypeœ: œotherœ}" }, { "className": "", "data": { "sourceHandle": { "dataType": "OpenAIModel", - "id": "OpenAIModel-xoZ41", + "id": "OpenAIModel-Yjtpu", "name": "model_output", "output_types": [ "LanguageModel" @@ -95,26 +95,26 @@ }, "targetHandle": { "fieldName": "llm", - "id": "CrewAIAgentComponent-XU5MB", + "id": "CrewAIAgentComponent-EbpXd", "inputTypes": [ "LanguageModel" ], "type": "other" } }, - "id": "reactflow__edge-OpenAIModel-xoZ41{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-xoZ41œ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-CrewAIAgentComponent-XU5MB{œfieldNameœ:œllmœ,œidœ:œCrewAIAgentComponent-XU5MBœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-OpenAIModel-Yjtpu{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Yjtpuœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-CrewAIAgentComponent-EbpXd{œfieldNameœ:œllmœ,œidœ:œCrewAIAgentComponent-EbpXdœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", "selected": false, - "source": "OpenAIModel-xoZ41", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-xoZ41œ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", - "target": "CrewAIAgentComponent-XU5MB", - "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œCrewAIAgentComponent-XU5MBœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" + "source": "OpenAIModel-Yjtpu", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-Yjtpuœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", + "target": "CrewAIAgentComponent-EbpXd", + "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œCrewAIAgentComponent-EbpXdœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" }, { "className": "", "data": { "sourceHandle": { "dataType": "CrewAIAgentComponent", - "id": "CrewAIAgentComponent-Trkfd", + "id": "CrewAIAgentComponent-9D8ao", "name": "output", "output_types": [ "Agent" @@ -122,26 +122,26 @@ }, "targetHandle": { "fieldName": "manager_agent", - "id": "HierarchicalCrewComponent-JNKGT", + "id": "HierarchicalCrewComponent-Y0Uvf", "inputTypes": [ "Agent" ], "type": "other" } }, - "id": "reactflow__edge-CrewAIAgentComponent-Trkfd{œdataTypeœ:œCrewAIAgentComponentœ,œidœ:œCrewAIAgentComponent-Trkfdœ,œnameœ:œoutputœ,œoutput_typesœ:[œAgentœ]}-HierarchicalCrewComponent-JNKGT{œfieldNameœ:œmanager_agentœ,œidœ:œHierarchicalCrewComponent-JNKGTœ,œinputTypesœ:[œAgentœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-CrewAIAgentComponent-9D8ao{œdataTypeœ:œCrewAIAgentComponentœ,œidœ:œCrewAIAgentComponent-9D8aoœ,œnameœ:œoutputœ,œoutput_typesœ:[œAgentœ]}-HierarchicalCrewComponent-Y0Uvf{œfieldNameœ:œmanager_agentœ,œidœ:œHierarchicalCrewComponent-Y0Uvfœ,œinputTypesœ:[œAgentœ],œtypeœ:œotherœ}", "selected": false, - "source": "CrewAIAgentComponent-Trkfd", - "sourceHandle": "{œdataTypeœ: œCrewAIAgentComponentœ, œidœ: œCrewAIAgentComponent-Trkfdœ, œnameœ: œoutputœ, œoutput_typesœ: [œAgentœ]}", - "target": "HierarchicalCrewComponent-JNKGT", - "targetHandle": "{œfieldNameœ: œmanager_agentœ, œidœ: œHierarchicalCrewComponent-JNKGTœ, œinputTypesœ: [œAgentœ], œtypeœ: œotherœ}" + "source": "CrewAIAgentComponent-9D8ao", + "sourceHandle": "{œdataTypeœ: œCrewAIAgentComponentœ, œidœ: œCrewAIAgentComponent-9D8aoœ, œnameœ: œoutputœ, œoutput_typesœ: [œAgentœ]}", + "target": "HierarchicalCrewComponent-Y0Uvf", + "targetHandle": "{œfieldNameœ: œmanager_agentœ, œidœ: œHierarchicalCrewComponent-Y0Uvfœ, œinputTypesœ: [œAgentœ], œtypeœ: œotherœ}" }, { "className": "", "data": { "sourceHandle": { "dataType": "OpenAIModel", - "id": "OpenAIModel-0cqIu", + "id": "OpenAIModel-HgNnu", "name": "model_output", "output_types": [ "LanguageModel" @@ -149,26 +149,26 @@ }, "targetHandle": { "fieldName": "llm", - "id": "CrewAIAgentComponent-Trkfd", + "id": "CrewAIAgentComponent-9D8ao", "inputTypes": [ "LanguageModel" ], "type": "other" } }, - "id": "reactflow__edge-OpenAIModel-0cqIu{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-0cqIuœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-CrewAIAgentComponent-Trkfd{œfieldNameœ:œllmœ,œidœ:œCrewAIAgentComponent-Trkfdœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", + "id": "reactflow__edge-OpenAIModel-HgNnu{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-HgNnuœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-CrewAIAgentComponent-9D8ao{œfieldNameœ:œllmœ,œidœ:œCrewAIAgentComponent-9D8aoœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", "selected": false, - "source": "OpenAIModel-0cqIu", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-0cqIuœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", - "target": "CrewAIAgentComponent-Trkfd", - "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œCrewAIAgentComponent-Trkfdœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" + "source": "OpenAIModel-HgNnu", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-HgNnuœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", + "target": "CrewAIAgentComponent-9D8ao", + "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œCrewAIAgentComponent-9D8aoœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" }, { "className": "", "data": { "sourceHandle": { "dataType": "Prompt", - "id": "Prompt-5o2wP", + "id": "Prompt-eqGhn", "name": "prompt", "output_types": [ "Message" @@ -176,25 +176,25 @@ }, "targetHandle": { "fieldName": "task_description", - "id": "HierarchicalTaskComponent-vvJXu", + "id": "HierarchicalTaskComponent-hE8H5", "inputTypes": [ "Message" ], "type": "str" } }, - "id": "reactflow__edge-Prompt-5o2wP{œdataTypeœ:œPromptœ,œidœ:œPrompt-5o2wPœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-HierarchicalTaskComponent-vvJXu{œfieldNameœ:œtask_descriptionœ,œidœ:œHierarchicalTaskComponent-vvJXuœ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", - "source": "Prompt-5o2wP", - "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-5o2wPœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", - "target": "HierarchicalTaskComponent-vvJXu", - "targetHandle": "{œfieldNameœ: œtask_descriptionœ, œidœ: œHierarchicalTaskComponent-vvJXuœ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" + "id": "reactflow__edge-Prompt-eqGhn{œdataTypeœ:œPromptœ,œidœ:œPrompt-eqGhnœ,œnameœ:œpromptœ,œoutput_typesœ:[œMessageœ]}-HierarchicalTaskComponent-hE8H5{œfieldNameœ:œtask_descriptionœ,œidœ:œHierarchicalTaskComponent-hE8H5œ,œinputTypesœ:[œMessageœ],œtypeœ:œstrœ}", + "source": "Prompt-eqGhn", + "sourceHandle": "{œdataTypeœ: œPromptœ, œidœ: œPrompt-eqGhnœ, œnameœ: œpromptœ, œoutput_typesœ: [œMessageœ]}", + "target": "HierarchicalTaskComponent-hE8H5", + "targetHandle": "{œfieldNameœ: œtask_descriptionœ, œidœ: œHierarchicalTaskComponent-hE8H5œ, œinputTypesœ: [œMessageœ], œtypeœ: œstrœ}" }, { "className": "", "data": { "sourceHandle": { "dataType": "ChatInput", - "id": "ChatInput-kHOLm", + "id": "ChatInput-xgRl9", "name": "message", "output_types": [ "Message" @@ -202,7 +202,7 @@ }, "targetHandle": { "fieldName": "query", - "id": "Prompt-5o2wP", + "id": "Prompt-eqGhn", "inputTypes": [ "Message", "Text" @@ -210,45 +210,18 @@ "type": "str" } }, - "id": "reactflow__edge-ChatInput-kHOLm{œdataTypeœ:œChatInputœ,œidœ:œChatInput-kHOLmœ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-5o2wP{œfieldNameœ:œqueryœ,œidœ:œPrompt-5o2wPœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", - "source": "ChatInput-kHOLm", - "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-kHOLmœ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", - "target": "Prompt-5o2wP", - "targetHandle": "{œfieldNameœ: œqueryœ, œidœ: œPrompt-5o2wPœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" - }, - { - "className": "", - "data": { - "sourceHandle": { - "dataType": "SearchAPITool", - "id": "SearchAPITool-fmETj", - "name": "tool", - "output_types": [ - "Tool" - ] - }, - "targetHandle": { - "fieldName": "tools", - "id": "CrewAIAgentComponent-XU5MB", - "inputTypes": [ - "Tool" - ], - "type": "other" - } - }, - "id": "reactflow__edge-SearchAPITool-fmETj{œdataTypeœ:œSearchAPIToolœ,œidœ:œSearchAPITool-fmETjœ,œnameœ:œtoolœ,œoutput_typesœ:[œToolœ]}-CrewAIAgentComponent-XU5MB{œfieldNameœ:œtoolsœ,œidœ:œCrewAIAgentComponent-XU5MBœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", - "selected": false, - "source": "SearchAPITool-fmETj", - "sourceHandle": "{œdataTypeœ: œSearchAPIToolœ, œidœ: œSearchAPITool-fmETjœ, œnameœ: œtoolœ, œoutput_typesœ: [œToolœ]}", - "target": "CrewAIAgentComponent-XU5MB", - "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œCrewAIAgentComponent-XU5MBœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" + "id": "reactflow__edge-ChatInput-xgRl9{œdataTypeœ:œChatInputœ,œidœ:œChatInput-xgRl9œ,œnameœ:œmessageœ,œoutput_typesœ:[œMessageœ]}-Prompt-eqGhn{œfieldNameœ:œqueryœ,œidœ:œPrompt-eqGhnœ,œinputTypesœ:[œMessageœ,œTextœ],œtypeœ:œstrœ}", + "source": "ChatInput-xgRl9", + "sourceHandle": "{œdataTypeœ: œChatInputœ, œidœ: œChatInput-xgRl9œ, œnameœ: œmessageœ, œoutput_typesœ: [œMessageœ]}", + "target": "Prompt-eqGhn", + "targetHandle": "{œfieldNameœ: œqueryœ, œidœ: œPrompt-eqGhnœ, œinputTypesœ: [œMessageœ, œTextœ], œtypeœ: œstrœ}" }, { "className": "", "data": { "sourceHandle": { "dataType": "CrewAIAgentComponent", - "id": "CrewAIAgentComponent-V2kfR", + "id": "CrewAIAgentComponent-UMpxO", "name": "output", "output_types": [ "Agent" @@ -256,25 +229,25 @@ }, "targetHandle": { "fieldName": "agents", - "id": "HierarchicalCrewComponent-JNKGT", + "id": "HierarchicalCrewComponent-Y0Uvf", "inputTypes": [ "Agent" ], "type": "other" } }, - "id": "reactflow__edge-CrewAIAgentComponent-V2kfR{œdataTypeœ:œCrewAIAgentComponentœ,œidœ:œCrewAIAgentComponent-V2kfRœ,œnameœ:œoutputœ,œoutput_typesœ:[œAgentœ]}-HierarchicalCrewComponent-JNKGT{œfieldNameœ:œagentsœ,œidœ:œHierarchicalCrewComponent-JNKGTœ,œinputTypesœ:[œAgentœ],œtypeœ:œotherœ}", - "source": "CrewAIAgentComponent-V2kfR", - "sourceHandle": "{œdataTypeœ: œCrewAIAgentComponentœ, œidœ: œCrewAIAgentComponent-V2kfRœ, œnameœ: œoutputœ, œoutput_typesœ: [œAgentœ]}", - "target": "HierarchicalCrewComponent-JNKGT", - "targetHandle": "{œfieldNameœ: œagentsœ, œidœ: œHierarchicalCrewComponent-JNKGTœ, œinputTypesœ: [œAgentœ], œtypeœ: œotherœ}" + "id": "reactflow__edge-CrewAIAgentComponent-UMpxO{œdataTypeœ:œCrewAIAgentComponentœ,œidœ:œCrewAIAgentComponent-UMpxOœ,œnameœ:œoutputœ,œoutput_typesœ:[œAgentœ]}-HierarchicalCrewComponent-Y0Uvf{œfieldNameœ:œagentsœ,œidœ:œHierarchicalCrewComponent-Y0Uvfœ,œinputTypesœ:[œAgentœ],œtypeœ:œotherœ}", + "source": "CrewAIAgentComponent-UMpxO", + "sourceHandle": "{œdataTypeœ: œCrewAIAgentComponentœ, œidœ: œCrewAIAgentComponent-UMpxOœ, œnameœ: œoutputœ, œoutput_typesœ: [œAgentœ]}", + "target": "HierarchicalCrewComponent-Y0Uvf", + "targetHandle": "{œfieldNameœ: œagentsœ, œidœ: œHierarchicalCrewComponent-Y0Uvfœ, œinputTypesœ: [œAgentœ], œtypeœ: œotherœ}" }, { "className": "", "data": { "sourceHandle": { "dataType": "OpenAIModel", - "id": "OpenAIModel-xoZ41", + "id": "OpenAIModel-Yjtpu", "name": "model_output", "output_types": [ "LanguageModel" @@ -282,18 +255,44 @@ }, "targetHandle": { "fieldName": "llm", - "id": "CrewAIAgentComponent-V2kfR", + "id": "CrewAIAgentComponent-UMpxO", "inputTypes": [ "LanguageModel" ], "type": "other" } }, - "id": "reactflow__edge-OpenAIModel-xoZ41{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-xoZ41œ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-CrewAIAgentComponent-V2kfR{œfieldNameœ:œllmœ,œidœ:œCrewAIAgentComponent-V2kfRœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", - "source": "OpenAIModel-xoZ41", - "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-xoZ41œ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", - "target": "CrewAIAgentComponent-V2kfR", - "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œCrewAIAgentComponent-V2kfRœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" + "id": "reactflow__edge-OpenAIModel-Yjtpu{œdataTypeœ:œOpenAIModelœ,œidœ:œOpenAIModel-Yjtpuœ,œnameœ:œmodel_outputœ,œoutput_typesœ:[œLanguageModelœ]}-CrewAIAgentComponent-UMpxO{œfieldNameœ:œllmœ,œidœ:œCrewAIAgentComponent-UMpxOœ,œinputTypesœ:[œLanguageModelœ],œtypeœ:œotherœ}", + "source": "OpenAIModel-Yjtpu", + "sourceHandle": "{œdataTypeœ: œOpenAIModelœ, œidœ: œOpenAIModel-Yjtpuœ, œnameœ: œmodel_outputœ, œoutput_typesœ: [œLanguageModelœ]}", + "target": "CrewAIAgentComponent-UMpxO", + "targetHandle": "{œfieldNameœ: œllmœ, œidœ: œCrewAIAgentComponent-UMpxOœ, œinputTypesœ: [œLanguageModelœ], œtypeœ: œotherœ}" + }, + { + "className": "", + "data": { + "sourceHandle": { + "dataType": "SearchAPI", + "id": "SearchAPI-Yokat", + "name": "api_build_tool", + "output_types": [ + "Tool" + ] + }, + "targetHandle": { + "fieldName": "tools", + "id": "CrewAIAgentComponent-EbpXd", + "inputTypes": [ + "Tool" + ], + "type": "other" + } + }, + "id": "reactflow__edge-SearchAPI-Yokat{œdataTypeœ:œSearchAPIœ,œidœ:œSearchAPI-Yokatœ,œnameœ:œapi_build_toolœ,œoutput_typesœ:[œToolœ]}-CrewAIAgentComponent-EbpXd{œfieldNameœ:œtoolsœ,œidœ:œCrewAIAgentComponent-EbpXdœ,œinputTypesœ:[œToolœ],œtypeœ:œotherœ}", + "source": "SearchAPI-Yokat", + "sourceHandle": "{œdataTypeœ: œSearchAPIœ, œidœ: œSearchAPI-Yokatœ, œnameœ: œapi_build_toolœ, œoutput_typesœ: [œToolœ]}", + "target": "CrewAIAgentComponent-EbpXd", + "targetHandle": "{œfieldNameœ: œtoolsœ, œidœ: œCrewAIAgentComponent-EbpXdœ, œinputTypesœ: [œToolœ], œtypeœ: œotherœ}" } ], "nodes": [ @@ -301,7 +300,7 @@ "data": { "description": "Represents a group of agents, defining how they should collaborate and the tasks they should perform.", "display_name": "Hierarchical Crew", - "id": "HierarchicalCrewComponent-JNKGT", + "id": "HierarchicalCrewComponent-Y0Uvf", "node": { "base_classes": [ "Message" @@ -532,19 +531,21 @@ "type": "HierarchicalCrewComponent" }, "dragging": false, - "height": 459, - "id": "HierarchicalCrewComponent-JNKGT", + "height": 516, + "id": "HierarchicalCrewComponent-Y0Uvf", "position": { "x": 568, "y": 352.296875 }, "selected": false, "type": "genericNode", - "width": 384 + "width": 432 }, { "data": { - "id": "OpenAIModel-xoZ41", + "description": "Generates text using OpenAI LLMs.", + "display_name": "OpenAI", + "id": "OpenAIModel-Yjtpu", "node": { "base_classes": [ "LanguageModel", @@ -559,16 +560,16 @@ "edited": false, "field_order": [ "input_value", + "system_message", + "stream", "max_tokens", "model_kwargs", "json_mode", "output_schema", "model_name", "openai_api_base", - "openai_api_key", + "api_key", "temperature", - "stream", - "system_message", "seed" ], "frozen": false, @@ -601,6 +602,22 @@ "pinned": false, "template": { "_type": "Component", + "api_key": { + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "The OpenAI API Key to use for the OpenAI model.", + "input_types": [], + "load_from_db": false, + "name": "api_key", + "password": true, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -617,7 +634,7 @@ "show": true, "title_case": false, "type": "code", - "value": "import operator\nfrom functools import reduce\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n name = \"OpenAIModel\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n info=\"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.\",\n ),\n SecretStrInput(\n name=\"openai_api_key\",\n display_name=\"OpenAI API Key\",\n info=\"The OpenAI API Key to use for the OpenAI model.\",\n advanced=False,\n value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n BoolInput(name=\"stream\", display_name=\"Stream\", info=STREAM_INFO_TEXT, advanced=True),\n StrInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"System message to pass to the model.\",\n advanced=True,\n ),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n # self.output_schea is a list of dictionarie s\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output # type: ignore\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n" + "value": "import operator\nfrom functools import reduce\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n name = \"OpenAIModel\"\n\n inputs = LCModelComponent._base_inputs + [\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n info=\"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"The OpenAI API Key to use for the OpenAI model.\",\n advanced=False,\n value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n # self.output_schema is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n seed=seed,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output # type: ignore\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n" }, "input_value": { "advanced": false, @@ -689,8 +706,10 @@ "display_name": "Model Name", "dynamic": false, "info": "", + "load_from_db": false, "name": "model_name", "options": [ + "gpt-4o-mini", "gpt-4o", "gpt-4-turbo", "gpt-4-turbo-preview", @@ -704,7 +723,7 @@ "title_case": false, "trace_as_metadata": true, "type": "str", - "value": "gpt-3.5-turbo" + "value": "gpt-4o" }, "openai_api_base": { "advanced": true, @@ -722,22 +741,6 @@ "type": "str", "value": "" }, - "openai_api_key": { - "advanced": false, - "display_name": "OpenAI API Key", - "dynamic": false, - "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [], - "load_from_db": true, - "name": "openai_api_key", - "password": true, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, "output_schema": { "advanced": true, "display_name": "Schema", @@ -788,6 +791,9 @@ "display_name": "System Message", "dynamic": false, "info": "System message to pass to the model.", + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "system_message", @@ -795,6 +801,7 @@ "required": false, "show": true, "title_case": false, + "trace_as_input": true, "trace_as_metadata": true, "type": "str", "value": "" @@ -819,8 +826,8 @@ "type": "OpenAIModel" }, "dragging": false, - "height": 623, - "id": "OpenAIModel-xoZ41", + "height": 700, + "id": "OpenAIModel-Yjtpu", "position": { "x": -1222.8457213471152, "y": 699.9983009041272 @@ -831,13 +838,13 @@ }, "selected": false, "type": "genericNode", - "width": 384 + "width": 432 }, { "data": { "description": "Display a chat message in the Playground.", "display_name": "Chat Output", - "id": "ChatOutput-IACYe", + "id": "ChatOutput-VzVJK", "node": { "base_classes": [ "Message" @@ -1012,8 +1019,8 @@ "type": "ChatOutput" }, "dragging": false, - "height": 309, - "id": "ChatOutput-IACYe", + "height": 347, + "id": "ChatOutput-VzVJK", "position": { "x": 1070.9148596889393, "y": 499.80777483894144 @@ -1024,13 +1031,13 @@ }, "selected": false, "type": "genericNode", - "width": 384 + "width": 432 }, { "data": { "description": "Each task must have a description, an expected output and an agent responsible for execution.", "display_name": "Hierarchical Task", - "id": "HierarchicalTaskComponent-vvJXu", + "id": "HierarchicalTaskComponent-hE8H5", "node": { "base_classes": [ "HierarchicalTask" @@ -1149,8 +1156,8 @@ "type": "HierarchicalTaskComponent" }, "dragging": false, - "height": 455, - "id": "HierarchicalTaskComponent-vvJXu", + "height": 480, + "id": "HierarchicalTaskComponent-hE8H5", "position": { "x": 63.673086094601445, "y": -375.5883288441139 @@ -1161,13 +1168,13 @@ }, "selected": false, "type": "genericNode", - "width": 384 + "width": 432 }, { "data": { "description": "Represents an agent of CrewAI.", "display_name": "CrewAI Agent", - "id": "CrewAIAgentComponent-XU5MB", + "id": "CrewAIAgentComponent-EbpXd", "node": { "base_classes": [ "Agent" @@ -1407,8 +1414,8 @@ "type": "CrewAIAgentComponent" }, "dragging": false, - "height": 665, - "id": "CrewAIAgentComponent-XU5MB", + "height": 746, + "id": "CrewAIAgentComponent-EbpXd", "position": { "x": -479.3544836211363, "y": 184.85118314299666 @@ -1419,13 +1426,13 @@ }, "selected": false, "type": "genericNode", - "width": 384 + "width": 432 }, { "data": { "description": "Represents an agent of CrewAI.", "display_name": "CrewAI Agent", - "id": "CrewAIAgentComponent-Trkfd", + "id": "CrewAIAgentComponent-9D8ao", "node": { "base_classes": [ "Agent" @@ -1665,8 +1672,8 @@ "type": "CrewAIAgentComponent" }, "dragging": false, - "height": 665, - "id": "CrewAIAgentComponent-Trkfd", + "height": 746, + "id": "CrewAIAgentComponent-9D8ao", "position": { "x": -451.69853654304495, "y": 1664.003205846104 @@ -1677,11 +1684,13 @@ }, "selected": false, "type": "genericNode", - "width": 384 + "width": 432 }, { "data": { - "id": "OpenAIModel-0cqIu", + "description": "Generates text using OpenAI LLMs.", + "display_name": "OpenAI", + "id": "OpenAIModel-HgNnu", "node": { "base_classes": [ "LanguageModel", @@ -1696,16 +1705,16 @@ "edited": false, "field_order": [ "input_value", + "system_message", + "stream", "max_tokens", "model_kwargs", "json_mode", "output_schema", "model_name", "openai_api_base", - "openai_api_key", + "api_key", "temperature", - "stream", - "system_message", "seed" ], "frozen": false, @@ -1738,6 +1747,22 @@ "pinned": false, "template": { "_type": "Component", + "api_key": { + "advanced": false, + "display_name": "OpenAI API Key", + "dynamic": false, + "info": "The OpenAI API Key to use for the OpenAI model.", + "input_types": [], + "load_from_db": false, + "name": "api_key", + "password": true, + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "type": "str", + "value": "" + }, "code": { "advanced": true, "dynamic": true, @@ -1754,7 +1779,7 @@ "show": true, "title_case": false, "type": "code", - "value": "import operator\nfrom functools import reduce\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.constants import STREAM_INFO_TEXT\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n MessageInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n name = \"OpenAIModel\"\n\n inputs = [\n MessageInput(name=\"input_value\", display_name=\"Input\"),\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n info=\"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.\",\n ),\n SecretStrInput(\n name=\"openai_api_key\",\n display_name=\"OpenAI API Key\",\n info=\"The OpenAI API Key to use for the OpenAI model.\",\n advanced=False,\n value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n BoolInput(name=\"stream\", display_name=\"Stream\", info=STREAM_INFO_TEXT, advanced=True),\n StrInput(\n name=\"system_message\",\n display_name=\"System Message\",\n info=\"System message to pass to the model.\",\n advanced=True,\n ),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n # self.output_schea is a list of dictionarie s\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.openai_api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n model_kwargs[\"seed\"] = seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output # type: ignore\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n" + "value": "import operator\nfrom functools import reduce\n\nfrom langchain_openai import ChatOpenAI\nfrom pydantic.v1 import SecretStr\n\nfrom langflow.base.models.model import LCModelComponent\nfrom langflow.base.models.openai_constants import MODEL_NAMES\nfrom langflow.field_typing import LanguageModel\nfrom langflow.inputs import (\n BoolInput,\n DictInput,\n DropdownInput,\n FloatInput,\n IntInput,\n SecretStrInput,\n StrInput,\n)\n\n\nclass OpenAIModelComponent(LCModelComponent):\n display_name = \"OpenAI\"\n description = \"Generates text using OpenAI LLMs.\"\n icon = \"OpenAI\"\n name = \"OpenAIModel\"\n\n inputs = LCModelComponent._base_inputs + [\n IntInput(\n name=\"max_tokens\",\n display_name=\"Max Tokens\",\n advanced=True,\n info=\"The maximum number of tokens to generate. Set to 0 for unlimited tokens.\",\n ),\n DictInput(name=\"model_kwargs\", display_name=\"Model Kwargs\", advanced=True),\n BoolInput(\n name=\"json_mode\",\n display_name=\"JSON Mode\",\n advanced=True,\n info=\"If True, it will output JSON regardless of passing a schema.\",\n ),\n DictInput(\n name=\"output_schema\",\n is_list=True,\n display_name=\"Schema\",\n advanced=True,\n info=\"The schema for the Output of the model. You must pass the word JSON in the prompt. If left blank, JSON mode will be disabled.\",\n ),\n DropdownInput(\n name=\"model_name\", display_name=\"Model Name\", advanced=False, options=MODEL_NAMES, value=MODEL_NAMES[0]\n ),\n StrInput(\n name=\"openai_api_base\",\n display_name=\"OpenAI API Base\",\n advanced=True,\n info=\"The base URL of the OpenAI API. Defaults to https://api.openai.com/v1. You can change this to use other APIs like JinaChat, LocalAI and Prem.\",\n ),\n SecretStrInput(\n name=\"api_key\",\n display_name=\"OpenAI API Key\",\n info=\"The OpenAI API Key to use for the OpenAI model.\",\n advanced=False,\n value=\"OPENAI_API_KEY\",\n ),\n FloatInput(name=\"temperature\", display_name=\"Temperature\", value=0.1),\n IntInput(\n name=\"seed\",\n display_name=\"Seed\",\n info=\"The seed controls the reproducibility of the job.\",\n advanced=True,\n value=1,\n ),\n ]\n\n def build_model(self) -> LanguageModel: # type: ignore[type-var]\n # self.output_schema is a list of dictionaries\n # let's convert it to a dictionary\n output_schema_dict: dict[str, str] = reduce(operator.ior, self.output_schema or {}, {})\n openai_api_key = self.api_key\n temperature = self.temperature\n model_name: str = self.model_name\n max_tokens = self.max_tokens\n model_kwargs = self.model_kwargs or {}\n openai_api_base = self.openai_api_base or \"https://api.openai.com/v1\"\n json_mode = bool(output_schema_dict) or self.json_mode\n seed = self.seed\n\n if openai_api_key:\n api_key = SecretStr(openai_api_key)\n else:\n api_key = None\n output = ChatOpenAI(\n max_tokens=max_tokens or None,\n model_kwargs=model_kwargs,\n model=model_name,\n base_url=openai_api_base,\n api_key=api_key,\n temperature=temperature or 0.1,\n seed=seed,\n )\n if json_mode:\n if output_schema_dict:\n output = output.with_structured_output(schema=output_schema_dict, method=\"json_mode\") # type: ignore\n else:\n output = output.bind(response_format={\"type\": \"json_object\"}) # type: ignore\n\n return output # type: ignore\n\n def _get_exception_message(self, e: Exception):\n \"\"\"\n Get a message from an OpenAI exception.\n\n Args:\n exception (Exception): The exception to get the message from.\n\n Returns:\n str: The message from the exception.\n \"\"\"\n\n try:\n from openai import BadRequestError\n except ImportError:\n return\n if isinstance(e, BadRequestError):\n message = e.body.get(\"message\") # type: ignore\n if message:\n return message\n return\n" }, "input_value": { "advanced": false, @@ -1826,8 +1851,10 @@ "display_name": "Model Name", "dynamic": false, "info": "", + "load_from_db": false, "name": "model_name", "options": [ + "gpt-4o-mini", "gpt-4o", "gpt-4-turbo", "gpt-4-turbo-preview", @@ -1859,22 +1886,6 @@ "type": "str", "value": "" }, - "openai_api_key": { - "advanced": false, - "display_name": "OpenAI API Key", - "dynamic": false, - "info": "The OpenAI API Key to use for the OpenAI model.", - "input_types": [], - "load_from_db": true, - "name": "openai_api_key", - "password": true, - "placeholder": "", - "required": false, - "show": true, - "title_case": false, - "type": "str", - "value": "" - }, "output_schema": { "advanced": true, "display_name": "Schema", @@ -1925,6 +1936,9 @@ "display_name": "System Message", "dynamic": false, "info": "System message to pass to the model.", + "input_types": [ + "Message" + ], "list": false, "load_from_db": false, "name": "system_message", @@ -1932,6 +1946,7 @@ "required": false, "show": true, "title_case": false, + "trace_as_input": true, "trace_as_metadata": true, "type": "str", "value": "" @@ -1956,8 +1971,8 @@ "type": "OpenAIModel" }, "dragging": false, - "height": 623, - "id": "OpenAIModel-0cqIu", + "height": 700, + "id": "OpenAIModel-HgNnu", "position": { "x": -1157.0105401589535, "y": 1699.4665001507685 @@ -1968,13 +1983,13 @@ }, "selected": false, "type": "genericNode", - "width": 384 + "width": 432 }, { "data": { "description": "Create a prompt template with dynamic variables.", "display_name": "Prompt", - "id": "Prompt-5o2wP", + "id": "Prompt-eqGhn", "node": { "base_classes": [ "Message" @@ -2081,8 +2096,8 @@ "type": "Prompt" }, "dragging": false, - "height": 423, - "id": "Prompt-5o2wP", + "height": 475, + "id": "Prompt-eqGhn", "position": { "x": -561.9017558579421, "y": -433.5913161630874 @@ -2093,11 +2108,11 @@ }, "selected": false, "type": "genericNode", - "width": 384 + "width": 432 }, { "data": { - "id": "ChatInput-kHOLm", + "id": "ChatInput-xgRl9", "node": { "base_classes": [ "Message" @@ -2294,154 +2309,25 @@ "type": "ChatInput" }, "dragging": false, - "height": 309, - "id": "ChatInput-kHOLm", + "height": 347, + "id": "ChatInput-xgRl9", "position": { - "x": -1142.1418710409707, - "y": -372.98153583376074 + "x": -1227.1161422332996, + "y": -380.26504479310324 }, "positionAbsolute": { - "x": -1142.1418710409707, - "y": -372.98153583376074 + "x": -1227.1161422332996, + "y": -380.26504479310324 }, "selected": false, "type": "genericNode", - "width": 384 - }, - { - "data": { - "id": "SearchAPITool-fmETj", - "node": { - "base_classes": [ - "BaseTool", - "Generic", - "object", - "Runnable", - "RunnableSerializable", - "Serializable", - "Tool" - ], - "beta": false, - "conditional_paths": [], - "custom_fields": { - "api_key": null, - "engine": null - }, - "description": "Real-time search engine results API.", - "display_name": "SearchApi Tool", - "documentation": "https://www.searchapi.io/docs/google", - "edited": false, - "field_order": [ - "engine", - "api_key" - ], - "frozen": false, - "output_types": [ - "Tool" - ], - "outputs": [ - { - "cache": true, - "display_name": "Tool", - "hidden": null, - "method": null, - "name": "tool", - "selected": "Tool", - "types": [ - "Tool" - ], - "value": "__UNDEFINED__" - } - ], - "pinned": false, - "template": { - "_type": "CustomComponent", - "api_key": { - "advanced": false, - "display_name": "API Key", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "The API key to use SearchApi.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "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": "from langchain_community.tools.searchapi import SearchAPIRun\nfrom langchain_community.utilities.searchapi import SearchApiAPIWrapper\n\nfrom langflow.custom import CustomComponent\nfrom langflow.field_typing import Tool\n\n\nclass SearchApiToolComponent(CustomComponent):\n display_name: str = \"SearchApi Tool\"\n description: str = \"Real-time search engine results API.\"\n name = \"SearchAPITool\"\n documentation: str = \"https://www.searchapi.io/docs/google\"\n field_config = {\n \"engine\": {\n \"display_name\": \"Engine\",\n \"field_type\": \"str\",\n \"info\": \"The search engine to use.\",\n },\n \"api_key\": {\n \"display_name\": \"API Key\",\n \"field_type\": \"str\",\n \"required\": True,\n \"password\": True,\n \"info\": \"The API key to use SearchApi.\",\n },\n }\n\n def build(\n self,\n engine: str,\n api_key: str,\n ) -> Tool:\n search_api_wrapper = SearchApiAPIWrapper(engine=engine, searchapi_api_key=api_key)\n\n tool = SearchAPIRun(api_wrapper=search_api_wrapper)\n\n self.status = tool\n return tool # type: ignore\n" - }, - "engine": { - "advanced": false, - "display_name": "Engine", - "dynamic": false, - "fileTypes": [], - "file_path": "", - "info": "The search engine to use.", - "input_types": [ - "Text" - ], - "list": false, - "load_from_db": false, - "multiline": false, - "name": "engine", - "password": false, - "placeholder": "", - "required": true, - "show": true, - "title_case": false, - "type": "str", - "value": "google" - } - } - }, - "type": "SearchAPITool" - }, - "dragging": false, - "height": 403, - "id": "SearchAPITool-fmETj", - "position": { - "x": -1200.258747181863, - "y": 206.70950310719434 - }, - "positionAbsolute": { - "x": -1200.258747181863, - "y": 206.70950310719434 - }, - "selected": false, - "type": "genericNode", - "width": 384 + "width": 432 }, { "data": { "description": "Represents an agent of CrewAI.", "display_name": "CrewAI Agent", - "id": "CrewAIAgentComponent-V2kfR", + "id": "CrewAIAgentComponent-UMpxO", "node": { "base_classes": [ "Agent" @@ -2681,8 +2567,8 @@ "type": "CrewAIAgentComponent" }, "dragging": false, - "height": 665, - "id": "CrewAIAgentComponent-V2kfR", + "height": 746, + "id": "CrewAIAgentComponent-UMpxO", "position": { "x": -397.2627184897183, "y": 892.6280687419107 @@ -2693,19 +2579,178 @@ }, "selected": false, "type": "genericNode", - "width": 384 + "width": 432 + }, + { + "data": { + "id": "SearchAPI-Yokat", + "node": { + "base_classes": [ + "Data", + "Tool" + ], + "beta": false, + "conditional_paths": [], + "custom_fields": {}, + "description": "Call the searchapi.io API", + "display_name": "Search API", + "documentation": "https://www.searchapi.io/docs/google", + "edited": false, + "field_order": [ + "engine", + "api_key", + "input_value", + "search_params" + ], + "frozen": false, + "output_types": [], + "outputs": [ + { + "cache": true, + "display_name": "Data", + "method": "run_model", + "name": "api_run_model", + "selected": "Data", + "types": [ + "Data" + ], + "value": "__UNDEFINED__" + }, + { + "cache": true, + "display_name": "Tool", + "method": "build_tool", + "name": "api_build_tool", + "selected": "Tool", + "types": [ + "Tool" + ], + "value": "__UNDEFINED__" + } + ], + "pinned": false, + "template": { + "_type": "Component", + "api_key": { + "advanced": false, + "display_name": "SearchAPI API Key", + "dynamic": false, + "info": "", + "input_types": [], + "load_from_db": false, + "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": "from typing import Union\n\nfrom langchain_community.utilities.searchapi import SearchApiAPIWrapper\n\nfrom langflow.base.langchain_utilities.model import LCToolComponent\nfrom langflow.inputs import SecretStrInput, MultilineInput, DictInput, MessageTextInput\nfrom langflow.schema import Data\nfrom langflow.field_typing import Tool\n\n\nclass SearchAPIComponent(LCToolComponent):\n display_name: str = \"Search API\"\n description: str = \"Call the searchapi.io API\"\n name = \"SearchAPI\"\n documentation: str = \"https://www.searchapi.io/docs/google\"\n\n inputs = [\n MessageTextInput(name=\"engine\", display_name=\"Engine\", value=\"google\"),\n SecretStrInput(name=\"api_key\", display_name=\"SearchAPI API Key\", required=True),\n MultilineInput(\n name=\"input_value\",\n display_name=\"Input\",\n ),\n DictInput(name=\"search_params\", display_name=\"Search parameters\", advanced=True, is_list=True),\n ]\n\n def run_model(self) -> Union[Data, list[Data]]:\n wrapper = self._build_wrapper()\n results = wrapper.results(query=self.input_value, **(self.search_params or {}))\n list_results = results.get(\"organic_results\", [])\n data = [Data(data=result, text=result[\"snippet\"]) for result in list_results]\n self.status = data\n return data\n\n def build_tool(self) -> Tool:\n wrapper = self._build_wrapper()\n return Tool(\n name=\"search_api\",\n description=\"Search for recent results.\",\n func=lambda x: wrapper.run(query=x, **(self.search_params or {})),\n )\n\n def _build_wrapper(self):\n return SearchApiAPIWrapper(engine=self.engine, searchapi_api_key=self.api_key)\n" + }, + "engine": { + "advanced": false, + "display_name": "Engine", + "dynamic": false, + "info": "", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "name": "engine", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "google" + }, + "input_value": { + "advanced": false, + "display_name": "Input", + "dynamic": false, + "info": "", + "input_types": [ + "Message" + ], + "list": false, + "load_from_db": false, + "multiline": true, + "name": "input_value", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "trace_as_metadata": true, + "type": "str", + "value": "" + }, + "search_params": { + "advanced": true, + "display_name": "Search parameters", + "dynamic": false, + "info": "", + "list": true, + "name": "search_params", + "placeholder": "", + "required": false, + "show": true, + "title_case": false, + "trace_as_input": true, + "type": "dict", + "value": {} + } + } + }, + "type": "SearchAPI" + }, + "dragging": false, + "height": 612, + "id": "SearchAPI-Yokat", + "position": { + "x": -1229.2392382339021, + "y": 59.93107827969652 + }, + "positionAbsolute": { + "x": -1229.2392382339021, + "y": 59.93107827969652 + }, + "selected": false, + "type": "genericNode", + "width": 432 } ], "viewport": { - "x": 689.9423538038659, - "y": 249.5990980882735, - "zoom": 0.28087497575792186 + "x": 538.2178429916559, + "y": 231.5938714083456, + "zoom": 0.25005698994252296 } }, "description": "This Agentic Flow has a Manager and a couple Workers. The manager can answer the user's question straightaway or delegate the task to other agents.", "endpoint_name": null, "id": "6302081f-5af2-4b9f-bd19-2baaf7218ba6", "is_component": false, - "last_tested_version": "1.0.9", + "last_tested_version": "1.0.12", "name": "Hierarchical Tasks Agent" } \ No newline at end of file diff --git a/src/frontend/playwright.config.ts b/src/frontend/playwright.config.ts index 54153792d..6453ee504 100644 --- a/src/frontend/playwright.config.ts +++ b/src/frontend/playwright.config.ts @@ -23,7 +23,7 @@ export default defineConfig({ /* Opt out of parallel tests on CI. */ workers: 2, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - timeout: 120 * 1000, + timeout: 3 * 60 * 1000, // reporter: [ // ["html", { open: "never", outputFolder: "playwright-report/test-results" }], // ], diff --git a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx index 0638e3c38..458d19e6b 100644 --- a/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx +++ b/src/frontend/src/modals/IOModal/components/chatView/chatMessage/index.tsx @@ -173,7 +173,10 @@ export default function ChatMessage({ )} {chat.thought && chat.thought !== "" && !hidden &&

}
-
+
{ + test.skip( + !process?.env?.OPENAI_API_KEY, + "OPENAI_API_KEY required to run this test", + ); + + test.skip( + !process?.env?.BRAVE_SEARCH_API_KEY, + "BRAVE_SEARCH_API_KEY required to run this test", + ); + + if (!process.env.CI) { + dotenv.config({ path: path.resolve(__dirname, "../../.env") }); + } + + await page.goto("/"); + await page.waitForTimeout(2000); + + let modalCount = 0; + try { + const modalTitleElement = await page?.getByTestId("modal-title"); + if (modalTitleElement) { + modalCount = await modalTitleElement.count(); + } + } catch (error) { + modalCount = 0; + } + + while (modalCount === 0) { + await page.getByText("New Project", { exact: true }).click(); + await page.waitForTimeout(5000); + modalCount = await page.getByTestId("modal-title")?.count(); + } + + await page.getByRole("heading", { name: "Complex Agent" }).click(); + + await page.waitForSelector('[title="fit view"]', { + timeout: 100000, + }); + + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + let outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); + + while (outdatedComponents > 0) { + await page.getByTestId("icon-AlertTriangle").first().click(); + await page.waitForTimeout(1000); + outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); + } + + await page + .getByTestId("popover-anchor-input-api_key") + .last() + .fill(process.env.BRAVE_SEARCH_API_KEY ?? ""); + + await page.waitForTimeout(2000); + + let openAiLlms = await page.getByText("OpenAI", { exact: true }).count(); + + for (let i = 0; i < openAiLlms; i++) { + await page + .getByTestId("popover-anchor-input-api_key") + .nth(i) + .fill(process.env.OPENAI_API_KEY ?? ""); + + await page.getByTestId("dropdown-model_name").nth(i).click(); + await page.getByTestId("gpt-4o-1-option").last().click(); + + await page.waitForTimeout(1000); + } + + await page.getByTestId("button_run_chat output").click(); + await page.waitForSelector("text=built successfully", { timeout: 60000 * 3 }); + + await page.getByText("built successfully").last().click({ + timeout: 15000, + }); + + await page.getByText("Playground", { exact: true }).click(); + + await page.waitForTimeout(2000); + + expect( + page.getByText("Could you search info about AAPL?", { exact: true }).last(), + ).toBeVisible(); + + const textContents = await page + .getByTestId("div-chat-message") + .allTextContents(); + + const concatAllText = textContents.join(" "); + expect(concatAllText.toLocaleLowerCase()).toContain("apple"); + const allTextLength = concatAllText.length; + expect(allTextLength).toBeGreaterThan(500); +}); diff --git a/src/frontend/tests/end-to-end/Hierarchical Tasks Agent.spec.ts b/src/frontend/tests/end-to-end/Hierarchical Tasks Agent.spec.ts new file mode 100644 index 000000000..f73f7917c --- /dev/null +++ b/src/frontend/tests/end-to-end/Hierarchical Tasks Agent.spec.ts @@ -0,0 +1,106 @@ +import { expect, test } from "@playwright/test"; +import * as dotenv from "dotenv"; +import path from "path"; + +test("Hierarchical Tasks Agent", async ({ page }) => { + test.skip( + !process?.env?.OPENAI_API_KEY, + "OPENAI_API_KEY required to run this test", + ); + + test.skip( + !process?.env?.BRAVE_SEARCH_API_KEY, + "BRAVE_SEARCH_API_KEY required to run this test", + ); + + if (!process.env.CI) { + dotenv.config({ path: path.resolve(__dirname, "../../.env") }); + } + + await page.goto("/"); + await page.waitForTimeout(2000); + + let modalCount = 0; + try { + const modalTitleElement = await page?.getByTestId("modal-title"); + if (modalTitleElement) { + modalCount = await modalTitleElement.count(); + } + } catch (error) { + modalCount = 0; + } + + while (modalCount === 0) { + await page.getByText("New Project", { exact: true }).click(); + await page.waitForTimeout(5000); + modalCount = await page.getByTestId("modal-title")?.count(); + } + + await page.getByRole("heading", { name: "Hierarchical Tasks Agent" }).click(); + + await page.waitForSelector('[title="fit view"]', { + timeout: 100000, + }); + + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + let outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); + + while (outdatedComponents > 0) { + await page.getByTestId("icon-AlertTriangle").first().click(); + await page.waitForTimeout(1000); + outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); + } + + await page + .getByTestId("popover-anchor-input-api_key") + .first() + .fill(process.env.OPENAI_API_KEY ?? ""); + + await page + .getByTestId("popover-anchor-input-api_key") + .nth(1) + .fill(process.env.OPENAI_API_KEY ?? ""); + + await page.getByTestId("dropdown-model_name").first().click(); + await page.getByTestId("gpt-4o-1-option").first().click(); + + await page.waitForTimeout(2000); + + await page.getByTestId("dropdown-model_name").last().click(); + await page.getByTestId("gpt-4o-1-option").last().click(); + + await page.waitForTimeout(2000); + + await page + .getByTestId("popover-anchor-input-api_key") + .last() + .fill(process.env.BRAVE_SEARCH_API_KEY ?? ""); + + await page.waitForTimeout(2000); + + await page.getByTestId("button_run_chat output").click(); + await page.waitForSelector("text=built successfully", { timeout: 60000 * 3 }); + + await page.getByText("built successfully").last().click({ + timeout: 15000, + }); + + await page.getByText("Playground", { exact: true }).click(); + + await page.waitForTimeout(2000); + + expect(page.getByText("What is Langflow?", { exact: true })).toBeVisible(); + + const textContents = await page + .getByTestId("div-chat-message") + .allTextContents(); + + const concatAllText = textContents.join(" "); + expect(concatAllText.toLocaleLowerCase()).toContain("langflow"); + const allTextLength = concatAllText.length; + expect(allTextLength).toBeGreaterThan(500); +}); diff --git a/src/frontend/tests/end-to-end/Sequential Tasks Agent.spec.ts b/src/frontend/tests/end-to-end/Sequential Tasks Agent.spec.ts new file mode 100644 index 000000000..d7ad815ac --- /dev/null +++ b/src/frontend/tests/end-to-end/Sequential Tasks Agent.spec.ts @@ -0,0 +1,106 @@ +import { expect, test } from "@playwright/test"; +import * as dotenv from "dotenv"; +import path from "path"; + +test("Sequential Tasks Agent", async ({ page }) => { + test.skip( + !process?.env?.OPENAI_API_KEY, + "OPENAI_API_KEY required to run this test", + ); + + test.skip( + !process?.env?.BRAVE_SEARCH_API_KEY, + "BRAVE_SEARCH_API_KEY required to run this test", + ); + + if (!process.env.CI) { + dotenv.config({ path: path.resolve(__dirname, "../../.env") }); + } + + await page.goto("/"); + await page.waitForTimeout(2000); + + let modalCount = 0; + try { + const modalTitleElement = await page?.getByTestId("modal-title"); + if (modalTitleElement) { + modalCount = await modalTitleElement.count(); + } + } catch (error) { + modalCount = 0; + } + + while (modalCount === 0) { + await page.getByText("New Project", { exact: true }).click(); + await page.waitForTimeout(5000); + modalCount = await page.getByTestId("modal-title")?.count(); + } + + await page.getByRole("heading", { name: "Sequential Tasks Agent" }).click(); + + await page.waitForSelector('[title="fit view"]', { + timeout: 100000, + }); + + await page.getByTitle("fit view").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + await page.getByTitle("zoom out").click(); + + let outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); + + while (outdatedComponents > 0) { + await page.getByTestId("icon-AlertTriangle").first().click(); + await page.waitForTimeout(1000); + outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); + } + + await page + .getByTestId("popover-anchor-input-api_key") + .first() + .fill(process.env.OPENAI_API_KEY ?? ""); + + await page.getByTestId("dropdown-model_name").click(); + await page.getByTestId("gpt-4o-1-option").click(); + + await page.waitForTimeout(2000); + + await page + .getByTestId("popover-anchor-input-api_key") + .last() + .fill(process.env.BRAVE_SEARCH_API_KEY ?? ""); + + await page.waitForTimeout(2000); + + await page.getByTestId("button_run_chat output").click(); + await page.waitForSelector("text=built successfully", { timeout: 60000 * 3 }); + + await page.getByText("built successfully").last().click({ + timeout: 15000, + }); + + await page.getByText("Playground", { exact: true }).click(); + + await page.waitForTimeout(2000); + + expect( + page + .getByPlaceholder("No chat input variables found. Click to run your flow") + .last(), + ).toBeVisible(); + + await page.getByText("Topic", { exact: true }).nth(1).isVisible(); + await page.getByText("Topic", { exact: true }).nth(1).click(); + expect(await page.getByPlaceholder("Enter text...").inputValue()).toBe( + "Agile", + ); + + const textContents = await page + .getByTestId("div-chat-message") + .allTextContents(); + + const concatAllText = textContents.join(" "); + expect(concatAllText.toLocaleLowerCase()).toContain("agile"); + const allTextLength = concatAllText.length; + expect(allTextLength).toBeGreaterThan(500); +}); diff --git a/src/frontend/tests/end-to-end/Vector Store.spec.ts b/src/frontend/tests/end-to-end/Vector Store.spec.ts index 3f825128a..8f44d3261 100644 --- a/src/frontend/tests/end-to-end/Vector Store.spec.ts +++ b/src/frontend/tests/end-to-end/Vector Store.spec.ts @@ -2,6 +2,21 @@ import { expect, test } from "@playwright/test"; import path from "path"; test("Vector Store RAG", async ({ page }) => { + test.skip( + !process?.env?.OPENAI_API_KEY, + "OPENAI_API_KEY required to run this test", + ); + + test.skip( + !process?.env?.ASTRA_DB_API_ENDPOINT, + "ASTRA_DB_API_ENDPOINT required to run this test", + ); + + test.skip( + !process?.env?.ASTRA_DB_APPLICATION_TOKEN, + "ASTRA_DB_APPLICATION_TOKEN required to run this test", + ); + await page.goto("/"); await page.waitForTimeout(2000); @@ -31,47 +46,46 @@ test("Vector Store RAG", async ({ page }) => { await page.getByTitle("zoom out").click(); await page.getByTitle("zoom out").click(); - // if ( - // !process.env.OPENAI_API_KEY || - // !process.env.ASTRA_DB_API_ENDPOINT || - // !process.env.ASTRA_DB_APPLICATION_TOKEN - // ) { - // //You must set the OPENAI_API_KEY, ASTRA_DB_API_ENDPOINT and ASTRA_DB_APPLICATION_TOKEN on .env file to run this test - // expect(false).toBe(true); - // } + let outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); - // await page - // .getByTestId("popover-anchor-input-openai_api_key") - // .nth(0) - // .fill(process.env.OPENAI_API_KEY ?? ""); + while (outdatedComponents > 0) { + await page.getByTestId("icon-AlertTriangle").first().click(); + await page.waitForTimeout(1000); + outdatedComponents = await page.getByTestId("icon-AlertTriangle").count(); + } - // await page - // .getByTestId("popover-anchor-input-openai_api_key") - // .nth(1) - // .fill(process.env.OPENAI_API_KEY ?? ""); + await page + .getByTestId("popover-anchor-input-api_key") + .nth(0) + .fill(process.env.OPENAI_API_KEY ?? ""); - // await page - // .getByTestId("popover-anchor-input-openai_api_key") - // .nth(2) - // .fill(process.env.OPENAI_API_KEY ?? ""); + await page + .getByTestId("popover-anchor-input-openai_api_key") + .nth(0) + .fill(process.env.OPENAI_API_KEY ?? ""); - // await page - // .getByTestId("popover-anchor-input-token") - // .nth(0) - // .fill(process.env.ASTRA_DB_APPLICATION_TOKEN ?? ""); - // await page - // .getByTestId("popover-anchor-input-token") - // .nth(1) - // .fill(process.env.ASTRA_DB_APPLICATION_TOKEN ?? ""); + await page + .getByTestId("popover-anchor-input-openai_api_key") + .nth(1) + .fill(process.env.OPENAI_API_KEY ?? ""); - // await page - // .getByTestId("popover-anchor-input-api_endpoint") - // .nth(0) - // .fill(process.env.ASTRA_DB_API_ENDPOINT ?? ""); - // await page - // .getByTestId("popover-anchor-input-api_endpoint") - // .nth(1) - // .fill(process.env.ASTRA_DB_API_ENDPOINT ?? ""); + await page + .getByTestId("popover-anchor-input-token") + .nth(0) + .fill(process.env.ASTRA_DB_APPLICATION_TOKEN ?? ""); + await page + .getByTestId("popover-anchor-input-token") + .nth(1) + .fill(process.env.ASTRA_DB_APPLICATION_TOKEN ?? ""); + + await page + .getByTestId("popover-anchor-input-api_endpoint") + .nth(0) + .fill(process.env.ASTRA_DB_API_ENDPOINT ?? ""); + await page + .getByTestId("popover-anchor-input-api_endpoint") + .nth(1) + .fill(process.env.ASTRA_DB_API_ENDPOINT ?? ""); const fileChooserPromise = page.waitForEvent("filechooser"); await page.getByTestId("icon-FileSearch2").last().click(); @@ -79,51 +93,60 @@ test("Vector Store RAG", async ({ page }) => { await fileChooser.setFiles(path.join(__dirname, "/assets/test_file.txt")); await page.getByText("test_file.txt").isVisible(); - // await page.getByTestId("button_run_astra db").first().click(); - // await page.waitForSelector("text=built successfully", { timeout: 30000 }); + await page.waitForTimeout(2000); - // await page.getByText("built successfully").last().click({ - // timeout: 30000, - // }); + await page.getByTestId("button_run_astra db").first().click(); + await page.waitForSelector("text=built successfully", { timeout: 30000 }); - // await page.getByTestId("button_run_chat output").click(); - // await page.waitForSelector("text=built successfully", { timeout: 30000 }); + await page.getByText("built successfully").last().click({ + timeout: 30000, + }); - // await page.getByText("built successfully").last().click({ - // timeout: 30000, - // }); + await page.getByTestId("button_run_chat output").click(); + await page.waitForSelector("text=built successfully", { timeout: 30000 }); - // await page.getByText("Playground", { exact: true }).click(); + await page.getByText("built successfully").last().click({ + timeout: 30000, + }); - // await page.waitForSelector('[data-testid="input-chat-playground"]', { - // timeout: 100000, - // }); + await page.getByTestId("button_run_astra db").last().click(); + await page.waitForSelector("text=built successfully", { timeout: 30000 }); - // await page.getByTestId("input-chat-playground").last().fill("hello"); + await page.getByText("built successfully").last().click({ + timeout: 30000, + }); - // await page.getByTestId("icon-LucideSend").last().click(); + await page.getByText("Playground", { exact: true }).click(); - // await page - // .getByText("This is a test file.", { exact: true }) - // .last() - // .isVisible(); + await page.waitForSelector('[data-testid="input-chat-playground"]', { + timeout: 100000, + }); - // await page.getByText("Memories", { exact: true }).last().click(); - // await page.getByText("Default Session").last().click(); + await page.getByTestId("input-chat-playground").last().fill("hello"); - // await page.getByText("timestamp", { exact: true }).last().isVisible(); - // await page.getByText("text", { exact: true }).last().isVisible(); - // await page.getByText("sender", { exact: true }).last().isVisible(); - // await page.getByText("sender_name", { exact: true }).last().isVisible(); - // await page.getByText("session_id", { exact: true }).last().isVisible(); - // await page.getByText("files", { exact: true }).last().isVisible(); + await page.getByTestId("icon-LucideSend").last().click(); - // await page.getByRole("gridcell").last().isVisible(); - // await page.getByTestId("icon-Trash2").first().click(); + await page + .getByText("This is a test file.", { exact: true }) + .last() + .isVisible(); - // await page.waitForSelector('[data-testid="input-chat-playground"]', { - // timeout: 100000, - // }); + await page.getByText("Memories", { exact: true }).last().click(); + await page.getByText("Default Session").last().click(); - // await page.getByTestId("input-chat-playground").last().isVisible(); + await page.getByText("timestamp", { exact: true }).last().isVisible(); + await page.getByText("text", { exact: true }).last().isVisible(); + await page.getByText("sender", { exact: true }).last().isVisible(); + await page.getByText("sender_name", { exact: true }).last().isVisible(); + await page.getByText("session_id", { exact: true }).last().isVisible(); + await page.getByText("files", { exact: true }).last().isVisible(); + + await page.getByRole("gridcell").last().isVisible(); + await page.getByTestId("icon-Trash2").first().click(); + + await page.waitForSelector('[data-testid="input-chat-playground"]', { + timeout: 100000, + }); + + await page.getByTestId("input-chat-playground").last().isVisible(); });