Skip to content

Commit b95579d

Browse files
fix: comprehensive task_name undefined error resolution
- Add task_name, task_description, task_id parameters to achat method signature - Update all achat call sites in agents.py to pass task parameters where available - Provide fallback None values for API handler contexts without task context - Fix Agent.aexecute to extract task info when available - Resolves NameError: name ''task_name'' is not defined in parallel agent execution Fixes persistent errors in agentic parallelization workflows where callbacks expect task context parameters but they were not being passed correctly through the async execution chain. Co-authored-by: Mervin Praison <[email protected]>
1 parent cc5e04a commit b95579d

File tree

3 files changed

+186
-8
lines changed

3 files changed

+186
-8
lines changed

src/praisonai-agents/praisonaiagents/agent/agent.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1536,7 +1536,7 @@ def clean_json_output(self, output: str) -> str:
15361536
cleaned = cleaned[:-3].strip()
15371537
return cleaned
15381538

1539-
async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False):
1539+
async def achat(self, prompt: str, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False, task_name=None, task_description=None, task_id=None):
15401540
"""Async version of chat method with self-reflection support."""
15411541
# Log all parameter values when in debug mode
15421542
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
@@ -1944,7 +1944,11 @@ async def aexecute(self, task, context=None):
19441944
prompt = task
19451945
else:
19461946
prompt = str(task)
1947-
return await self.achat(prompt)
1947+
# Extract task info if available
1948+
task_name = getattr(task, 'name', None) if hasattr(task, 'name') else None
1949+
task_description = getattr(task, 'description', None) if hasattr(task, 'description') else None
1950+
task_id = getattr(task, 'id', None) if hasattr(task, 'id') else None
1951+
return await self.achat(prompt, task_name=task_name, task_description=task_description, task_id=task_id)
19481952

19491953
async def execute_tool_async(self, function_name: str, arguments: Dict[str, Any]) -> Any:
19501954
"""Async version of execute_tool"""
@@ -2113,7 +2117,7 @@ async def handle_agent_query(request: Request, query_data: Optional[AgentQuery]
21132117
try:
21142118
# Use async version if available, otherwise use sync version
21152119
if asyncio.iscoroutinefunction(self.chat):
2116-
response = await self.achat(query)
2120+
response = await self.achat(query, task_name=None, task_description=None, task_id=None)
21172121
else:
21182122
# Run sync function in a thread to avoid blocking
21192123
loop = asyncio.get_event_loop()
@@ -2234,7 +2238,7 @@ async def execute_agent_task(prompt: str) -> str:
22342238
try:
22352239
# Ensure self.achat is used as it's the async version and pass its tools
22362240
if hasattr(self, 'achat') and asyncio.iscoroutinefunction(self.achat):
2237-
response = await self.achat(prompt, tools=self.tools)
2241+
response = await self.achat(prompt, tools=self.tools, task_name=None, task_description=None, task_id=None)
22382242
elif hasattr(self, 'chat'): # Fallback for synchronous chat
22392243
loop = asyncio.get_event_loop()
22402244
response = await loop.run_in_executor(None, lambda p=prompt: self.chat(p, tools=self.tools))

src/praisonai-agents/praisonaiagents/agents/agents.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -362,14 +362,20 @@ def _get_multimodal_message(text_prompt, images):
362362
_get_multimodal_message(task_prompt, task.images),
363363
tools=tools,
364364
output_json=task.output_json,
365-
output_pydantic=task.output_pydantic
365+
output_pydantic=task.output_pydantic,
366+
task_name=task.name,
367+
task_description=task.description,
368+
task_id=task.id
366369
)
367370
else:
368371
agent_output = await executor_agent.achat(
369372
task_prompt,
370373
tools=tools,
371374
output_json=task.output_json,
372-
output_pydantic=task.output_pydantic
375+
output_pydantic=task.output_pydantic,
376+
task_name=task.name,
377+
task_description=task.description,
378+
task_id=task.id
373379
)
374380

