diff --git a/lib/crewai/src/crewai/tools/structured_tool.py b/lib/crewai/src/crewai/tools/structured_tool.py index 8ecba85496..946b203e11 100644 --- a/lib/crewai/src/crewai/tools/structured_tool.py +++ b/lib/crewai/src/crewai/tools/structured_tool.py @@ -58,6 +58,11 @@ def _format_tool_output_for_agent(tool: Any, raw_result: Any) -> str: result_schema = getattr(tool, "result_schema", None) if not (isinstance(result_schema, type) and issubclass(result_schema, BaseModel)): + if isinstance(raw_result, (dict, list)): + try: + return json.dumps(raw_result, ensure_ascii=False) + except Exception: + return str(raw_result) return str(raw_result) try: @@ -70,6 +75,23 @@ def _format_tool_output_for_agent(tool: Any, raw_result: Any) -> str: validated = result_schema.model_validate(validation_input) return validated.model_dump_json() except Exception as exc: + if isinstance(raw_result, (dict, list)): + try: + serialized = json.dumps(raw_result, ensure_ascii=False) + warnings.warn( + ( + f"Failed to validate or serialize output from tool " + f"'{getattr(tool, 'name', '')}' using result_schema " + f"'{result_schema.__name__}': {exc.__class__.__name__}. " + "Falling back to JSON-serialized raw_result." + ), + RuntimeWarning, + stacklevel=2, + ) + return serialized + except Exception: + pass + warnings.warn( ( f"Failed to validate or serialize output from tool " diff --git a/lib/crewai/tests/tools/test_structured_tool.py b/lib/crewai/tests/tools/test_structured_tool.py index 0241abbcff..90beeb590b 100644 --- a/lib/crewai/tests/tools/test_structured_tool.py +++ b/lib/crewai/tests/tools/test_structured_tool.py @@ -519,3 +519,31 @@ def failing_function(should_fail: bool) -> str: tool.invoke({"should_fail": True}) assert call_count == 1 + + +def test_format_output_for_agent_with_dict_and_list(): + """Test that format_output_for_agent automatically serializes dict and list outputs to JSON.""" + def dict_tool() -> dict: + return {"nested": {"key": "value"}} + + tool = CrewStructuredTool.from_function( + func=dict_tool, + name="dict_tool", + description="A tool returning a dict", + ) + + result = tool.format_output_for_agent({"nested": {"key": "value"}}) + assert json.loads(result) == {"nested": {"key": "value"}} + + def list_tool() -> list: + return [{"nested": {"key": "value"}}] + + tool_list = CrewStructuredTool.from_function( + func=list_tool, + name="list_tool", + description="A tool returning a list", + ) + + result_list = tool_list.format_output_for_agent([{"nested": {"key": "value"}}]) + assert json.loads(result_list) == [{"nested": {"key": "value"}}] +