Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions src/praisonai-agents/praisonaiagents/agents/autoagents.py
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,43 @@ def _display_agents_and_tasks(self, agents: List[Agent], tasks: List[Task]):
Tools: {', '.join(task_tools)}"""
)

def _normalize_config(self, config_dict: Dict[str, Any]) -> Dict[str, Any]:
"""
Normalize the configuration dictionary to ensure tasks are proper TaskConfig objects.

This handles cases where LLMs return tasks as strings instead of dictionaries.
"""
if 'agents' in config_dict:
for agent in config_dict['agents']:
if 'tasks' in agent and isinstance(agent['tasks'], list):
normalized_tasks = []
for task in agent['tasks']:
if isinstance(task, str):
# Convert string task to TaskConfig format
normalized_task = {
'name': task[:50], # Use first 50 chars as name
'description': task,
'expected_output': f"Completed: {task}",
'tools': [] # Will be populated based on agent tools
}
normalized_tasks.append(normalized_task)
elif isinstance(task, dict):
# Ensure all required fields are present
if 'name' not in task:
task['name'] = task.get('description', 'Task')[:50]
if 'description' not in task:
task['description'] = task.get('name', 'Task description')
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The current logic for setting a default description when it's missing results in it being a copy of the name. This can lead to redundant information and less descriptive task configurations, as the name is often a summary. Consider using a more informative default for the description to improve clarity.

Suggested change
task['description'] = task.get('name', 'Task description')
task['description'] = f"Complete the task: {task['name']}"

if 'expected_output' not in task:
task['expected_output'] = f"Completed: {task.get('name', 'task')}"
if 'tools' not in task:
task['tools'] = []
normalized_tasks.append(task)
else:
# Skip invalid task types
logging.warning(f"Skipping invalid task type: {type(task)}")
agent['tasks'] = normalized_tasks
return config_dict

def _get_available_tools(self) -> List[str]:
"""Get list of available tools"""
if not self.tools:
Expand Down Expand Up @@ -289,6 +326,8 @@ def _generate_config(self) -> AutoAgentsConfig:
try:
# First try to parse as is
config_dict = json.loads(response_text)
# Normalize tasks if they are strings
config_dict = self._normalize_config(config_dict)
config = AutoAgentsConfig(**config_dict)
except json.JSONDecodeError:
# If that fails, try to extract JSON from the response
Expand All @@ -303,6 +342,8 @@ def _generate_config(self) -> AutoAgentsConfig:
cleaned_response = cleaned_response.strip()

config_dict = json.loads(cleaned_response)
# Normalize tasks if they are strings
config_dict = self._normalize_config(config_dict)
config = AutoAgentsConfig(**config_dict)

# Ensure we have exactly max_agents number of agents
Expand Down
81 changes: 81 additions & 0 deletions src/praisonai-agents/test_autoagents_fix.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
#!/usr/bin/env python3
"""Test script to verify the AutoAgents fix for string tasks"""

import json
from praisonaiagents.agents.autoagents import AutoAgents

# Test the _normalize_config method directly
def test_normalize_config():
# Create a minimal AutoAgents instance just to test the method
agents = AutoAgents(
instructions="Test",
max_agents=1
)

# Test case 1: Tasks as strings (the problematic case)
config_dict = {
"main_instruction": "Test instruction",
"process_type": "sequential",
"agents": [
{
"name": "Agent 1",
"role": "Test role",
"goal": "Test goal",
"backstory": "Test backstory",
"tools": [],
"tasks": [
"Get the current stock price for Google (GOOG).",
"Get the current stock price for Apple (AAPL)."
]
}
]
}

print("Original config with string tasks:")
print(json.dumps(config_dict, indent=2))

# Normalize the config
normalized = agents._normalize_config(config_dict.copy())

print("\nNormalized config:")
print(json.dumps(normalized, indent=2))

# Verify tasks are now dictionaries
for agent in normalized['agents']:
for task in agent['tasks']:
assert isinstance(task, dict), f"Task should be dict, got {type(task)}"
assert 'name' in task, "Task missing 'name' field"
assert 'description' in task, "Task missing 'description' field"
assert 'expected_output' in task, "Task missing 'expected_output' field"
assert 'tools' in task, "Task missing 'tools' field"

print("\n✅ Test passed: String tasks are properly normalized to TaskConfig format")

# Test case 2: Tasks already as dictionaries (should work as before)
config_dict2 = {
"main_instruction": "Test instruction",
"process_type": "sequential",
"agents": [
{
"name": "Agent 1",
"role": "Test role",
"goal": "Test goal",
"backstory": "Test backstory",
"tools": [],
"tasks": [
{
"name": "Get Google stock",
"description": "Get the current stock price for Google (GOOG).",
"expected_output": "Stock price of Google",
"tools": ["get_stock_price"]
}
]
}
]
}

normalized2 = agents._normalize_config(config_dict2.copy())
print("\n✅ Test passed: Dict tasks remain properly formatted")
Comment on lines +77 to +79
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This test case for dictionary-based tasks is missing assertions, which means it doesn't actually verify that the _normalize_config method works correctly for this scenario. A test without assertions can provide a false sense of security.

Add an assertion to ensure that a valid configuration is not modified by the normalization process.

Additionally, this test file appears to be a standalone script. For better maintainability and integration with CI/CD pipelines, structure it as a proper test using a framework like pytest. This would involve removing print statements in favor of framework-based reporting and removing the if __name__ == "__main__" block.

Suggested change
normalized2 = agents._normalize_config(config_dict2.copy())
print("\n✅ Test passed: Dict tasks remain properly formatted")
normalized2 = agents._normalize_config(config_dict2.copy())
assert normalized2 == config_dict2, "Normalization should not alter an already valid configuration."


if __name__ == "__main__":
test_normalize_config()
Loading