From ade883e3f3e5c0a4c8365b89e7e6d3a3a6af493e Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Tue, 29 Oct 2024 00:42:01 -0400 Subject: [PATCH 01/21] simple ai agent new component: simple ai agent py tests for tool calling agent as well as for simple ai agent --- .../components/agents/simple_agent.py | 156 ++++++++++++++++++ .../components/agents/test_simple_agent.py | 31 ++++ .../agents/test_tool_calling_agent.py | 32 ++++ 3 files changed, 219 insertions(+) create mode 100644 src/backend/base/langflow/components/agents/simple_agent.py create mode 100644 src/backend/tests/unit/components/agents/test_simple_agent.py create mode 100644 src/backend/tests/unit/components/agents/test_tool_calling_agent.py diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py new file mode 100644 index 00000000000..56078bd1374 --- /dev/null +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -0,0 +1,156 @@ +from langflow.base.models.model import LCModelComponent +from langflow.components.agents.tool_calling import ToolCallingAgentComponent +from langflow.components.models.azure_openai import AzureChatOpenAIComponent +from langflow.components.models.openai import OpenAIModelComponent +from langflow.io import ( + DataInput, + DropdownInput, + HandleInput, + MessageTextInput, + Output, +) +from langflow.schema.message import Message + + +class SimpleAgentComponent(ToolCallingAgentComponent): + display_name: str = "Simple Agent" + description: str = "Agent that uses tools" + icon = "workflow" + beta = True + name = "SimpleAgent" + AZURE_OPENAI_API_VERSIONS = [ + "2024-06-01", + "2024-07-01-preview", + "2024-08-01-preview", + "2024-09-01-preview", + "2024-10-01-preview", + "2023-05-15", + "2023-12-01-preview", + "2024-02-15-preview", + "2024-03-01-preview", + ] + openai_inputs = [ + component_input + for component_input in OpenAIModelComponent().inputs + if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] + ] + azure_inputs = [ + component_input + for component_input in AzureChatOpenAIComponent().inputs + if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] + ] + inputs = [ + HandleInput( + name="tools", display_name="Tools", input_types=["Tool", "BaseTool", "StructuredTool"], is_list=True + ), + MessageTextInput(name="input_value", display_name="Input"), + DataInput(name="chat_history", display_name="Chat History", is_list=True, advanced=True), + DropdownInput( + name="agent_llm", + display_name="Language Model Type", + options=["Custom", "Azure OpenAI", "OpenAI"], + value="OpenAI", + real_time_refresh=True, + ), + *openai_inputs, + ] + outputs = [Output(name="response", display_name="Response", method="get_response")] + + async def get_response(self) -> Message: + # Chat input initialization + + # Default OpenAI Model Component + llm_model = self.get_llm() + if llm_model is None: + msg = "No language model selected" + raise ValueError(msg) + + agent = ToolCallingAgentComponent().set( + llm=llm_model, tools=[self.tools], chat_history=self.chat_history, input_value=self.input_value + ) + + return await agent.message_response() + + def get_llm(self): + if self.agent_llm == "OpenAI": + return ( + OpenAIModelComponent() + .set( + **{ + component_input.name: getattr(self, component_input.name) + for component_input in self.openai_inputs + } + ) + .build_model() + ) + if self.agent_llm == "Azure OpenAI": + return ( + AzureChatOpenAIComponent() + .set( + **{ + component_input.name: getattr(self, f"azure_param_{component_input.name}") + for component_input in self.azure_inputs + } + ) + .build_model() + ) + if self.agent_llm == "Custom": + return self.llm_custom + return None + + def insert_in_dict(self, build_config, field_name, new_parameters): + # Insert the new key-value pair after the found key + for new_field_name, new_parameter in new_parameters.items(): + # Get all the items as a list of tuples (key, value) + items = list(build_config.items()) + + # Find the index of the key to insert after + idx = len(items) + for i, (key, _value) in enumerate(items): + if key == field_name: + idx = i + 1 + break + + items.insert(idx, (new_field_name, new_parameter)) + + # Clear the original dictionary and update with the modified items + build_config.clear() + build_config.update(items) + + return build_config + + def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None): + if field_name == "agent_llm": + openai_fields = {component.name: component for component in self.openai_inputs} + azure_fields = {f"azure_param_{component.name}": component for component in self.azure_inputs} + if field_value == "OpenAI": + for field in [key for key in build_config if key.startswith("azure_param_")]: + if field in build_config: + del build_config[field] + for field in openai_fields: + if field in build_config: + del build_config[field] + if "llm_custom" in build_config: + del build_config["llm_custom"] + + self.insert_in_dict(build_config, "agent_llm", openai_fields) + elif field_value == "Azure OpenAI": + for field in openai_fields: + if field in build_config: + del build_config[field] + if "llm_custom" in build_config: + del build_config["llm_custom"] + self.insert_in_dict(build_config, "agent_llm", azure_fields) + elif field_value == "Custom": + for field in openai_fields: + if field in build_config: + del build_config[field] + for field in azure_fields: + if field in build_config: + del build_config[field] + llm_custom_fields = HandleInput( + name="llm_custom", display_name="Language Model", input_types=["LanguageModel"], required=False + ).to_dict() + self.insert_in_dict(build_config, "agent_llm", {"llm_custom": llm_custom_fields}) + build_config["agent_llm"]["value"] = field_value + return build_config diff --git a/src/backend/tests/unit/components/agents/test_simple_agent.py b/src/backend/tests/unit/components/agents/test_simple_agent.py new file mode 100644 index 00000000000..8e456970547 --- /dev/null +++ b/src/backend/tests/unit/components/agents/test_simple_agent.py @@ -0,0 +1,31 @@ +import os + +import pytest +from langflow.components.agents.simple_agent import SimpleAgentComponent +from langflow.components.tools.calculator import CalculatorToolComponent + + +@pytest.mark.api_key_required +@pytest.mark.asyncio +async def test_simple_agent_with_calculator(): + # Mock inputs + tools = [CalculatorToolComponent().build_tool()] # Use the Calculator component as a tool + input_value = "What is 2 + 2?" + + api_key = os.environ["OPENAI_API_KEY"] + temperature = 0.1 + + # Initialize the SimpleAgentComponent with mocked inputs + agent = SimpleAgentComponent( + tools=tools, + input_value=input_value, + api_key=api_key, + model_name="gpt-4o", + llm_type="OpenAI", + temperature=temperature + ) + + response = await agent.get_response() + assert "4" in response.data.get("text") + + diff --git a/src/backend/tests/unit/components/agents/test_tool_calling_agent.py b/src/backend/tests/unit/components/agents/test_tool_calling_agent.py new file mode 100644 index 00000000000..934085909f4 --- /dev/null +++ b/src/backend/tests/unit/components/agents/test_tool_calling_agent.py @@ -0,0 +1,32 @@ +import os + +from langflow.components.agents.tool_calling import ToolCallingAgentComponent +from langflow.components.inputs.chat import ChatInput +from langflow.components.models.openai import OpenAIModelComponent +from langflow.components.outputs import ChatOutput +from langflow.components.tools.calculator import CalculatorToolComponent +from langflow.graph.graph.base import Graph +from langflow.graph.state.model import create_state_model +from loguru import logger + + +async def test_tool_calling_agent_component(): + tools = [CalculatorToolComponent().build_tool()] # Use the Calculator component as a tool + input_value = "What is 2 + 2?" + chat_history = [] + api_key = os.environ["OPENAI_API_KEY"] + temperature = 0.1 + + # Default OpenAI Model Component + llm_component = OpenAIModelComponent().set( + api_key=api_key, + temperature=temperature, + ) + llm = llm_component.build_model() + + agent = ToolCallingAgentComponent() + agent.set(llm=llm, tools=[tools], chat_history=chat_history, input_value=input_value) + + # Chat output + response = await agent.message_response() + assert "4" in response.data.get("text") From 019f3b32da7bf83f0321b128822b64b68a504504 Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Tue, 29 Oct 2024 13:17:16 -0400 Subject: [PATCH 02/21] Update simple_agent.py update _config Changes and code oprimised --- .../components/agents/simple_agent.py | 122 +++++++----------- 1 file changed, 45 insertions(+), 77 deletions(-) diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py index 56078bd1374..be5612c0016 100644 --- a/src/backend/base/langflow/components/agents/simple_agent.py +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -9,6 +9,7 @@ MessageTextInput, Output, ) +from langflow.schema.dotdict import dotdict from langflow.schema.message import Message @@ -29,6 +30,7 @@ class SimpleAgentComponent(ToolCallingAgentComponent): "2024-02-15-preview", "2024-03-01-preview", ] + openai_inputs = [ component_input for component_input in OpenAIModelComponent().inputs @@ -48,18 +50,17 @@ class SimpleAgentComponent(ToolCallingAgentComponent): DropdownInput( name="agent_llm", display_name="Language Model Type", - options=["Custom", "Azure OpenAI", "OpenAI"], + options=["Azure OpenAI", "OpenAI"], value="OpenAI", real_time_refresh=True, + input_types=["LanguageModel"], + refresh_button=True ), *openai_inputs, ] outputs = [Output(name="response", display_name="Response", method="get_response")] async def get_response(self) -> Message: - # Chat input initialization - - # Default OpenAI Model Component llm_model = self.get_llm() if llm_model is None: msg = "No language model selected" @@ -72,85 +73,52 @@ async def get_response(self) -> Message: return await agent.message_response() def get_llm(self): - if self.agent_llm == "OpenAI": - return ( - OpenAIModelComponent() - .set( - **{ - component_input.name: getattr(self, component_input.name) - for component_input in self.openai_inputs - } - ) - .build_model() - ) - if self.agent_llm == "Azure OpenAI": - return ( - AzureChatOpenAIComponent() - .set( - **{ - component_input.name: getattr(self, f"azure_param_{component_input.name}") - for component_input in self.azure_inputs - } - ) - .build_model() - ) - if self.agent_llm == "Custom": - return self.llm_custom - return None - - def insert_in_dict(self, build_config, field_name, new_parameters): - # Insert the new key-value pair after the found key - for new_field_name, new_parameter in new_parameters.items(): - # Get all the items as a list of tuples (key, value) - items = list(build_config.items()) + try: + if self.agent_llm == "OpenAI": + return self._build_llm_model(OpenAIModelComponent(), self.openai_inputs) + if self.agent_llm == "Azure OpenAI": + return self._build_llm_model(AzureChatOpenAIComponent(), self.azure_inputs, prefix="azure_param_") + except Exception as e: + msg = f"Error building {self.agent_llm} language model" + raise ValueError(msg) from e + return self.agent_llm - # Find the index of the key to insert after - idx = len(items) - for i, (key, _value) in enumerate(items): - if key == field_name: - idx = i + 1 - break + def _build_llm_model(self, component, inputs, prefix=""): + return component.set( + **{component_input.name: getattr(self, f"{prefix}{component_input.name}") for component_input in inputs} + ).build_model() - items.insert(idx, (new_field_name, new_parameter)) + def delete_fields(self, build_config, fields): + for field in fields: + build_config.pop(field, None) - # Clear the original dictionary and update with the modified items - build_config.clear() - build_config.update(items) - - return build_config - - def update_build_config(self, build_config: dict, field_value: str, field_name: str | None = None): + def update_build_config(self, build_config: dotdict, field_value: str, field_name: str | None = None): if field_name == "agent_llm": - openai_fields = {component.name: component for component in self.openai_inputs} - azure_fields = {f"azure_param_{component.name}": component for component in self.azure_inputs} + openai_fields = {component_input.name: component_input for component_input in self.openai_inputs} + azure_fields = { + f"azure_param_{component_input.name}": component_input for component_input in self.azure_inputs + } + if field_value == "OpenAI": - for field in [key for key in build_config if key.startswith("azure_param_")]: - if field in build_config: - del build_config[field] - for field in openai_fields: - if field in build_config: - del build_config[field] - if "llm_custom" in build_config: - del build_config["llm_custom"] + self.delete_fields(build_config, {**azure_fields}) + if not any(field in build_config for field in openai_fields): + build_config.update(openai_fields) - self.insert_in_dict(build_config, "agent_llm", openai_fields) elif field_value == "Azure OpenAI": - for field in openai_fields: - if field in build_config: - del build_config[field] - if "llm_custom" in build_config: - del build_config["llm_custom"] - self.insert_in_dict(build_config, "agent_llm", azure_fields) + self.delete_fields(build_config, {**openai_fields}) + build_config.update(azure_fields) elif field_value == "Custom": - for field in openai_fields: - if field in build_config: - del build_config[field] - for field in azure_fields: - if field in build_config: - del build_config[field] - llm_custom_fields = HandleInput( - name="llm_custom", display_name="Language Model", input_types=["LanguageModel"], required=False - ).to_dict() - self.insert_in_dict(build_config, "agent_llm", {"llm_custom": llm_custom_fields}) - build_config["agent_llm"]["value"] = field_value + self.delete_fields(build_config, {**openai_fields}) + self.delete_fields(build_config, {**azure_fields}) + + default_keys = ["code", "_type", "agent_llm", "tools", "input_value"] + missing_keys = [key for key in default_keys if key not in build_config] + if missing_keys: + msg = f"Missing required keys in build_config: {missing_keys}" + raise ValueError(msg) + #debug code + for key, value in build_config.items(): + if isinstance(value, dict) and value.get("input_types") is None and key not in ["code", "_type"]: + msg = f"Component {key} has no input types specified" + raise ValueError(msg) return build_config From 19f50471e19006476f8e30bb66fe55a0dffe8931 Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Tue, 29 Oct 2024 13:45:34 -0400 Subject: [PATCH 03/21] Update simple_agent.py Error or inputType as None Solved --- .../components/agents/simple_agent.py | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py index be5612c0016..7a69753dcd9 100644 --- a/src/backend/base/langflow/components/agents/simple_agent.py +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -19,17 +19,6 @@ class SimpleAgentComponent(ToolCallingAgentComponent): icon = "workflow" beta = True name = "SimpleAgent" - AZURE_OPENAI_API_VERSIONS = [ - "2024-06-01", - "2024-07-01-preview", - "2024-08-01-preview", - "2024-09-01-preview", - "2024-10-01-preview", - "2023-05-15", - "2023-12-01-preview", - "2024-02-15-preview", - "2024-03-01-preview", - ] openai_inputs = [ component_input @@ -50,11 +39,11 @@ class SimpleAgentComponent(ToolCallingAgentComponent): DropdownInput( name="agent_llm", display_name="Language Model Type", - options=["Azure OpenAI", "OpenAI"], + options=["Azure OpenAI", "OpenAI", "Custom"], value="OpenAI", real_time_refresh=True, - input_types=["LanguageModel"], - refresh_button=True + refresh_button=True, + input_types=[], ), *openai_inputs, ] @@ -103,22 +92,41 @@ def update_build_config(self, build_config: dotdict, field_value: str, field_nam self.delete_fields(build_config, {**azure_fields}) if not any(field in build_config for field in openai_fields): build_config.update(openai_fields) + build_config["agent_llm"]["input_types"] = [] + build_config = self.update_input_types(build_config) elif field_value == "Azure OpenAI": self.delete_fields(build_config, {**openai_fields}) build_config.update(azure_fields) + build_config["agent_llm"]["input_types"] = [] + build_config = self.update_input_types(build_config) elif field_value == "Custom": self.delete_fields(build_config, {**openai_fields}) self.delete_fields(build_config, {**azure_fields}) + new_component = DropdownInput( + name="agent_llm", + display_name="Language Model", + options=["Azure OpenAI", "OpenAI", "Custom"], + value="Custom", + real_time_refresh=True, + input_types=["LanguageModel"], + ) + build_config.update({"agent_llm": new_component.to_dict()}) + build_config = self.update_input_types(build_config) + default_keys = ["code", "_type", "agent_llm", "tools", "input_value"] + missing_keys = [key for key in default_keys if key not in build_config] + if missing_keys: + msg = f"Missing required keys in build_config: {missing_keys}" + raise ValueError(msg) + return build_config - default_keys = ["code", "_type", "agent_llm", "tools", "input_value"] - missing_keys = [key for key in default_keys if key not in build_config] - if missing_keys: - msg = f"Missing required keys in build_config: {missing_keys}" - raise ValueError(msg) - #debug code + def update_input_types(self, build_config): for key, value in build_config.items(): - if isinstance(value, dict) and value.get("input_types") is None and key not in ["code", "_type"]: - msg = f"Component {key} has no input types specified" - raise ValueError(msg) + # Check if the value is a dictionary + if isinstance(value, dict): + if value.get("input_types") is None: + build_config[key]["input_types"] = [] + # Check if the value has an attribute 'input_types' and it is None + elif hasattr(value, "input_types") and value.input_types is None: + value.input_types = [] return build_config From f685b7710961a4997699710a7bff0fb4c4eaf494 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Tue, 29 Oct 2024 17:56:46 +0000 Subject: [PATCH 04/21] [autofix.ci] apply automated fixes --- .../tests/unit/components/agents/test_simple_agent.py | 4 +--- .../tests/unit/components/agents/test_tool_calling_agent.py | 5 ----- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/backend/tests/unit/components/agents/test_simple_agent.py b/src/backend/tests/unit/components/agents/test_simple_agent.py index 8e456970547..fca5dfbde55 100644 --- a/src/backend/tests/unit/components/agents/test_simple_agent.py +++ b/src/backend/tests/unit/components/agents/test_simple_agent.py @@ -22,10 +22,8 @@ async def test_simple_agent_with_calculator(): api_key=api_key, model_name="gpt-4o", llm_type="OpenAI", - temperature=temperature + temperature=temperature, ) response = await agent.get_response() assert "4" in response.data.get("text") - - diff --git a/src/backend/tests/unit/components/agents/test_tool_calling_agent.py b/src/backend/tests/unit/components/agents/test_tool_calling_agent.py index 934085909f4..b8e6f11893b 100644 --- a/src/backend/tests/unit/components/agents/test_tool_calling_agent.py +++ b/src/backend/tests/unit/components/agents/test_tool_calling_agent.py @@ -1,13 +1,8 @@ import os from langflow.components.agents.tool_calling import ToolCallingAgentComponent -from langflow.components.inputs.chat import ChatInput from langflow.components.models.openai import OpenAIModelComponent -from langflow.components.outputs import ChatOutput from langflow.components.tools.calculator import CalculatorToolComponent -from langflow.graph.graph.base import Graph -from langflow.graph.state.model import create_state_model -from loguru import logger async def test_tool_calling_agent_component(): From 32229b6365716800fc60d4694fd5c639fb72f2e9 Mon Sep 17 00:00:00 2001 From: italojohnny Date: Wed, 30 Oct 2024 09:53:08 -0300 Subject: [PATCH 05/21] fix: INP001 ruff error --- src/backend/tests/unit/components/agents/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/backend/tests/unit/components/agents/__init__.py diff --git a/src/backend/tests/unit/components/agents/__init__.py b/src/backend/tests/unit/components/agents/__init__.py new file mode 100644 index 00000000000..e69de29bb2d From 6e75f1123bf7f31cf70eac94f23423f8d1f8fa81 Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Wed, 30 Oct 2024 10:31:43 -0400 Subject: [PATCH 06/21] Update test_tool_calling_agent.py added pytest mark as api required --- .../tests/unit/components/agents/test_tool_calling_agent.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/backend/tests/unit/components/agents/test_tool_calling_agent.py b/src/backend/tests/unit/components/agents/test_tool_calling_agent.py index b8e6f11893b..312d29cf3bb 100644 --- a/src/backend/tests/unit/components/agents/test_tool_calling_agent.py +++ b/src/backend/tests/unit/components/agents/test_tool_calling_agent.py @@ -1,10 +1,13 @@ import os +import pytest from langflow.components.agents.tool_calling import ToolCallingAgentComponent from langflow.components.models.openai import OpenAIModelComponent from langflow.components.tools.calculator import CalculatorToolComponent +@pytest.mark.api_key_required +@pytest.mark.asyncio async def test_tool_calling_agent_component(): tools = [CalculatorToolComponent().build_tool()] # Use the Calculator component as a tool input_value = "What is 2 + 2?" From b475c2b6dddbe4408972c6418f530e6e9b75fb3b Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Wed, 30 Oct 2024 10:42:19 -0400 Subject: [PATCH 07/21] Update simple_agent.py added system prompt --- .../components/agents/simple_agent.py | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py index 7a69753dcd9..c449d879c52 100644 --- a/src/backend/base/langflow/components/agents/simple_agent.py +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -1,3 +1,4 @@ +from langflow.base.agents.agent import LCToolsAgentComponent from langflow.base.models.model import LCModelComponent from langflow.components.agents.tool_calling import ToolCallingAgentComponent from langflow.components.models.azure_openai import AzureChatOpenAIComponent @@ -31,11 +32,19 @@ class SimpleAgentComponent(ToolCallingAgentComponent): if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] ] inputs = [ - HandleInput( - name="tools", display_name="Tools", input_types=["Tool", "BaseTool", "StructuredTool"], is_list=True + *LCToolsAgentComponent._base_inputs, + MessageTextInput( + name="system_prompt", + display_name="System Prompt", + info="Initial instructions and context provided to guide the agent's behavior.", + value="You are a helpful assistant that can use tools to answer questions and perform tasks.", ), - MessageTextInput(name="input_value", display_name="Input"), - DataInput(name="chat_history", display_name="Chat History", is_list=True, advanced=True), + MessageTextInput( + name="input_value", + display_name="Input", + info="The input provided by the user for the agent to process.", + ), + DataInput(name="chat_history", display_name="Chat Memory", is_list=True, advanced=True), DropdownInput( name="agent_llm", display_name="Language Model Type", @@ -56,7 +65,8 @@ async def get_response(self) -> Message: raise ValueError(msg) agent = ToolCallingAgentComponent().set( - llm=llm_model, tools=[self.tools], chat_history=self.chat_history, input_value=self.input_value + llm=llm_model, tools=[self.tools], chat_history=self.chat_history, input_value=self.input_value, + system_prompt=self.system_prompt, ) return await agent.message_response() From 3c43404930d469e20936c88f83b46121b448c6fb Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 14:43:00 +0000 Subject: [PATCH 08/21] [autofix.ci] apply automated fixes --- .../base/langflow/components/agents/simple_agent.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py index c449d879c52..df3f1b58a10 100644 --- a/src/backend/base/langflow/components/agents/simple_agent.py +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -6,7 +6,6 @@ from langflow.io import ( DataInput, DropdownInput, - HandleInput, MessageTextInput, Output, ) @@ -44,7 +43,7 @@ class SimpleAgentComponent(ToolCallingAgentComponent): display_name="Input", info="The input provided by the user for the agent to process.", ), - DataInput(name="chat_history", display_name="Chat Memory", is_list=True, advanced=True), + DataInput(name="chat_history", display_name="Chat Memory", is_list=True, advanced=True), DropdownInput( name="agent_llm", display_name="Language Model Type", @@ -65,7 +64,10 @@ async def get_response(self) -> Message: raise ValueError(msg) agent = ToolCallingAgentComponent().set( - llm=llm_model, tools=[self.tools], chat_history=self.chat_history, input_value=self.input_value, + llm=llm_model, + tools=[self.tools], + chat_history=self.chat_history, + input_value=self.input_value, system_prompt=self.system_prompt, ) From 91d3c1cc17010152d6a9d2a1279a148db081fe7c Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Wed, 30 Oct 2024 18:13:36 -0400 Subject: [PATCH 09/21] updates in the input field orders --- .../base/langflow/base/agents/agent.py | 2 +- .../components/agents/simple_agent.py | 35 +++++++++---------- 2 files changed, 18 insertions(+), 19 deletions(-) diff --git a/src/backend/base/langflow/base/agents/agent.py b/src/backend/base/langflow/base/agents/agent.py index ca2e52d275f..959696e0cec 100644 --- a/src/backend/base/langflow/base/agents/agent.py +++ b/src/backend/base/langflow/base/agents/agent.py @@ -136,10 +136,10 @@ def create_agent_runnable(self) -> Runnable: class LCToolsAgentComponent(LCAgentComponent): _base_inputs = [ - *LCAgentComponent._base_inputs, HandleInput( name="tools", display_name="Tools", input_types=["Tool", "BaseTool", "StructuredTool"], is_list=True ), + *LCAgentComponent._base_inputs, ] def build_agent(self) -> AgentExecutor: diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py index df3f1b58a10..bf2d803f0a4 100644 --- a/src/backend/base/langflow/components/agents/simple_agent.py +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -14,11 +14,11 @@ class SimpleAgentComponent(ToolCallingAgentComponent): - display_name: str = "Simple Agent" - description: str = "Agent that uses tools" - icon = "workflow" + display_name: str = "Agent" + description: str = "Define the agent's instructions, then enter a task to complete using tools." + icon = "bot" beta = True - name = "SimpleAgent" + name = "Agent" openai_inputs = [ component_input @@ -31,22 +31,9 @@ class SimpleAgentComponent(ToolCallingAgentComponent): if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] ] inputs = [ - *LCToolsAgentComponent._base_inputs, - MessageTextInput( - name="system_prompt", - display_name="System Prompt", - info="Initial instructions and context provided to guide the agent's behavior.", - value="You are a helpful assistant that can use tools to answer questions and perform tasks.", - ), - MessageTextInput( - name="input_value", - display_name="Input", - info="The input provided by the user for the agent to process.", - ), - DataInput(name="chat_history", display_name="Chat Memory", is_list=True, advanced=True), DropdownInput( name="agent_llm", - display_name="Language Model Type", + display_name="Model Provider", options=["Azure OpenAI", "OpenAI", "Custom"], value="OpenAI", real_time_refresh=True, @@ -54,6 +41,18 @@ class SimpleAgentComponent(ToolCallingAgentComponent): input_types=[], ), *openai_inputs, + MessageTextInput( + name="system_prompt", + display_name="Agent Instructions", + info="Initial instructions and context provided to guide the agent's behavior.", + value="You are a helpful assistant that can use tools to answer questions and perform tasks.", + advanced=False, + ), + *LCToolsAgentComponent._base_inputs, + + DataInput(name="chat_history", display_name="Chat Memory", is_list=True, advanced=True), + + ] outputs = [Output(name="response", display_name="Response", method="get_response")] From ce911b46e6c55b1561832849894a986a9d62b538 Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Wed, 30 Oct 2024 22:14:17 +0000 Subject: [PATCH 10/21] [autofix.ci] apply automated fixes --- src/backend/base/langflow/components/agents/simple_agent.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py index bf2d803f0a4..cc611883c2a 100644 --- a/src/backend/base/langflow/components/agents/simple_agent.py +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -49,10 +49,7 @@ class SimpleAgentComponent(ToolCallingAgentComponent): advanced=False, ), *LCToolsAgentComponent._base_inputs, - DataInput(name="chat_history", display_name="Chat Memory", is_list=True, advanced=True), - - ] outputs = [Output(name="response", display_name="Response", method="get_response")] From 410ef12725b208a3daf1c93ff0844e095b11f628 Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Thu, 31 Oct 2024 18:21:25 -0400 Subject: [PATCH 11/21] Update simple_agent.py chat memory added --- .../components/agents/simple_agent.py | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py index cc611883c2a..0d08edf6c76 100644 --- a/src/backend/base/langflow/components/agents/simple_agent.py +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -1,14 +1,16 @@ from langflow.base.agents.agent import LCToolsAgentComponent from langflow.base.models.model import LCModelComponent from langflow.components.agents.tool_calling import ToolCallingAgentComponent +from langflow.components.helpers.memory import MemoryComponent from langflow.components.models.azure_openai import AzureChatOpenAIComponent from langflow.components.models.openai import OpenAIModelComponent from langflow.io import ( DataInput, DropdownInput, - MessageTextInput, + MultilineInput, Output, ) +from langflow.schema.data import Data from langflow.schema.dotdict import dotdict from langflow.schema.message import Message @@ -22,6 +24,8 @@ class SimpleAgentComponent(ToolCallingAgentComponent): openai_inputs = [ component_input + if component_input.name != "temperature" + else setattr(component_input, "advanced", True) or component_input for component_input in OpenAIModelComponent().inputs if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] ] @@ -30,6 +34,11 @@ class SimpleAgentComponent(ToolCallingAgentComponent): for component_input in AzureChatOpenAIComponent().inputs if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] ] + memory_inputs = [ + setattr(component_input, "advanced", True) or component_input + for component_input in MemoryComponent().inputs + ] + inputs = [ DropdownInput( name="agent_llm", @@ -41,7 +50,7 @@ class SimpleAgentComponent(ToolCallingAgentComponent): input_types=[], ), *openai_inputs, - MessageTextInput( + MultilineInput( name="system_prompt", display_name="Agent Instructions", info="Initial instructions and context provided to guide the agent's behavior.", @@ -49,7 +58,7 @@ class SimpleAgentComponent(ToolCallingAgentComponent): advanced=False, ), *LCToolsAgentComponent._base_inputs, - DataInput(name="chat_history", display_name="Chat Memory", is_list=True, advanced=True), + *memory_inputs ] outputs = [Output(name="response", display_name="Response", method="get_response")] @@ -58,6 +67,7 @@ async def get_response(self) -> Message: if llm_model is None: msg = "No language model selected" raise ValueError(msg) + self.chat_history = self.get_memory_data() agent = ToolCallingAgentComponent().set( llm=llm_model, @@ -69,6 +79,16 @@ async def get_response(self) -> Message: return await agent.message_response() + + + def get_memory_data(self): + memory_kwargs = { + component_input.name: getattr(self, f"{component_input.name}") + for component_input in self.memory_inputs + } + + return MemoryComponent().set(**memory_kwargs).retrieve_messages() + def get_llm(self): try: if self.agent_llm == "OpenAI": From caea6b1164921014dbf63e325ef187ec8e98ca7e Mon Sep 17 00:00:00 2001 From: "autofix-ci[bot]" <114827586+autofix-ci[bot]@users.noreply.github.com> Date: Thu, 31 Oct 2024 22:21:54 +0000 Subject: [PATCH 12/21] [autofix.ci] apply automated fixes --- .../base/langflow/components/agents/simple_agent.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py index 0d08edf6c76..ac3e878bdf1 100644 --- a/src/backend/base/langflow/components/agents/simple_agent.py +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -5,12 +5,10 @@ from langflow.components.models.azure_openai import AzureChatOpenAIComponent from langflow.components.models.openai import OpenAIModelComponent from langflow.io import ( - DataInput, DropdownInput, MultilineInput, Output, ) -from langflow.schema.data import Data from langflow.schema.dotdict import dotdict from langflow.schema.message import Message @@ -35,8 +33,7 @@ class SimpleAgentComponent(ToolCallingAgentComponent): if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] ] memory_inputs = [ - setattr(component_input, "advanced", True) or component_input - for component_input in MemoryComponent().inputs + setattr(component_input, "advanced", True) or component_input for component_input in MemoryComponent().inputs ] inputs = [ @@ -58,7 +55,7 @@ class SimpleAgentComponent(ToolCallingAgentComponent): advanced=False, ), *LCToolsAgentComponent._base_inputs, - *memory_inputs + *memory_inputs, ] outputs = [Output(name="response", display_name="Response", method="get_response")] @@ -79,12 +76,9 @@ async def get_response(self) -> Message: return await agent.message_response() - - def get_memory_data(self): memory_kwargs = { - component_input.name: getattr(self, f"{component_input.name}") - for component_input in self.memory_inputs + component_input.name: getattr(self, f"{component_input.name}") for component_input in self.memory_inputs } return MemoryComponent().set(**memory_kwargs).retrieve_messages() From 5f9a6d618facb1a61da831f1ccb32a04bf2998cc Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Thu, 31 Oct 2024 18:41:16 -0400 Subject: [PATCH 13/21] lint Errors Solved --- .../components/agents/simple_agent.py | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/backend/base/langflow/components/agents/simple_agent.py b/src/backend/base/langflow/components/agents/simple_agent.py index ac3e878bdf1..d4f6258ac9f 100644 --- a/src/backend/base/langflow/components/agents/simple_agent.py +++ b/src/backend/base/langflow/components/agents/simple_agent.py @@ -13,6 +13,11 @@ from langflow.schema.message import Message +def set_advanced_true(component_input): + component_input.advanced = True + return component_input + + class SimpleAgentComponent(ToolCallingAgentComponent): display_name: str = "Agent" description: str = "Define the agent's instructions, then enter a task to complete using tools." @@ -20,22 +25,19 @@ class SimpleAgentComponent(ToolCallingAgentComponent): beta = True name = "Agent" - openai_inputs = [ - component_input - if component_input.name != "temperature" - else setattr(component_input, "advanced", True) or component_input - for component_input in OpenAIModelComponent().inputs - if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] - ] azure_inputs = [ - component_input + set_advanced_true(component_input) if component_input.name == "temperature" else component_input for component_input in AzureChatOpenAIComponent().inputs if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] ] - memory_inputs = [ - setattr(component_input, "advanced", True) or component_input for component_input in MemoryComponent().inputs + openai_inputs = [ + set_advanced_true(component_input) if component_input.name == "temperature" else component_input + for component_input in OpenAIModelComponent().inputs + if component_input.name not in [input_field.name for input_field in LCModelComponent._base_inputs] ] + memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs] + inputs = [ DropdownInput( name="agent_llm", From 4c81f5b08d799ec6eee8fd4fa891a4dace579976 Mon Sep 17 00:00:00 2001 From: Edwin Jose Date: Thu, 31 Oct 2024 18:41:34 -0400 Subject: [PATCH 14/21] Squashed commit of the following: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit commit e965fd3b7f518753cb9c56a1029df8113316af11 Author: Sebastián Estévez Date: Thu Oct 31 17:16:08 2024 -0400 fix: broken docker test (#4337) * Fix broken docker test Fixes #4336 Update the Docker test workflow to use the `get_version` function from `langflow.utils.version`. * Modify the `Test image` step in `.github/workflows/docker_test.yml` to use the `get_version` function instead of directly importing `__version__`. * Modify the `Test backend image` step in `.github/workflows/docker_test.yml` to use the `get_version` function instead of directly importing `__version__`. --- For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/langflow-ai/langflow/issues/4336?shareId=XXXX-XXXX-XXXX-XXXX). * version.version not util.version commit 3279b8a1e83cb83cc805f173328e23751e2a8206 Author: Lucas Oliveira <62335616+lucaseduoli@users.noreply.github.com> Date: Thu Oct 31 16:39:58 2024 -0300 fix: add collapsible function to templates and fix design bugs (#4305) * Updated colors * Fixed design for small screens * Change border radius * Changed size of text on templates description * Fix shine effect on small screens * Fixed icons on starter templates * Updated mono font to JetBrains * Updated icon hit area for X * Added gradient wrapper and x-gradient * Changed colors and font weights for nav component * Added zoom on hover of gradient * Fixed input size * Fixed all templates to show everything * Hide scrollbar * Change text size of card * Removed title of the categories * Removed unused currentTab from templatecategory * Updated position of search icon * Updated style of inputs * Updated search clear button * Fixed bug on small screens * Added no results query * Fixed background on get started cards * Added focus ring on nav component * Added tab index to search and sidebar buttons * Added keyboard navigation to templates * Updated templatesModal to use ShadCN Sidebar * Implemented collapsible sidebar * Fix collapsible to work on mobile but be overlaying content * Added noise to styleUtils * Updated padding and sizes for mobile * Updated text size * Updated font family to inter * Made get started components fetch title and description from the flow * Updated description on get started component * Updated naming of sidebar * Updated description of start from scratch * Updated color of selected sidebar item * Changed text color for sidebar not active items * changed description sizes * changed to line clamp * Reduced gap between icon and category text * Fixed no results state * Fixed X icon only appearing on hover * Fix auto focus issue * fixed hover color of primary button * Fixed gradients to use stops if it exists and stop using random gradient * removed random gradient * Fixed design of cards in templates * Updated nav to go through tests * Fixed focus on input * [autofix.ci] apply automated fixes * New color * fix testes * Fixed starter projects test * ✨ (starter-projects.spec.ts): add Page import to test function parameters for better code readability and maintainability 📝 (starter-projects.spec.ts): refactor test to include a function for waiting for template visibility, improving code readability and reducing duplication --------- Co-authored-by: Cristhian Zanforlin Lousa Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> commit a03da10750794b11cc43aa866a108e7e80d0ec4f Author: Sebastián Estévez Date: Thu Oct 31 14:57:07 2024 -0400 fix: langfuse upgrade and crew result usage (#4342) * lanfuse upgrade and crew result usage * ✅ (Dynamic Agent.spec.ts): Enable tests for checking the presence of required environment variables before running the test suite. ✨ (Hierarchical Agent.spec.ts): Add integration test for Hierarchical Tasks Agent to ensure proper functionality and behavior ✨ (Sequential Task Agent.spec.ts): Add integration test for Sequential Tasks Agent to ensure proper functionality and behavior --------- Co-authored-by: cristhianzl --- .github/workflows/docker_test.yml | 4 +- docs/css/custom.css | 22 +- pyproject.toml | 2 +- .../base/langflow/base/agents/crewai/crew.py | 2 +- src/frontend/index.html | 8 +- src/frontend/package-lock.json | 23 - src/frontend/package.json | 1 - .../components/NodeStatus/index.tsx | 2 +- .../src/components/GradientWrapper/index.tsx | 19 + .../components/popover/index.tsx | 2 +- src/frontend/src/components/ui/button.tsx | 8 +- src/frontend/src/components/ui/dialog.tsx | 11 +- src/frontend/src/components/ui/separator.tsx | 2 +- src/frontend/src/components/ui/sheet.tsx | 140 ++++ src/frontend/src/components/ui/sidebar.tsx | 751 ++++++++++++++++++ src/frontend/src/contexts/index.tsx | 23 +- src/frontend/src/hooks/use-mobile.ts | 22 + .../baseModal/helpers/switch-case-size.ts | 5 +- src/frontend/src/modals/baseModal/index.tsx | 3 +- .../components/GetStartedComponent/index.tsx | 30 +- .../TemplateCardComponent/index.tsx | 52 +- .../TemplateCategoryComponent/index.tsx | 16 +- .../TemplateContentComponent/index.tsx | 50 +- .../TemplateGetStartedCardComponent/index.tsx | 50 +- .../components/navComponent/index.tsx | 101 ++- .../src/modals/templatesModal/index.tsx | 99 ++- src/frontend/src/style/applies.css | 15 +- src/frontend/src/style/classes.css | 24 +- src/frontend/src/style/index.css | 3 + src/frontend/src/types/templates/types.ts | 9 +- src/frontend/src/utils/styleUtils.ts | 27 + src/frontend/tailwind.config.mjs | 28 +- .../core/integrations/Dynamic Agent.spec.ts | 195 +++-- .../integrations/Hierarchical Agent.spec.ts | 122 +++ .../Sequential Task Agent.spec.ts | 103 +++ .../features/starter-projects.spec.ts | 49 +- uv.lock | 8 +- 37 files changed, 1639 insertions(+), 392 deletions(-) create mode 100644 src/frontend/src/components/GradientWrapper/index.tsx create mode 100644 src/frontend/src/components/ui/sheet.tsx create mode 100644 src/frontend/src/components/ui/sidebar.tsx create mode 100644 src/frontend/src/hooks/use-mobile.ts create mode 100644 src/frontend/tests/core/integrations/Hierarchical Agent.spec.ts create mode 100644 src/frontend/tests/core/integrations/Sequential Task Agent.spec.ts diff --git a/.github/workflows/docker_test.yml b/.github/workflows/docker_test.yml index 07b826c588e..9564ccd271b 100644 --- a/.github/workflows/docker_test.yml +++ b/.github/workflows/docker_test.yml @@ -35,7 +35,7 @@ jobs: - name: Test image run: | expected_version=$(cat pyproject.toml | grep version | head -n 1 | cut -d '"' -f 2) - version=$(docker run --rm --entrypoint bash langflowai/langflow:latest-dev -c 'python -c "from langflow.version import __version__ as langflow_version; print(langflow_version)"') + version=$(docker run --rm --entrypoint bash langflowai/langflow:latest-dev -c 'python -c "from langflow.version.version import get_version; print(get_version())"') if [ "$expected_version" != "$version" ]; then echo "Expected version: $expected_version" echo "Actual version: $version" @@ -51,7 +51,7 @@ jobs: - name: Test backend image run: | expected_version=$(cat pyproject.toml | grep version | head -n 1 | cut -d '"' -f 2) - version=$(docker run --rm --entrypoint bash langflowai/langflow-backend:latest-dev -c 'python -c "from langflow.version import __version__ as langflow_version; print(langflow_version)"') + version=$(docker run --rm --entrypoint bash langflowai/langflow-backend:latest-dev -c 'python -c "from langflow.version.version import get_version; print(get_version())"') if [ "$expected_version" != "$version" ]; then echo "Expected version: $expected_version" echo "Actual version: $version" diff --git a/docs/css/custom.css b/docs/css/custom.css index 68d901e0761..85dba23c945 100644 --- a/docs/css/custom.css +++ b/docs/css/custom.css @@ -8,7 +8,7 @@ --ifm-navbar-link-hover-color: initial; --ifm-navbar-padding-vertical: 0; --ifm-navbar-item-padding-vertical: 0; - --ifm-font-family-base: -apple-system, BlinkMacSystemFont, Inter, Helvetica, + --ifm-font-family-base: Inter, -apple-system, BlinkMacSystemFont, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI emoji"; --ifm-font-family-monospace: "SFMono-Regular", "Roboto Mono", Consolas, "Liberation Mono", Menlo, Courier, monospace; @@ -118,17 +118,15 @@ body { width: 24px; height: 24px; display: flex; - background: url("/logos/gitLight.svg") - no-repeat; + background: url("/logos/gitLight.svg") no-repeat; } -[data-theme='dark'] .header-github-link:before { +[data-theme="dark"] .header-github-link:before { content: ""; width: 24px; height: 24px; display: flex; - background: url("/logos/gitDark.svg") - no-repeat; + background: url("/logos/gitDark.svg") no-repeat; } /* Twitter */ @@ -145,7 +143,7 @@ body { background-size: contain; } -[data-theme='dark'] .header-twitter-link::before { +[data-theme="dark"] .header-twitter-link::before { content: ""; width: 24px; height: 24px; @@ -164,7 +162,7 @@ body { opacity: 0.6; } -[data-theme='dark'] .header-discord-link::before { +[data-theme="dark"] .header-discord-link::before { content: ""; width: 24px; height: 24px; @@ -241,6 +239,8 @@ body { min-height: 70px; } -.theme-doc-sidebar-item-category.theme-doc-sidebar-item-category-level-2.menu__list-item:not(:first-child) { - margin-top: 0.25rem!important; -} \ No newline at end of file +.theme-doc-sidebar-item-category.theme-doc-sidebar-item-category-level-2.menu__list-item:not( + :first-child + ) { + margin-top: 0.25rem !important; +} diff --git a/pyproject.toml b/pyproject.toml index fbf33a382c0..30212ddd4b7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -54,7 +54,7 @@ dependencies = [ "redis>=5.0.1", "metaphor-python>=0.1.11", 'pywin32>=306; sys_platform == "win32"', - "langfuse>=2.33.0", + "langfuse~=2.53.1", "metal-sdk>=2.5.0", "markupsafe>=2.1.3", "boto3~=1.34.162", diff --git a/src/backend/base/langflow/base/agents/crewai/crew.py b/src/backend/base/langflow/base/agents/crewai/crew.py index c2cc019862c..8a78199558a 100644 --- a/src/backend/base/langflow/base/agents/crewai/crew.py +++ b/src/backend/base/langflow/base/agents/crewai/crew.py @@ -78,6 +78,6 @@ def step_callback(agent_output: AgentFinish | list[tuple[AgentAction, str]]) -> async def build_output(self) -> Message: crew = self.build_crew() result = await crew.kickoff_async() - message = Message(text=result, sender=MESSAGE_SENDER_AI) + message = Message(text=result.raw, sender=MESSAGE_SENDER_AI) self.status = message return message diff --git a/src/frontend/index.html b/src/frontend/index.html index cbbde528331..1091280085e 100644 --- a/src/frontend/index.html +++ b/src/frontend/index.html @@ -5,12 +5,10 @@ + + -