Incorrect Usage of Flow as Tool
Describe the bug
When invoking the Flow as Tool through the Tool Call Agent, the system displayed an error message:
TypeError: sequence item 0: expected str instance, dict found
│ /Users/xxx/miniconda3/lib/python3.11/site-packages/langflow/base/tools/flow_tool.py:113 in _arun │
│ │
│ 110 │ │ │ for output in run_output.outputs: │
│ 111 │ │ │ │ if output: │
│ 112 │ │ │ │ │ data.extend(build_data_from_result_data(output, │
│ get_final_results_only=self.get_final_results_only)) │
│ ❱ 113 │ │ return format_flow_output_data(data) │
│ 114 │
│ │
│ /Users/xxx/miniconda3/lib/python3.11/site-packages/langflow/base/flow_processing/utils.py:91 in │
│ format_flow_output_data │
│ │
│ 89 │ """ │
│ 90 │ result = "Flow run output:\n" │
│ ❱ 91 │ results = "\n".join([value.result for value in data if value.data["message"]]) │
│ 92 │ return result + results │
│ 93 │
╰───────────────────────────────────────────────────────────────────────────────────────────────────────╯
TypeError: sequence item 0: expected str instance, dict found
Browser and Version
- Browser: chrome
- Version: 1.0.5
To Reproduce Steps to reproduce the behavior:
- Create a flow: A
- Create a flow: B, and use Flow as Tool for A
- Run
- See error
Additional context The issue was resolved after I made the following modifications to the code. Details are as follows:
langflow/base/flow_processing/utils.py, line 91:
Before the modification:
results = "\n".join([value.result for value in data if value.data["message"]])
After the modification:
results = "\n".join([value.message["message"] for value in data if value.data["message"]])
To resolve the TypeError: sequence item 0: expected str instance, dict found error, you need to ensure that all items in the sequence being joined are strings. The error typically occurs when you try to join a list of dictionaries or other non-string items.
In the provided code, the issue likely arises when converting the result to a string. Specifically, the result["output"] might be a dictionary or a list of dictionaries instead of a string.
To fix this, you should convert the result["output"] to a string before returning it. Here is the modified part of the run_agent method:
async def run_agent(self) -> Message:
if "input" not in self.user_prompt:
raise ValueError("Prompt must contain 'input' key.")
messages = [
("system", self.system_prompt),
("placeholder", "{chat_history}"),
("human", self.user_prompt),
("placeholder", "{agent_scratchpad}"),
]
prompt = ChatPromptTemplate.from_messages(messages)
agent = create_tool_calling_agent(self.llm, self.tools, prompt)
runnable = AgentExecutor.from_agent_and_tools(
agent=cast(BaseSingleActionAgent, agent),
tools=self.tools,
verbose=True,
handle_parsing_errors=self.handle_parsing_errors,
)
input_dict: dict[str, str | list[Dict[str, str]]] = {"input": self.input_value}
if hasattr(self, "memory") and self.memory:
input_dict["chat_history"] = self.convert_chat_history(self.memory)
result = await runnable.ainvoke(input_dict)
self.status = result
if "output" not in result:
raise ValueError("Output key not found in result. Tried 'output'.")
# Ensure result_string is a string
result_string = str(result["output"])
return Message(text=result_string)
By converting result["output"] to a string using str(result["output"]), you ensure that the Message object receives a string, thus avoiding the TypeError [1].
Additionally, ensure that the user_prompt contains the 'input' key as required by the ToolCallingAgentComponent class:
async def build(
self,
input_value: str,
llm: BaseLanguageModel,
tools: List[Tool],
user_prompt: str = "{input}",
message_history: Optional[List[Record]] = None,
system_message: str = "You are a helpful assistant",
handle_parsing_errors: bool = True,
) -> Text:
if "input" not in user_prompt:
raise ValueError("Prompt must contain 'input' key.")
messages = [
("system", system_message),
("placeholder", "{chat_history}"),
("human", user_prompt),
("placeholder", "{agent_scratchpad}"),
]
prompt = ChatPromptTemplate.from_messages(messages)
agent = create_tool_calling_agent(llm, tools, prompt)
result = await self.run_agent(agent, input_value, tools, message_history, handle_parsing_errors)
self.status = result
return result
This ensures that the prompt is correctly formatted and the agent can process the input without errors [1].
In the context of using the Flow as Tool through the Tool Call Agent, ensure that the FlowToolComponent is correctly configured and that the result["output"] is properly handled. The FlowToolComponent class is designed to construct a Tool from a function that runs the loaded Flow, and it includes methods to retrieve and manage flows:
class FlowToolComponent(CustomComponent):
display_name = "Flow as Tool"
description = "Construct a Tool from a function that runs the loaded Flow."
field_order = ["flow_name", "name", "description", "return_direct"]
trace_type = "tool"
beta = True
def get_flow_names(self) -> List[str]:
flow_datas = self.list_flows()
return [flow_data.data["name"] for flow_data in flow_datas]
def get_flow(self, flow_name: str) -> Optional[Data]:
flow_datas = self.list_flows()
for flow_data in flow_datas:
if flow_data.data["name"] == flow_name:
return flow_data
return None
def update_build_config(self, build_config: dotdict, field_value: Any, field_name: str | None = None):
logger.debug(f"Updating build config with field value {field_value} and field name {field_name}")
if field_name == "flow_name":
build_config["flow_name"]["options"] = self.get_flow_names()
return build_config
def build_config(self):
return {
"flow_name": {
"display_name": "Flow Name",
"info": "The name of the flow to run.",
"options": [],
"real_time_refresh": True,
"refresh_button": True,
},
"name": {
"display_name": "Name",
"description": "The name of the tool.",
},
"description": {
"display_name": "Description",
"description": "The description of the tool.",
},
"return_direct": {
"display_name": "Return Direct",
"description": "Return the result directly from the Tool.",
"advanced": True,
},
}
async def build(self, flow_name: str, name: str, description: str, return_direct: bool = False) -> Tool:
FlowTool.update_forward_refs()
flow_data = self.get_flow(flow_name)
if not flow_data:
raise ValueError("Flow not found.")
graph = Graph.from_payload(flow_data.data["data"])
inputs = get_flow_inputs(graph)
tool = FlowTool(
name=name,
description=description,
graph=graph,
return_direct=return_direct,
inputs=inputs,
flow_id=str(flow_data.id),
user_id=str(self._user_id),
)
description_repr = repr(tool.description).strip("'")
args_str = "\n".join([f"- {arg_name}: {arg_data['description']}" for arg_name, arg_data in tool.args.items()])
self.status = f"{description_repr}\nArguments:\n{args_str}"
return tool # type: ignore
Ensure that the FlowToolComponent is properly instantiated and that the result["output"] is correctly converted to a string before being used [1].
To continue talking to Dosu, mention @dosu.
Hi @qiaolb Could you try using the new version 1.0.10 and see if the error still occurs?
Thank you for your contribution! This issue will be closed. If you have any questions or encounter another problem, please open a new issue and we will be ready to assist you.