feat: Add SimpleAgentComponent and related pytests (#4318)
* simple ai agent new component: simple ai agent py tests for tool calling agent as well as for simple ai agent * Update simple_agent.py update _config Changes and code oprimised * Update simple_agent.py Error or inputType as None Solved * [autofix.ci] apply automated fixes * fix: INP001 ruff error * Update test_tool_calling_agent.py added pytest mark as api required * Update simple_agent.py added system prompt * [autofix.ci] apply automated fixes * updates in the input field orders * [autofix.ci] apply automated fixes * Update simple_agent.py chat memory added * [autofix.ci] apply automated fixes * lint Errors Solved * Squashed commit of the following: commite965fd3b7fAuthor: Sebastián Estévez <estevezsebastian@gmail.com> 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 commit3279b8a1e8Author: 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 <cristhian.lousa@gmail.com> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> commita03da10750Author: Sebastián Estévez <estevezsebastian@gmail.com> 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 <cristhian.lousa@gmail.com> * [autofix.ci] apply automated fixes * Update worker.py adding # noqa: to solve CI Error * [autofix.ci] apply automated fixes * Update worker.py removed unused params that were causing the issue --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: italojohnny <italojohnnydosanjos@gmail.com>
This commit is contained in:
parent
0fed08a4cb
commit
e2c26f1455
7 changed files with 883 additions and 751 deletions
|
|
@ -136,10 +136,10 @@ class LCAgentComponent(Component):
|
|||
|
||||
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:
|
||||
|
|
|
|||
156
src/backend/base/langflow/components/agents/agent.py
Normal file
156
src/backend/base/langflow/components/agents/agent.py
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
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 (
|
||||
DropdownInput,
|
||||
MultilineInput,
|
||||
Output,
|
||||
)
|
||||
from langflow.schema.dotdict import dotdict
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
def set_advanced_true(component_input):
|
||||
component_input.advanced = True
|
||||
return component_input
|
||||
|
||||
|
||||
class AgentComponent(ToolCallingAgentComponent):
|
||||
display_name: str = "Agent"
|
||||
description: str = "Define the agent's instructions, then enter a task to complete using tools."
|
||||
icon = "bot"
|
||||
beta = True
|
||||
name = "Agent"
|
||||
|
||||
azure_inputs = [
|
||||
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]
|
||||
]
|
||||
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",
|
||||
display_name="Model Provider",
|
||||
options=["Azure OpenAI", "OpenAI", "Custom"],
|
||||
value="OpenAI",
|
||||
real_time_refresh=True,
|
||||
refresh_button=True,
|
||||
input_types=[],
|
||||
),
|
||||
*openai_inputs,
|
||||
MultilineInput(
|
||||
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,
|
||||
*memory_inputs,
|
||||
]
|
||||
outputs = [Output(name="response", display_name="Response", method="get_response")]
|
||||
|
||||
async def get_response(self) -> Message:
|
||||
llm_model = self.get_llm()
|
||||
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,
|
||||
tools=[self.tools],
|
||||
chat_history=self.chat_history,
|
||||
input_value=self.input_value,
|
||||
system_prompt=self.system_prompt,
|
||||
)
|
||||
|
||||
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":
|
||||
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
|
||||
|
||||
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()
|
||||
|
||||
def delete_fields(self, build_config, fields):
|
||||
for field in fields:
|
||||
build_config.pop(field, 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_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":
|
||||
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
|
||||
|
||||
def update_input_types(self, build_config):
|
||||
for key, value in build_config.items():
|
||||
# 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
|
||||
File diff suppressed because one or more lines are too long
0
src/backend/tests/unit/components/agents/__init__.py
Normal file
0
src/backend/tests/unit/components/agents/__init__.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
import os
|
||||
|
||||
import pytest
|
||||
from langflow.components.agents.agent import AgentComponent
|
||||
from langflow.components.tools.calculator import CalculatorToolComponent
|
||||
|
||||
|
||||
@pytest.mark.api_key_required
|
||||
@pytest.mark.asyncio
|
||||
async def test_agent_component_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 AgentComponent with mocked inputs
|
||||
agent = AgentComponent(
|
||||
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")
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
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?"
|
||||
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")
|
||||
|
|
@ -77,6 +77,8 @@ test("Simple Agent", async ({ page }) => {
|
|||
.getByTestId("popover-anchor-input-api_key")
|
||||
.fill(process.env.OPENAI_API_KEY ?? "");
|
||||
|
||||
await page.getByTestId("fit_view").click();
|
||||
|
||||
await page.getByTestId("dropdown_str_model_name").click();
|
||||
await page.getByTestId("gpt-4o-1-option").click();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue