-
Notifications
You must be signed in to change notification settings - Fork 6.2k
refactor: Optimize agent component for efficiency and readability #5593
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Arikatakur
wants to merge
10
commits into
langflow-ai:main
Choose a base branch
from
Arikatakur:main
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
cc9af14
fix: filter out empty messages in chat history to resolve "String too…
Arikatakur 2f947d9
fix: filter out empty messages in chat history (#5505)
Arikatakur 059f9d8
style: fix Ruff violations in agent.py (TRY003, EM101)
Arikatakur ec041b2
Merge branch 'langflow-ai:main' into main
Arikatakur 1ecf627
refactor: Optimize agent component for efficiency and readability
Arikatakur 388ef5f
[autofix.ci] apply automated fixes
autofix-ci[bot] d03de32
Merge branch 'langflow-ai:main' into main
Arikatakur cea0e90
refactor: Optimize agent component for efficiency and readability
Arikatakur 3c2004c
[autofix.ci] apply automated fixes
autofix-ci[bot] 22b8d57
Merge branch 'main' into main
Arikatakur File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import logging | ||
import re | ||
from abc import abstractmethod | ||
from typing import TYPE_CHECKING, cast | ||
|
@@ -28,7 +29,7 @@ | |
|
||
|
||
DEFAULT_TOOLS_DESCRIPTION = "A helpful assistant with access to the following tools:" | ||
DEFAULT_AGENT_NAME = "Agent ({tools_names})" | ||
TOOL_NAME_PATTERN = re.compile(r"^[a-zA-Z0-9_-]+$") | ||
|
||
|
||
class LCAgentComponent(Component): | ||
|
@@ -81,82 +82,62 @@ | |
"""Run the agent and return the response.""" | ||
agent = self.build_agent() | ||
message = await self.run_agent(agent=agent) | ||
|
||
self.status = message | ||
return message | ||
|
||
def _validate_outputs(self) -> None: | ||
required_output_methods = ["build_agent"] | ||
output_names = [output.name for output in self.outputs] | ||
for method_name in required_output_methods: | ||
if method_name not in output_names: | ||
msg = f"Output with name '{method_name}' must be defined." | ||
raise ValueError(msg) | ||
if not hasattr(self, method_name): | ||
msg = f"Method '{method_name}' must be defined." | ||
raise ValueError(msg) | ||
|
||
def get_agent_kwargs(self, *, flatten: bool = False) -> dict: | ||
base = { | ||
def get_agent_kwargs(self) -> dict: | ||
"""Get agent configuration arguments.""" | ||
return { | ||
"handle_parsing_errors": self.handle_parsing_errors, | ||
"verbose": self.verbose, | ||
"allow_dangerous_code": True, | ||
} | ||
agent_kwargs = { | ||
"handle_parsing_errors": self.handle_parsing_errors, | ||
"max_iterations": self.max_iterations, | ||
"allow_dangerous_code": True, | ||
} | ||
if flatten: | ||
return { | ||
**base, | ||
**agent_kwargs, | ||
} | ||
return {**base, "agent_executor_kwargs": agent_kwargs} | ||
|
||
def get_chat_history_data(self) -> list[Data] | None: | ||
# might be overridden in subclasses | ||
"""Retrieve chat history data if available.""" | ||
return None | ||
|
||
async def run_agent( | ||
self, | ||
agent: Runnable | BaseSingleActionAgent | BaseMultiActionAgent | AgentExecutor, | ||
) -> Message: | ||
if isinstance(agent, AgentExecutor): | ||
runnable = agent | ||
else: | ||
if not hasattr(self, "tools") or not self.tools: | ||
msg = "Tools are required to run the agent." | ||
raise ValueError(msg) | ||
handle_parsing_errors = hasattr(self, "handle_parsing_errors") and self.handle_parsing_errors | ||
verbose = hasattr(self, "verbose") and self.verbose | ||
max_iterations = hasattr(self, "max_iterations") and self.max_iterations | ||
runnable = AgentExecutor.from_agent_and_tools( | ||
"""Run the agent and process its events.""" | ||
if not hasattr(self, "tools") or not self.tools: | ||
error_message = "Tools are required to run the agent." | ||
raise ValueError(error_message) | ||
|
||
runnable = ( | ||
agent | ||
if isinstance(agent, AgentExecutor) | ||
else AgentExecutor.from_agent_and_tools( | ||
agent=agent, | ||
tools=self.tools, | ||
handle_parsing_errors=handle_parsing_errors, | ||
verbose=verbose, | ||
max_iterations=max_iterations, | ||
**self.get_agent_kwargs(), | ||
) | ||
) | ||
|
||
input_dict: dict[str, str | list[BaseMessage]] = {"input": self.input_value} | ||
if hasattr(self, "system_prompt"): | ||
input_dict["system_prompt"] = self.system_prompt | ||
if hasattr(self, "chat_history") and self.chat_history: | ||
input_dict["chat_history"] = data_to_messages(self.chat_history) | ||
|
||
if hasattr(self, "graph"): | ||
session_id = self.graph.session_id | ||
elif hasattr(self, "_session_id"): | ||
session_id = self._session_id | ||
else: | ||
session_id = None | ||
|
||
if hasattr(self, "chat_history") and self.chat_history: | ||
messages = data_to_messages(self.chat_history) | ||
filtered_messages = [msg for msg in messages if msg.get("content", "").strip()] | ||
if not filtered_messages: | ||
error_message = "No valid messages to process in chat history." | ||
raise ValueError(error_message) | ||
input_dict["chat_history"] = filtered_messages | ||
Comment on lines
+126
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no problem in having 0 messages. |
||
|
||
session_id = getattr(self, "graph", getattr(self, "_session_id", None)) | ||
agent_message = Message( | ||
sender=MESSAGE_SENDER_AI, | ||
sender_name=self.display_name or "Agent", | ||
properties={"icon": "Bot", "state": "partial"}, | ||
content_blocks=[ContentBlock(title="Agent Steps", contents=[])], | ||
session_id=session_id, | ||
) | ||
|
||
try: | ||
result = await process_agent_events( | ||
runnable.astream_events( | ||
|
@@ -172,7 +153,8 @@ | |
await delete_message(id_=msg_id) | ||
await self._send_message_event(e.agent_message, category="remove_message") | ||
raise | ||
except Exception: | ||
except Exception as error: | ||
logging.exception("An error occurred: %s", error) | ||
raise | ||
|
||
self.status = result | ||
|
@@ -184,15 +166,14 @@ | |
|
||
def validate_tool_names(self) -> None: | ||
"""Validate tool names to ensure they match the required pattern.""" | ||
pattern = re.compile(r"^[a-zA-Z0-9_-]+$") | ||
if hasattr(self, "tools") and self.tools: | ||
for tool in self.tools: | ||
if not pattern.match(tool.name): | ||
msg = ( | ||
f"Invalid tool name '{tool.name}': must only contain letters, numbers, underscores, dashes," | ||
" and cannot contain spaces." | ||
if not TOOL_NAME_PATTERN.match(tool.name): | ||
error_message = ( | ||
f"Invalid tool name '{tool.name}': must only contain letters, numbers, underscores, dashes, " | ||
"and cannot contain spaces." | ||
) | ||
raise ValueError(msg) | ||
raise ValueError(error_message) | ||
|
||
|
||
class LCToolsAgentComponent(LCAgentComponent): | ||
|
@@ -209,36 +190,36 @@ | |
] | ||
|
||
def build_agent(self) -> AgentExecutor: | ||
"""Build the agent executor with tools.""" | ||
self.validate_tool_names() | ||
agent = self.create_agent_runnable() | ||
return AgentExecutor.from_agent_and_tools( | ||
agent=RunnableAgent(runnable=agent, input_keys_arg=["input"], return_keys_arg=["output"]), | ||
tools=self.tools, | ||
**self.get_agent_kwargs(flatten=True), | ||
**self.get_agent_kwargs(), | ||
) | ||
|
||
@abstractmethod | ||
def create_agent_runnable(self) -> Runnable: | ||
"""Create the agent.""" | ||
|
||
def get_tool_name(self) -> str: | ||
"""Get the name of the tool.""" | ||
return self.display_name or "Agent" | ||
|
||
def get_tool_description(self) -> str: | ||
return self.agent_description or DEFAULT_TOOLS_DESCRIPTION | ||
"""Get the description of the tool.""" | ||
return DEFAULT_TOOLS_DESCRIPTION | ||
|
||
def _build_tools_names(self): | ||
tools_names = "" | ||
if self.tools: | ||
tools_names = ", ".join([tool.name for tool in self.tools]) | ||
return tools_names | ||
def _build_tools_names(self) -> str: | ||
"""Build a string of tool names.""" | ||
return ", ".join(tool.name for tool in self.tools) if self.tools else "" | ||
|
||
def to_toolkit(self) -> list[Tool]: | ||
"""Convert the component to a toolkit.""" | ||
component_toolkit = _get_component_toolkit() | ||
tools_names = self._build_tools_names() | ||
agent_description = self.get_tool_description() | ||
# TODO: Agent Description Depreciated Feature to be removed | ||
description = f"{agent_description}{tools_names}" | ||
description = f"{DEFAULT_TOOLS_DESCRIPTION}{tools_names}" | ||
tools = component_toolkit(component=self).get_tools( | ||
tool_name=self.get_tool_name(), tool_description=description, callbacks=self.get_langchain_callbacks() | ||
) | ||
|
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Error details:
AttributeError
Details: 'HumanMessage' object has no attribute 'get'