375381
if agent_output:
@@ -1138,7 +1144,7 @@ async def handle_query(request: Request, query_data: Optional[AgentQuery] = None
11381144
try:
11391145
# Use async version if available, otherwise use sync version
11401146
if asyncio.iscoroutinefunction(agent_instance.chat):
1141-
response = await agent_instance.achat(current_input)
1147+
response = await agent_instance.achat(current_input, task_name=None, task_description=None, task_id=None)
11421148
else:
11431149
# Run sync function in a thread to avoid blocking
11441150
loop = asyncio.get_running_loop()
@@ -1294,7 +1300,7 @@ async def execute_workflow_tool(query: str) -> str: # Renamed for clarity
12941300
try:
12951301
logging.debug(f"Processing with agent: {agent_instance.name}")
12961302
if hasattr(agent_instance, 'achat') and asyncio.iscoroutinefunction(agent_instance.achat):
1297-
response = await agent_instance.achat(current_input, tools=agent_instance.tools)
1303+
response = await agent_instance.achat(current_input, tools=agent_instance.tools, task_name=None, task_description=None, task_id=None)
12981304
elif hasattr(agent_instance, 'chat'): # Fallback to sync chat if achat not suitable
12991305
loop = asyncio.get_running_loop()
13001306
response = await loop.run_in_executor(None, lambda ci=current_input: agent_instance.chat(ci, tools=agent_instance.tools))

test_task_name_fix.py

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test script to validate the task_name fix for agentic parallelization.
4+
This script tests the structure without requiring API keys.
5+
"""
6+
7+
import asyncio
8+
import sys
9+
import os
10+
11+
# Add the source path to sys.path
12+
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
13+
14+
def test_achat_signature():
15+
"""Test that achat method has the correct signature"""
16+
try:
17+
from praisonaiagents import Agent
18+
19+
# Create a basic agent
20+
agent = Agent(
21+
name="TestAgent",
22+
role="Test Role",
23+
goal="Test Goal",
24+
llm="mock-llm" # Using a mock LLM
25+
)
26+
27+
# Check if achat method exists and has the correct signature
28+
import inspect
29+
achat_sig = inspect.signature(agent.achat)
30+
params = list(achat_sig.parameters.keys())
31+
32+
required_params = ['prompt', 'temperature', 'tools', 'output_json', 'output_pydantic', 'reasoning_steps', 'task_name', 'task_description', 'task_id']
33+
34+
print("✅ Agent.achat signature test:")
35+
print(f" Method parameters: {params}")
36+
37+
missing_params = [p for p in required_params if p not in params]
38+
if missing_params:
39+
print(f" ❌ Missing parameters: {missing_params}")
40+
return False
41+
else:
42+
print(f" ✅ All required parameters present")
43+
return True
44+
45+
except Exception as e:
46+
print(f"❌ Error testing achat signature: {e}")
47+
return False
48+
49+
def test_task_structure():
50+
"""Test that Task objects have the required attributes"""
51+
try:
52+
from praisonaiagents import Agent, Task
53+
54+
# Create a basic task
55+
agent = Agent(
56+
name="TestAgent",
57+
role="Test Role",
58+
goal="Test Goal",
59+
llm="mock-llm"
60+
)
61+
62+
task = Task(
63+
name="test_task",
64+
description="Test task description",
65+
expected_output="Test output",
66+
agent=agent
67+
)
68+
69+
print("✅ Task structure test:")
70+
print(f" Task name: {getattr(task, 'name', 'MISSING')}")
71+
print(f" Task description: {getattr(task, 'description', 'MISSING')}")
72+
print(f" Task id: {getattr(task, 'id', 'MISSING')}")
73+
74+
has_name = hasattr(task, 'name')
75+
has_description = hasattr(task, 'description')
76+
has_id = hasattr(task, 'id')
77+
78+
if has_name and has_description and has_id:
79+
print(f" ✅ Task has all required attributes")
80+
return True
81+
else:
82+
print(f" ❌ Task missing attributes - name: {has_name}, description: {has_description}, id: {has_id}")
83+
return False
84+
85+
except Exception as e:
86+
print(f"❌ Error testing task structure: {e}")
87+
return False
88+
89+
async def test_achat_call():
90+
"""Test that achat can be called with task parameters"""
91+
try:
92+
from praisonaiagents import Agent
93+
94+
# Create a basic agent
95+
agent = Agent(
96+
name="TestAgent",
97+
role="Test Role",
98+
goal="Test Goal",
99+
llm="mock-llm" # This should gracefully handle mock LLM
100+
)
101+
102+
print("✅ Testing achat call with task parameters:")
103+
104+
# This should not raise a NameError for task_name anymore
105+
try:
106+
# We expect this to fail due to mock LLM, but NOT due to NameError: task_name not defined
107+
response = await agent.achat(
108+
"Test prompt",
109+
task_name="test_task",
110+
task_description="Test description",
111+
task_id="test_id"
112+
)
113+
print(f" ✅ achat call succeeded (unexpected but good!)")
114+
return True
115+
except NameError as e:
116+
if "task_name" in str(e):
117+
print(f" ❌ Still getting task_name NameError: {e}")
118+
return False
119+
else:
120+
print(f" ⚠️ Different NameError (acceptable): {e}")
121+
return True
122+
except Exception as e:
123+
if "task_name" in str(e) and "not defined" in str(e):
124+
print(f" ❌ Still getting task_name error: {e}")
125+
return False
126+
else:
127+
print(f" ✅ Different error (expected with mock LLM): {type(e).__name__}: {e}")
128+
return True
129+
130+
except Exception as e:
131+
print(f"❌ Error testing achat call: {e}")
132+
return False
133+
134+
async def main():
135+
"""Run all tests"""
136+
print("🧪 Testing task_name fix for agentic parallelization...")
137+
print()
138+
139+
results = []
140+
141+
# Test 1: Check achat signature
142+
results.append(test_achat_signature())
143+
print()
144+
145+
# Test 2: Check task structure
146+
results.append(test_task_structure())
147+
print()
148+
149+
# Test 3: Test achat call
150+
results.append(await test_achat_call())
151+
print()
152+
153+
# Summary
154+
passed = sum(results)
155+
total = len(results)
156+
157+
print(f"📊 Test Results: {passed}/{total} tests passed")
158+
159+
if passed == total:
160+
print("🎉 All tests passed! The task_name fix appears to be working.")
161+
return 0
162+
else:
163+
print("❌ Some tests failed. The fix may need more work.")
164+
return 1
165+
166+
if __name__ == "__main__":
167+
exit_code = asyncio.run(main())
168+
sys.exit(exit_code)

0 commit comments

Comments
 (0)