Compare commits

...
15 Commits
Author SHA1 Message Date
Mervin Praison 000bdf3418 refactor: move test files to src/praisonai-agents and bump version to 2.2.57 2025-07-14 18:40:18 +01:00
Mervin Praison 812abec2eb Merge pull request #897 from MervinPraison/claude/issue-896-20250714_104106
feat: enhance callbacks with agent and task context (fixes #896)
2025-07-14 18:27:25 +01:00
github-actions[bot]andMervin Praison e58f6deb4b fix: resolve critical runtime issues in callback enhancement
- Fix undefined variables in llm.py response() and aresponse() methods
  by adding missing parameters to method signatures
- Fix backward compatibility by adding signature checking to callbacks
  in display_interaction(), execute_callback(), and execute_sync_callback()
- Ensure consistent parameter propagation in agent.py get_response calls
- Legacy callbacks now work without TypeError, new callbacks get full context

Co-authored-by: Mervin Praison <[email protected]>
2025-07-14 17:21:11 +00:00
Mervin Praison f23680c121 Merge pull request #903 from MervinPraison/claude/issue-899-20250714_122107
fix: exclude response_format parameter for Gemini image models
2025-07-14 18:01:38 +01:00
Mervin Praison e569400260 Merge pull request #902 from MervinPraison/claude/issue-901-20250714_140528
Issue #901: Changes from Claude
2025-07-14 17:57:58 +01:00
Mervin Praison 6249d14d3f Merge pull request #895 from MervinPraison/claude/issue-893-20250714_084251
fix: resolve Ollama sequential tool calling redundant execution issue
2025-07-14 17:55:05 +01:00
github-actions[bot]andMervin Praison fb4ecf1893 fix: Address reviewer feedback on self-reflection test files
- Move test files from src/praisonai-agents/ to tests/ directory per repository guidelines
- Replace print statements and return values with proper assert statements
- Fix f-string issues without placeholders
- Add comprehensive exception handling for test failures
- Create improved test structure following repository patterns

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>
2025-07-14 14:55:27 +00:00
github-actions[bot]andMervin Praison 5a4cbf8aeb fix: improve robustness of Gemini model detection in ImageAgent
- Use self.llm_model property instead of self.llm for more robust model name resolution
- Handles all LLM configuration types (strings, dicts, custom instances, base_url configs)
- Fixes potential AttributeError when self.llm is not set
- Maintains backward compatibility for all existing configurations

Co-authored-by: Mervin Praison <[email protected]>
2025-07-14 14:54:16 +00:00
github-actions[bot]andMervin Praison 885a73f697 fix: add intelligent final answer detection to async method
Apply the same intelligent final answer detection logic that was added to the sync method (get_response) to the async method (get_response_async). This ensures consistent behavior between sync and async methods when detecting substantive final answers alongside tool calls.

The fix prevents redundant tool execution loops in async contexts by checking if the LLM provides substantive content (>10 characters) after tool execution, breaking the loop when appropriate.

Co-authored-by: Mervin Praison <[email protected]>
2025-07-14 14:48:03 +00:00
Mervin Praison 6d55cfa87e Merge branch 'main' into claude/issue-896-20250714_104106 2025-07-14 15:46:44 +01:00
github-actions[bot]andMervin Praison e858cebed9 fix: Enable self-reflection with tools in LLM class
- Fixed issue where self-reflection was not working when tools were present
- Added tools=formatted_tools parameter to completion calls during reflection regeneration
- Self-reflection now maintains tool access throughout the reflection process
- Backward compatibility maintained, no existing features removed
- Resolves issue #901

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-authored-by: Mervin Praison <[email protected]>
2025-07-14 14:17:28 +00:00
github-actions[bot]andMervin Praison 4ffbc1b52a fix: exclude response_format parameter for Gemini image models
- Add detection logic to identify Gemini models (case-insensitive)
- Exclude response_format parameter for Gemini models to prevent litellm.UnsupportedParamsError
- Maintain backward compatibility for non-Gemini models
- Fixes #899

Co-authored-by: Mervin Praison <[email protected]>
2025-07-14 12:24:35 +00:00
2ef506fa8a feat: enhance callbacks with agent and task context (fixes #896)
- Add agent_name, agent_role, agent_tools, task_name, task_description, task_id to callbacks
- Update display_interaction() and adisplay_interaction() to accept new parameters
- Enhance LLM.get_response() to accept and pass task context
- Modify Agent.chat() to accept task parameters
- Update task execution in agents.py to pass task information
- Maintain backward compatibility with existing callbacks
- Add test scripts to verify functionality

Co-authored-by: Mervin Praison <[email protected]>

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-07-14 10:53:46 +00:00
Mervin Praison 7ce783e59b Merge branch 'main' into claude/issue-893-20250714_084251 2025-07-14 11:11:05 +01:00
github-actions[bot]andClaude 35250e429e fix: clean up redundant conditionals and improve tool execution logic in llm.py
- Remove redundant conditional expressions that were causing unnecessary repetition
- Add intelligent final answer detection after tool execution
- Prevent unnecessary tool execution loops when LLM provides substantive response

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
2025-07-14 08:51:02 +00:00
32 changed files with 1049 additions and 74 deletions

No files matched your search

+15 -3
View File
@@ -75,17 +75,29 @@ jobs:
issue_number: issueNumber
});
const existingComment = comments.data.find(comment =>
// Look for Claude's completed review comments
const claudeReviewComments = comments.data.filter(comment =>
(comment.user.login === 'claude[bot]' || comment.body.includes('✅') || comment.body.includes('## Summary')) &&
!comment.body.includes('⏳') &&
!comment.body.includes('🔄') &&
!comment.body.includes('Loading...')
);
// Check if our review request comment already exists
const existingReviewRequest = comments.data.find(comment =>
comment.body.includes(reviewText)
);
const shouldPost = !existingComment;
// Post if Claude has completed a review AND we haven't posted the review request yet
const shouldPost = claudeReviewComments.length > 0 && !existingReviewRequest;
core.setOutput('should-post', shouldPost);
console.log(`Claude review completed: ${claudeReviewComments.length > 0}`);
console.log(`Review request exists: ${!!existingReviewRequest}`);
console.log(`Should post comment: ${shouldPost}`);
} catch (error) {
console.log('Error checking comments:', error);
core.setOutput('should-post', true); // If we can't check, proceed with posting
core.setOutput('should-post', false); // If we can't check, don't post
}
- name: Wait 30 seconds before posting comment
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
flask \
"praisonai>=2.2.56" \
"praisonai>=2.2.57" \
"praisonai[api]" \
gunicorn \
markdown
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.56" \
"praisonai>=2.2.57" \
"praisonai[chat]" \
"embedchain[github,youtube]"
+1 -1
View File
@@ -20,7 +20,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.56" \
"praisonai>=2.2.57" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
+1 -1
View File
@@ -16,7 +16,7 @@ RUN mkdir -p /root/.praison
# Install Python packages (using latest versions)
RUN pip install --no-cache-dir \
praisonai_tools \
"praisonai>=2.2.56" \
"praisonai>=2.2.57" \
"praisonai[ui]" \
"praisonai[crewai]"
+2 -2
View File
@@ -121,7 +121,7 @@ healthcheck:
## 📦 Package Versions
All Docker images use consistent, up-to-date versions:
- PraisonAI: `>=2.2.56`
- PraisonAI: `>=2.2.57`
- PraisonAI Agents: `>=0.0.92`
- Python: `3.11-slim`
@@ -218,7 +218,7 @@ docker-compose up -d
### Version Pinning
To use specific versions, update the Dockerfile:
```dockerfile
RUN pip install "praisonai==2.2.56" "praisonaiagents==0.0.92"
RUN pip install "praisonai==2.2.57" "praisonaiagents==0.0.92"
```
## 🌐 Production Deployment
@@ -1100,6 +1100,10 @@ Your Goal: {self.goal}"""
execute_tool_fn=self.execute_tool,
agent_name=self.name,
agent_role=self.role,
agent_tools=[t.__name__ for t in self.tools] if self.tools else None,
task_name=task_name,
task_description=task_description,
task_id=task_id,
reasoning_steps=reasoning_steps
)
else:
@@ -1116,6 +1120,10 @@ Your Goal: {self.goal}"""
execute_tool_fn=self.execute_tool,
agent_name=self.name,
agent_role=self.role,
agent_tools=[t.__name__ for t in self.tools] if self.tools else None,
task_name=task_name,
task_description=task_description,
task_id=task_id,
reasoning_steps=reasoning_steps
)
else:
@@ -1162,14 +1170,26 @@ Your Goal: {self.goal}"""
message=prompt,
response=response,
markdown=self.markdown,
generation_time=generation_time
generation_time=generation_time,
agent_name=self.name,
agent_role=self.role,
agent_tools=[t.__name__ for t in self.tools] if self.tools else None,
task_name=None, # Not available in this context
task_description=None, # Not available in this context
task_id=None # Not available in this context
)
# Only display interaction if not using custom LLM (to avoid double output) and verbose is True
if self.verbose and not self._using_custom_llm:
display_interaction(prompt, response, markdown=self.markdown,
generation_time=generation_time, console=self.console)
generation_time=generation_time, console=self.console,
agent_name=self.name,
agent_role=self.role,
agent_tools=[t.__name__ for t in self.tools] if self.tools else None,
task_name=None, # Not available in this context
task_description=None, # Not available in this context
task_id=None) # Not available in this context
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False, stream=True):
def chat(self, prompt, temperature=0.2, tools=None, output_json=None, output_pydantic=None, reasoning_steps=False, stream=True, task_name=None, task_description=None, task_id=None):
# Log all parameter values when in debug mode
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
param_info = {
@@ -1260,6 +1280,9 @@ Your Goal: {self.goal}"""
agent_name=self.name,
agent_role=self.role,
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in (tools if tools is not None else self.tools)],
task_name=task_name,
task_description=task_description,
task_id=task_id,
execute_tool_fn=self.execute_tool, # Pass tool execution function
reasoning_steps=reasoning_steps,
stream=stream # Pass the stream parameter from chat method
@@ -1582,6 +1605,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
agent_name=self.name,
agent_role=self.role,
agent_tools=[t.__name__ if hasattr(t, '__name__') else str(t) for t in (tools if tools is not None else self.tools)],
task_name=task_name,
task_description=task_description,
task_id=task_id,
execute_tool_fn=self.execute_tool_async,
reasoning_steps=reasoning_steps
)
@@ -144,8 +144,17 @@ class ImageAgent(Agent):
config = self.image_config.dict(exclude_none=True)
config.update(kwargs)
# Use llm parameter as the model
config['model'] = self.llm
# Get the model name robustly from the parent Agent's property
model_info = self.llm_model
model_name = model_info.model if hasattr(model_info, 'model') else str(model_info)
# Use the model name in config
config['model'] = model_name
# Check if we're using a Gemini model and remove unsupported parameters
if 'gemini' in model_name.lower():
# Gemini models don't support response_format parameter
config.pop('response_format', None)
with Progress(
SpinnerColumn(),
@@ -154,7 +163,7 @@ class ImageAgent(Agent):
) as progress:
try:
# Add a task for image generation
task = progress.add_task(f"[cyan]Generating image with {self.llm}...", total=None)
task = progress.add_task(f"[cyan]Generating image with {model_name}...", total=None)
# Use litellm's image generation
response = self.litellm(
@@ -689,7 +689,10 @@ Context:
_get_multimodal_message(task_prompt, task.images),
tools=task.tools,
output_json=task.output_json,
output_pydantic=task.output_pydantic
output_pydantic=task.output_pydantic,
task_name=task.name,
task_description=task.description,
task_id=task_id
)
else:
agent_output = executor_agent.chat(
@@ -698,6 +701,9 @@ Context:
output_json=task.output_json,
output_pydantic=task.output_pydantic,
stream=self.stream,
task_name=task.name,
task_description=task.description,
task_id=task_id
)
if agent_output:
+182 -37
View File
@@ -620,6 +620,9 @@ class LLM:
agent_name: Optional[str] = None,
agent_role: Optional[str] = None,
agent_tools: Optional[List[str]] = None,
task_name: Optional[str] = None,
task_description: Optional[str] = None,
task_id: Optional[str] = None,
execute_tool_fn: Optional[Callable] = None,
stream: bool = True,
**kwargs
@@ -760,8 +763,14 @@ class LLM:
original_prompt,
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
markdown=markdown,
generation_time=generation_time_val,
console=console
generation_time=time.time() - current_time,
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
interaction_displayed = True
elif verbose and not interaction_displayed:
@@ -769,8 +778,14 @@ class LLM:
original_prompt,
response_text,
markdown=markdown,
generation_time=generation_time_val,
console=console
generation_time=time.time() - current_time,
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
interaction_displayed = True
@@ -883,7 +898,13 @@ class LLM:
response_text,
markdown=markdown,
generation_time=time.time() - current_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
interaction_displayed = True
@@ -963,16 +984,14 @@ class LLM:
iteration_count += 1
continue
# For Ollama, add explicit prompt if we need a final answer
if self._is_ollama_provider() and iteration_count > 0:
# Add an explicit prompt for Ollama to generate the final answer
messages.append({
"role": "user",
"content": self.OLLAMA_FINAL_ANSWER_PROMPT
})
# Check if the LLM provided a final answer alongside the tool calls
# If response_text contains substantive content, treat it as the final answer
if response_text and response_text.strip() and len(response_text.strip()) > 10:
# LLM provided a final answer after tool execution, don't continue
final_response_text = response_text.strip()
break
# After tool execution, continue the loop to check if more tools are needed
# instead of immediately trying to get a final response
# Otherwise, continue the loop to check if more tools are needed
iteration_count += 1
continue
else:
@@ -1010,7 +1029,13 @@ class LLM:
f"Reasoning:\n{stored_reasoning_content}\n\nAnswer:\n{response_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
else:
display_interaction(
@@ -1018,7 +1043,13 @@ class LLM:
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
interaction_displayed = True
@@ -1043,7 +1074,9 @@ class LLM:
)
if verbose and not interaction_displayed:
display_interaction(original_prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
generation_time=time.time() - start_time, console=console,
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
task_name=task_name, task_description=task_description, task_id=task_id)
interaction_displayed = True
return response_text
@@ -1060,7 +1093,9 @@ class LLM:
if verbose and not interaction_displayed:
display_interaction(original_prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
generation_time=time.time() - start_time, console=console,
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
task_name=task_name, task_description=task_description, task_id=task_id)
interaction_displayed = True
# Return reasoning content if reasoning_steps is True
if reasoning_steps and stored_reasoning_content:
@@ -1106,7 +1141,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
f"{reasoning_content}\n\nReflection result:\n{reflection_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
elif verbose:
display_interaction(
@@ -1114,7 +1155,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
reflection_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
else:
# Existing streaming approach
@@ -1165,14 +1212,18 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
if satisfactory and reflection_count >= min_reflect - 1:
if verbose and not interaction_displayed:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
generation_time=time.time() - start_time, console=console,
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
task_name=task_name, task_description=task_description, task_id=task_id)
interaction_displayed = True
return response_text
if reflection_count >= max_reflect - 1:
if verbose and not interaction_displayed:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
generation_time=time.time() - start_time, console=console,
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
task_name=task_name, task_description=task_description, task_id=task_id)
interaction_displayed = True
return response_text
@@ -1193,6 +1244,7 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
messages=messages,
temperature=temperature,
stream=True,
tools=formatted_tools,
output_json=output_json,
output_pydantic=output_pydantic,
**kwargs
@@ -1209,6 +1261,7 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
messages=messages,
temperature=temperature,
stream=True,
tools=formatted_tools,
output_json=output_json,
output_pydantic=output_pydantic,
**kwargs
@@ -1225,7 +1278,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
if reflection_count >= max_reflect:
if verbose and not interaction_displayed:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
generation_time=time.time() - start_time, console=console,
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
task_name=task_name, task_description=task_description, task_id=task_id)
interaction_displayed = True
return response_text
continue
@@ -1273,6 +1328,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
agent_name: Optional[str] = None,
agent_role: Optional[str] = None,
agent_tools: Optional[List[str]] = None,
task_name: Optional[str] = None,
task_description: Optional[str] = None,
task_id: Optional[str] = None,
execute_tool_fn: Optional[Callable] = None,
stream: bool = True,
**kwargs
@@ -1380,7 +1438,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
interaction_displayed = True
elif verbose and not interaction_displayed:
@@ -1389,7 +1453,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
interaction_displayed = True
else:
@@ -1473,7 +1543,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
interaction_displayed = True
@@ -1567,7 +1643,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
f"Reasoning:\n{reasoning_content}\n\nAnswer:\n{response_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
interaction_displayed = True
elif verbose and not interaction_displayed:
@@ -1576,7 +1658,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
interaction_displayed = True
else:
@@ -1626,6 +1714,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
if reasoning_steps and reasoning_content:
stored_reasoning_content = reasoning_content
# Check if the LLM provided a final answer alongside the tool calls
# If response_text contains substantive content, treat it as the final answer
if response_text and response_text.strip() and len(response_text.strip()) > 10:
# LLM provided a final answer after tool execution, don't continue
final_response_text = response_text.strip()
break
# Continue the loop to check if more tools are needed
iteration_count += 1
continue
@@ -1642,7 +1737,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
self.chat_history.append({"role": "assistant", "content": response_text})
if verbose and not interaction_displayed:
display_interaction(original_prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
generation_time=time.time() - start_time, console=console,
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
task_name=task_name, task_description=task_description, task_id=task_id)
interaction_displayed = True
return response_text
@@ -1658,11 +1755,19 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
f"Reasoning:\n{stored_reasoning_content}\n\nAnswer:\n{display_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
else:
display_interaction(original_prompt, display_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
generation_time=time.time() - start_time, console=console,
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
task_name=task_name, task_description=task_description, task_id=task_id)
interaction_displayed = True
# Return reasoning content if reasoning_steps is True and we have it
@@ -1707,7 +1812,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
f"{reasoning_content}\n\nReflection result:\n{reflection_text}",
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
elif verbose:
display_interaction(
@@ -1715,7 +1826,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
reflection_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console
console=console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
else:
# Existing streaming approach
@@ -1767,14 +1884,18 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
if satisfactory and reflection_count >= min_reflect - 1:
if verbose and not interaction_displayed:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
generation_time=time.time() - start_time, console=console,
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
task_name=task_name, task_description=task_description, task_id=task_id)
interaction_displayed = True
return response_text
if reflection_count >= max_reflect - 1:
if verbose and not interaction_displayed:
display_interaction(prompt, response_text, markdown=markdown,
generation_time=time.time() - start_time, console=console)
generation_time=time.time() - start_time, console=console,
agent_name=agent_name, agent_role=agent_role, agent_tools=agent_tools,
task_name=task_name, task_description=task_description, task_id=task_id)
interaction_displayed = True
return response_text
@@ -2039,6 +2160,12 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
verbose: bool = True,
markdown: bool = True,
console: Optional[Console] = None,
agent_name: Optional[str] = None,
agent_role: Optional[str] = None,
agent_tools: Optional[List[str]] = None,
task_name: Optional[str] = None,
task_description: Optional[str] = None,
task_id: Optional[str] = None,
**kwargs
) -> str:
"""Simple function to get model response without tool calls or complex features"""
@@ -2107,7 +2234,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console or self.console
console=console or self.console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
return response_text.strip() if response_text else ""
@@ -2126,6 +2259,12 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
verbose: bool = True,
markdown: bool = True,
console: Optional[Console] = None,
agent_name: Optional[str] = None,
agent_role: Optional[str] = None,
agent_tools: Optional[List[str]] = None,
task_name: Optional[str] = None,
task_description: Optional[str] = None,
task_id: Optional[str] = None,
**kwargs
) -> str:
"""Async version of response function"""
@@ -2195,7 +2334,13 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
response_text,
markdown=markdown,
generation_time=time.time() - start_time,
console=console or self.console
console=console or self.console,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
return response_text.strip() if response_text else ""
+74 -6
View File
@@ -73,7 +73,18 @@ def execute_sync_callback(display_type: str, **kwargs):
"""
if display_type in sync_display_callbacks:
callback = sync_display_callbacks[display_type]
callback(**kwargs)
import inspect
sig = inspect.signature(callback)
# Filter kwargs to what the callback accepts to maintain backward compatibility
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
# Callback accepts **kwargs, so pass all arguments
supported_kwargs = kwargs
else:
# Only pass arguments that the callback signature supports
supported_kwargs = {k: v for k, v in kwargs.items() if k in sig.parameters}
callback(**supported_kwargs)
async def execute_callback(display_type: str, **kwargs):
"""Execute both sync and async callbacks for a given display type.
@@ -82,16 +93,38 @@ async def execute_callback(display_type: str, **kwargs):
display_type (str): Type of display event
**kwargs: Arguments to pass to the callback functions
"""
import inspect
# Execute synchronous callback if registered
if display_type in sync_display_callbacks:
callback = sync_display_callbacks[display_type]
sig = inspect.signature(callback)
# Filter kwargs to what the callback accepts to maintain backward compatibility
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
# Callback accepts **kwargs, so pass all arguments
supported_kwargs = kwargs
else:
# Only pass arguments that the callback signature supports
supported_kwargs = {k: v for k, v in kwargs.items() if k in sig.parameters}
loop = asyncio.get_event_loop()
await loop.run_in_executor(None, lambda: callback(**kwargs))
await loop.run_in_executor(None, lambda: callback(**supported_kwargs))
# Execute asynchronous callback if registered
if display_type in async_display_callbacks:
callback = async_display_callbacks[display_type]
await callback(**kwargs)
sig = inspect.signature(callback)
# Filter kwargs to what the callback accepts to maintain backward compatibility
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
# Callback accepts **kwargs, so pass all arguments
supported_kwargs = kwargs
else:
# Only pass arguments that the callback signature supports
supported_kwargs = {k: v for k, v in kwargs.items() if k in sig.parameters}
await callback(**supported_kwargs)
def _clean_display_content(content: str, max_length: int = 20000) -> str:
"""Helper function to clean and truncate content for display."""
@@ -114,7 +147,7 @@ def _clean_display_content(content: str, max_length: int = 20000) -> str:
return content.strip()
def display_interaction(message, response, markdown=True, generation_time=None, console=None):
def display_interaction(message, response, markdown=True, generation_time=None, console=None, agent_name=None, agent_role=None, agent_tools=None, task_name=None, task_description=None, task_id=None):
"""Synchronous version of display_interaction."""
if console is None:
console = Console()
@@ -126,6 +159,35 @@ def display_interaction(message, response, markdown=True, generation_time=None,
message = _clean_display_content(str(message))
response = _clean_display_content(str(response))
# Execute synchronous callback if registered
if 'interaction' in sync_display_callbacks:
callback = sync_display_callbacks['interaction']
import inspect
sig = inspect.signature(callback)
all_kwargs = {
'message': message,
'response': response,
'markdown': markdown,
'generation_time': generation_time,
'agent_name': agent_name,
'agent_role': agent_role,
'agent_tools': agent_tools,
'task_name': task_name,
'task_description': task_description,
'task_id': task_id
}
# Filter kwargs to what the callback accepts to maintain backward compatibility
if any(p.kind == inspect.Parameter.VAR_KEYWORD for p in sig.parameters.values()):
# Callback accepts **kwargs, so pass all arguments
supported_kwargs = all_kwargs
else:
# Only pass arguments that the callback signature supports
supported_kwargs = {k: v for k, v in all_kwargs.items() if k in sig.parameters}
callback(**supported_kwargs)
# Rest of the display logic...
if generation_time:
console.print(Text(f"Response generated in {generation_time:.1f}s", style="dim"))
@@ -204,7 +266,7 @@ def display_generating(content: str = "", start_time: Optional[float] = None):
return Panel(Markdown(content), title=f"Generating...{elapsed_str}", border_style="green")
# Async versions with 'a' prefix
async def adisplay_interaction(message, response, markdown=True, generation_time=None, console=None):
async def adisplay_interaction(message, response, markdown=True, generation_time=None, console=None, agent_name=None, agent_role=None, agent_tools=None, task_name=None, task_description=None, task_id=None):
"""Async version of display_interaction."""
if console is None:
console = Console()
@@ -222,7 +284,13 @@ async def adisplay_interaction(message, response, markdown=True, generation_time
message=message,
response=response,
markdown=markdown,
generation_time=generation_time
generation_time=generation_time,
agent_name=agent_name,
agent_role=agent_role,
agent_tools=agent_tools,
task_name=task_name,
task_description=task_description,
task_id=task_id
)
# Rest of the display logic...
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.130"
version = "0.0.131"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
requires-python = ">=3.10"
authors = [
@@ -0,0 +1,82 @@
#!/usr/bin/env python3
"""Test script for callback enhancement - Issue #896"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
import asyncio
from praisonaiagents import (
register_display_callback,
Agent,
Task,
PraisonAIAgents
)
def enhanced_callback(message=None, response=None, agent_name=None, agent_role=None, agent_tools=None, task_name=None, task_description=None, task_id=None, **kwargs):
"""Enhanced callback that demonstrates the new context information"""
print("="*80)
print("ENHANCED CALLBACK TRIGGERED")
print("="*80)
print(f"Message: {message}")
print(f"Response: {response}")
print(f"Agent Name: {agent_name}")
print(f"Agent Role: {agent_role}")
print(f"Agent Tools: {agent_tools}")
print(f"Task Name: {task_name}")
print(f"Task Description: {task_description}")
print(f"Task ID: {task_id}")
print(f"Other kwargs: {kwargs}")
print("="*80)
print()
# Register the enhanced callback
register_display_callback('interaction', enhanced_callback, is_async=False)
def test_callback_enhancement():
"""Test function to verify the callback enhancement works"""
# Create an agent with detailed information
agent = Agent(
name="TestAgent",
role="Assistant",
goal="Help with testing callbacks",
backstory="I am a helpful assistant for testing purposes",
llm="openai/gpt-4o-mini",
verbose=True
)
# Create a task with detailed information
task = Task(
name="callback_test_task",
description="Test the enhanced callback functionality",
agent=agent,
expected_output="A simple response to test callbacks"
)
# Run the agents
try:
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
verbose=True
)
result = agents.start()
print(f"Test completed successfully! Result: {result}")
return True
except Exception as e:
print(f"Test failed with error: {e}")
return False
if __name__ == "__main__":
print("Testing callback enhancement (Issue #896)...")
print("This test will demonstrate the new agent_name and task context in callbacks")
print()
# Run the test
success = test_callback_enhancement()
if success:
print("✅ Test passed! Callback enhancement is working correctly.")
else:
print("❌ Test failed! Check the error output above.")
@@ -34,7 +34,7 @@ agent = Agent(
role="Assistant",
goal="Help with tasks",
backstory="I am a helpful assistant",
llm="openai/gpt-4o-mini", # Using a simple model for testing
llm="gpt-4o-mini", # Using a simple model for testing
verbose=False
)
@@ -0,0 +1,44 @@
#!/usr/bin/env python3
"""Simple test for callback enhancement - Issue #896"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
# Test just the callback registration and display functions
from praisonaiagents import register_display_callback, display_interaction
def test_callback(message=None, response=None, agent_name=None, agent_role=None, agent_tools=None, task_name=None, task_description=None, task_id=None, **kwargs):
"""Test callback function"""
print("Callback triggered with:")
print(f" Message: {message}")
print(f" Response: {response}")
print(f" Agent Name: {agent_name}")
print(f" Agent Role: {agent_role}")
print(f" Agent Tools: {agent_tools}")
print(f" Task Name: {task_name}")
print(f" Task Description: {task_description}")
print(f" Task ID: {task_id}")
print(f" Other kwargs: {kwargs}")
print()
# Register the callback
register_display_callback('interaction', test_callback, is_async=False)
# Test the display_interaction function with new parameters
print("Testing display_interaction with enhanced parameters...")
display_interaction(
message="Test message",
response="Test response",
agent_name="TestAgent",
agent_role="Assistant",
agent_tools=["tool1", "tool2"],
task_name="test_task",
task_description="Test task description",
task_id="task_123",
markdown=True,
generation_time=1.5
)
print("✅ Simple callback test completed!")
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Simple test to verify the self-reflection fix works"""
from praisonaiagents import Agent
from praisonaiagents.tools import calculator
def test_self_reflection_fix():
"""Test that self-reflection works with tools after the fix"""
print("=== Testing Self-Reflection Fix ===")
# Create an agent with self-reflection and a simple tool
agent = Agent(
role="Math Assistant",
goal="Solve math problems accurately",
backstory="You are a helpful math assistant",
self_reflect=True,
llm="gpt-4o-mini", # Use a more widely available model
verbose=True,
tools=[calculator],
min_reflect=1,
max_reflect=2
)
# Test with a simple calculation that might trigger self-reflection
response = agent.start("What is 25 * 17? Show your work and double-check the answer.")
print(f"\nResponse: {response}")
if response:
print("\n✅ SUCCESS: Self-reflection with tools is working!")
return True
else:
print("\n❌ FAILED: Self-reflection with tools is not working")
return False
if __name__ == "__main__":
test_self_reflection_fix()
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""Test to verify the self-reflection fix works with tools"""
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import calculator
def test_self_reflection_fix_verification():
"""Test that self-reflection now works with tools"""
print("=== Testing Self-Reflection Fix Verification ===")
# Create an agent with self-reflection enabled and tools
agent = Agent(
name="MathAgent",
role="Math Assistant",
goal="Solve mathematical problems accurately",
backstory="You are a helpful math assistant who double-checks calculations",
tools=[calculator],
self_reflect=True, # This should now work with tools
min_reflect=1,
max_reflect=2,
verbose=True
)
# Define a task that would benefit from self-reflection
task = Task(
description="Calculate 123 * 456 and verify the result is correct",
expected_output="The calculation result with verification",
agent=agent,
name="math_calculation"
)
# Create and run the agents
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="sequential"
)
try:
result = agents.start()
if result:
print(f"\n✅ SUCCESS: Self-reflection with tools is working!")
print(f"Result: {result}")
return True
else:
print(f"\n❌ FAILED: No result returned")
return False
except Exception as e:
print(f"\n❌ ERROR: {str(e)}")
return False
if __name__ == "__main__":
success = test_self_reflection_fix_verification()
if success:
print("\n🎉 Fix verification successful! Self-reflection now works with tools.")
else:
print("\n💥 Fix verification failed. Self-reflection with tools is still not working.")
File renamed without changes.
File renamed without changes.
+121
View File
@@ -0,0 +1,121 @@
#!/usr/bin/env python3
"""
Demonstration of Issue #896 Enhancement: Callback Needs More Contextual Information
This script demonstrates how the enhanced callback system now provides agent_name,
agent_role, agent_tools, task_name, task_description, and task_id to callbacks.
Usage:
python test_issue_896_demo.py
Author: Generated by Claude Code
"""
import sys
import os
sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
from praisonaiagents import register_display_callback, display_interaction
def enhanced_callback(message=None, response=None, agent_name=None, agent_role=None,
agent_tools=None, task_name=None, task_description=None,
task_id=None, **kwargs):
"""
Enhanced callback function that demonstrates the new contextual information.
This callback now receives:
- agent_name: The name of the agent
- agent_role: The role of the agent
- agent_tools: List of tools available to the agent
- task_name: The name of the task being executed
- task_description: The description of the task
- task_id: The unique identifier of the task
"""
print("="*80)
print("🔔 ENHANCED CALLBACK TRIGGERED")
print("="*80)
print(f"📝 Message: {message}")
print(f"💬 Response: {response}")
print(f"🤖 Agent Name: {agent_name}")
print(f"👤 Agent Role: {agent_role}")
print(f"🛠️ Agent Tools: {agent_tools}")
print(f"📋 Task Name: {task_name}")
print(f"📄 Task Description: {task_description}")
print(f"🔢 Task ID: {task_id}")
print(f"⚙️ Other kwargs: {kwargs}")
print("="*80)
print()
def legacy_callback(message=None, response=None, **kwargs):
"""
Legacy callback function that demonstrates backward compatibility.
This callback works exactly as before, proving that existing callbacks
continue to function without modification.
"""
print("🔄 Legacy callback still works!")
print(f"Message: {message}")
print(f"Response: {response}")
print(f"Other kwargs: {kwargs}")
print()
def demo_enhanced_callbacks():
"""Demonstrate the enhanced callback functionality"""
print("🚀 Demonstrating Enhanced Callbacks for Issue #896")
print("="*60)
print()
# Register both enhanced and legacy callbacks
register_display_callback('interaction', enhanced_callback, is_async=False)
print("1. Testing enhanced callback with full context:")
print("-" * 50)
# Test with full context
display_interaction(
message="You need to do the following task: Say the number 1.\nExpected Output: 1.\nPlease provide only the final result of your work.",
response="1",
agent_name="MyAgent",
agent_role="Assistant",
agent_tools=["calculator", "text_processor"],
task_name="simple_task",
task_description="Say the number 1",
task_id="task_001",
markdown=True,
generation_time=1.3638858795166016
)
print("2. Testing backward compatibility (missing context):")
print("-" * 50)
# Test with minimal context (backward compatibility)
display_interaction(
message="Test message without context",
response="Test response",
markdown=True,
generation_time=0.5
)
print("3. Testing with partial context:")
print("-" * 50)
# Test with partial context
display_interaction(
message="Partial context test",
response="Response with partial context",
agent_name="PartialAgent",
task_name="partial_task",
markdown=True,
generation_time=0.8
)
print("✅ All tests completed successfully!")
print()
print("🎉 The enhanced callback system is working correctly!")
print(" - Callbacks now receive agent and task context")
print(" - Backward compatibility is maintained")
print(" - All parameters are optional with sensible defaults")
if __name__ == "__main__":
demo_enhanced_callbacks()
+35
View File
@@ -0,0 +1,35 @@
#!/usr/bin/env python3
"""Test LLM class directly to verify self-reflection fix"""
from praisonaiagents.llm import LLM
from praisonaiagents.tools import calculator
def test_llm_direct():
"""Test LLM class directly with self-reflection and tools"""
print("=== Testing LLM Direct with Self-Reflection and Tools ===")
# Create LLM instance
llm = LLM(model="gpt-4o-mini")
# Test with self-reflection and tools
response = llm.get_response(
prompt="Calculate 15 * 23 and verify your answer",
system_prompt="You are a helpful math assistant. Use tools when needed.",
tools=[calculator],
self_reflect=True,
min_reflect=1,
max_reflect=2,
verbose=True
)
print(f"\nResponse: {response}")
if response:
print("\n✅ SUCCESS: LLM self-reflection with tools is working!")
return True
else:
print("\n❌ FAILED: LLM self-reflection with tools is not working")
return False
if __name__ == "__main__":
test_llm_direct()
@@ -0,0 +1,90 @@
#!/usr/bin/env python3
"""Test script to verify self-reflection works with tools after the fix"""
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import duckduckgo_search
def test_self_reflection_with_tools():
"""Test self-reflection with tools - should work after the fix"""
print("=== Testing Self-Reflection WITH Tools ===")
# Create an agent with self-reflection and tools
agent = Agent(
role="Senior Research Analyst",
goal="Analyze and provide insights on given topics",
backstory="You are an expert analyst with strong critical thinking skills",
self_reflect=True,
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=True,
tools=[duckduckgo_search]
)
# Create a task
task = Task(
description="Search for recent developments in AI and provide a brief analysis",
expected_output="A detailed analysis report",
agent=agent
)
# Create and start the agents
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="sequential"
)
# Start execution
result = agents.start()
print(f"Result with tools: {result}")
return result
def test_self_reflection_without_tools():
"""Test self-reflection without tools - should work (baseline)"""
print("\n=== Testing Self-Reflection WITHOUT Tools ===")
# Create an agent with self-reflection but no tools
agent = Agent(
role="Senior Research Analyst",
goal="Analyze and provide insights on given topics",
backstory="You are an expert analyst with strong critical thinking skills",
self_reflect=True,
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=True
)
# Create a task
task = Task(
description="Analyze recent developments in AI",
expected_output="A detailed analysis report",
agent=agent
)
# Create and start the agents
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="sequential"
)
# Start execution
result = agents.start()
print(f"Result without tools: {result}")
return result
if __name__ == "__main__":
print("Testing self-reflection fix...")
# Test without tools (should work)
result_without_tools = test_self_reflection_without_tools()
# Test with tools (should work after fix)
result_with_tools = test_self_reflection_with_tools()
print("\n=== Test Summary ===")
print(f"Without tools: {'SUCCESS' if result_without_tools else 'FAILED'}")
print(f"With tools: {'SUCCESS' if result_with_tools else 'FAILED'}")
if result_with_tools:
print("\n✅ Fix verified: Self-reflection now works with tools!")
else:
print("\n❌ Fix failed: Self-reflection still not working with tools")
@@ -0,0 +1,36 @@
#!/usr/bin/env python3
"""Test LLM class directly to verify self-reflection fix"""
from praisonaiagents.llm import LLM
from praisonaiagents.tools import calculator
def test_llm_direct():
"""Test LLM class directly with self-reflection and tools"""
print("=== Testing LLM Direct with Self-Reflection and Tools ===")
# Create LLM instance
llm = LLM(model="gpt-4o-mini")
# Test with self-reflection and tools
try:
response = llm.get_response(
prompt="Calculate 15 * 23 and verify your answer",
system_prompt="You are a helpful math assistant. Use tools when needed.",
tools=[calculator],
self_reflect=True,
min_reflect=1,
max_reflect=2,
verbose=True
)
print(f"\nResponse: {response}")
assert response, "LLM self-reflection with tools failed to produce a response."
print("\n✅ SUCCESS: LLM self-reflection with tools is working!")
except Exception as e:
print(f"\n❌ ERROR: {str(e)}")
raise AssertionError(f"Test failed with error: {str(e)}")
if __name__ == "__main__":
test_llm_direct()
@@ -0,0 +1,117 @@
#!/usr/bin/env python3
"""Comprehensive test script to verify self-reflection works with tools after the fix"""
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import duckduckgo_search
def test_self_reflection_with_tools():
"""Test self-reflection with tools - should work after the fix"""
print("=== Testing Self-Reflection WITH Tools ===")
# Create an agent with self-reflection and tools
agent = Agent(
role="Senior Research Analyst",
goal="Analyze and provide insights on given topics",
backstory="You are an expert analyst with strong critical thinking skills",
self_reflect=True,
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=True,
tools=[duckduckgo_search]
)
# Create a task
task = Task(
description="Search for recent developments in AI and provide a brief analysis",
expected_output="A detailed analysis report",
agent=agent
)
# Create and start the agents
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="sequential"
)
try:
# Start execution
result = agents.start()
print(f"Result with tools: {result}")
assert result, "Self-reflection with tools failed to produce a result."
print("\n✅ SUCCESS: Self-reflection with tools is working!")
return result
except Exception as e:
print(f"\n❌ ERROR: {str(e)}")
raise AssertionError(f"Test with tools failed: {str(e)}")
def test_self_reflection_without_tools():
"""Test self-reflection without tools - should work (baseline)"""
print("\n=== Testing Self-Reflection WITHOUT Tools ===")
# Create an agent with self-reflection but no tools
agent = Agent(
role="Senior Research Analyst",
goal="Analyze and provide insights on given topics",
backstory="You are an expert analyst with strong critical thinking skills",
self_reflect=True,
llm="gemini/gemini-2.5-flash-lite-preview-06-17",
verbose=True
)
# Create a task
task = Task(
description="Analyze recent developments in AI",
expected_output="A detailed analysis report",
agent=agent
)
# Create and start the agents
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="sequential"
)
try:
# Start execution
result = agents.start()
print(f"Result without tools: {result}")
assert result, "Self-reflection without tools failed to produce a result."
print("\n✅ SUCCESS: Self-reflection without tools is working!")
return result
except Exception as e:
print(f"\n❌ ERROR: {str(e)}")
raise AssertionError(f"Test without tools failed: {str(e)}")
if __name__ == "__main__":
print("Testing self-reflection fix...")
# Test without tools (should work)
try:
result_without_tools = test_self_reflection_without_tools()
without_tools_success = True
except Exception as e:
print(f"Test without tools failed: {e}")
without_tools_success = False
# Test with tools (should work after fix)
try:
result_with_tools = test_self_reflection_with_tools()
with_tools_success = True
except Exception as e:
print(f"Test with tools failed: {e}")
with_tools_success = False
print("\n=== Test Summary ===")
print(f"Without tools: {'SUCCESS' if without_tools_success else 'FAILED'}")
print(f"With tools: {'SUCCESS' if with_tools_success else 'FAILED'}")
if with_tools_success:
print("\n✅ Fix verified: Self-reflection now works with tools!")
else:
print("\n❌ Fix failed: Self-reflection still not working with tools")
raise AssertionError("Self-reflection with tools test failed")
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Simple test to verify the self-reflection fix works"""
from praisonaiagents import Agent
from praisonaiagents.tools import calculator
def test_self_reflection_fix():
"""Test that self-reflection works with tools after the fix"""
print("=== Testing Self-Reflection Fix ===")
# Create an agent with self-reflection and a simple tool
agent = Agent(
role="Math Assistant",
goal="Solve math problems accurately",
backstory="You are a helpful math assistant",
self_reflect=True,
llm="gpt-4o-mini", # Use a more widely available model
verbose=True,
tools=[calculator],
min_reflect=1,
max_reflect=2
)
# Test with a simple calculation that might trigger self-reflection
try:
response = agent.start("What is 25 * 17? Show your work and double-check the answer.")
print(f"\nResponse: {response}")
assert response, "Self-reflection with tools failed to produce a response."
print("\n✅ SUCCESS: Self-reflection with tools is working!")
except Exception as e:
print(f"\n❌ ERROR: {str(e)}")
raise AssertionError(f"Test failed with error: {str(e)}")
if __name__ == "__main__":
test_self_reflection_fix()
@@ -0,0 +1,51 @@
#!/usr/bin/env python3
"""Test to verify the self-reflection fix works with tools"""
from praisonaiagents import Agent, Task, PraisonAIAgents
from praisonaiagents.tools import calculator
def test_self_reflection_fix_verification():
"""Test that self-reflection now works with tools"""
print("=== Testing Self-Reflection Fix Verification ===")
# Create an agent with self-reflection enabled and tools
agent = Agent(
name="MathAgent",
role="Math Assistant",
goal="Solve mathematical problems accurately",
backstory="You are a helpful math assistant who double-checks calculations",
tools=[calculator],
self_reflect=True, # This should now work with tools
min_reflect=1,
max_reflect=2,
verbose=True
)
# Define a task that would benefit from self-reflection
task = Task(
description="Calculate 123 * 456 and verify the result is correct",
expected_output="The calculation result with verification",
agent=agent,
name="math_calculation"
)
# Create and run the agents
agents = PraisonAIAgents(
agents=[agent],
tasks=[task],
process="sequential"
)
try:
result = agents.start()
assert result, "Self-reflection with tools failed to produce a result."
print("\n✅ SUCCESS: Self-reflection with tools is working!")
print(f"Result: {result}")
except Exception as e:
print(f"\n❌ ERROR: {str(e)}")
raise AssertionError(f"Test failed with error: {str(e)}")
if __name__ == "__main__":
test_self_reflection_fix_verification()
+1 -1
View File
@@ -2375,7 +2375,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.130"
version = "0.0.131"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
+2 -2
View File
@@ -3,8 +3,8 @@ class Praisonai < Formula
desc "AI tools for various AI applications"
homepage "https://github.com/MervinPraison/PraisonAI"
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.56.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.56.tar.gz | shasum -a 256`.split.first
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.57.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.57.tar.gz | shasum -a 256`.split.first
license "MIT"
depends_on "[email protected]"
+1 -1
View File
@@ -57,7 +57,7 @@ class CloudDeployer:
file.write("FROM python:3.11-slim\n")
file.write("WORKDIR /app\n")
file.write("COPY . .\n")
file.write("RUN pip install flask praisonai==2.2.56 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.2.57 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
+4 -4
View File
@@ -1,6 +1,6 @@
[project]
name = "PraisonAI"
version = "2.2.56"
version = "2.2.57"
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
readme = "README.md"
license = ""
@@ -12,7 +12,7 @@ dependencies = [
"rich>=13.7",
"markdown>=3.5",
"pyparsing>=3.0.0",
"praisonaiagents>=0.0.130",
"praisonaiagents>=0.0.131",
"python-dotenv>=0.19.0",
"instructor>=1.3.3",
"PyYAML>=6.0",
@@ -95,7 +95,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
[tool.poetry]
name = "PraisonAI"
version = "2.2.56"
version = "2.2.57"
description = "PraisonAI is an AI Agents Framework with Self Reflection. PraisonAI application combines PraisonAI Agents, AutoGen, and CrewAI into a low-code solution for building and managing multi-agent LLM systems, focusing on simplicity, customisation, and efficient human-agent collaboration."
authors = ["Mervin Praison"]
license = ""
@@ -113,7 +113,7 @@ python = ">=3.10,<3.13"
rich = ">=13.7"
markdown = ">=3.5"
pyparsing = ">=3.0.0"
praisonaiagents = ">=0.0.130"
praisonaiagents = ">=0.0.131"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
+5 -5
View File
@@ -3932,7 +3932,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.2.56"
version = "2.2.57"
source = { editable = "." }
dependencies = [
{ name = "instructor" },
@@ -4074,7 +4074,7 @@ requires-dist = [
{ name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" },
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.15" },
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.15" },
{ name = "praisonaiagents", specifier = ">=0.0.130" },
{ name = "praisonaiagents", specifier = ">=0.0.131" },
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = ">=0.2.19" },
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
@@ -4132,7 +4132,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.130"
version = "0.0.131"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohttp" },
@@ -4142,9 +4142,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/30/3a/e4f4bf6724051ada87c8f4a02615075b7c9b3963bdb37268d79f8a2bde08/praisonaiagents-0.0.130.tar.gz", hash = "sha256:0f893fdb65b68b489d7a9daf732a9c0c8eb2ce942d2c7b1c073d41e9496da239", size = 193079, upload-time = "2025-07-14T16:26:59.782Z" }
sdist = { url = "https://files.pythonhosted.org/packages/ac/a8/6c8b10f2254a5914a0c282e8691bea7f3fff60ea99d975db11e8d3382c9f/praisonaiagents-0.0.131.tar.gz", hash = "sha256:8b6ceef477f2c1d4e33af1242c52cf1956826066ef4d7ed3bc2dcd50ce1c68c8", size = 198480, upload-time = "2025-07-14T17:35:51.53Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/3b/8e/54f1858a001dbaf0a97b31406e06bfd47cbdf335b2ee509f591fcd4311de/praisonaiagents-0.0.130-py3-none-any.whl", hash = "sha256:c75983e55f9a0bd74905c28f7000acdd6d5516e519ce2aa5ef3e37048544ea6e", size = 214415, upload-time = "2025-07-14T16:26:58.109Z" },
{ url = "https://files.pythonhosted.org/packages/bd/d7/c443377ac2eaf68154b4ae6f87e5bc681f80774f8af9fcb368454ea9a7d8/praisonaiagents-0.0.131-py3-none-any.whl", hash = "sha256:fac2cdfe78da0fea89a74d54aab2fa9e920d61ca8d08ccebeb58132fc3770dc8", size = 218389, upload-time = "2025-07-14T17:35:49.94Z" },
]
[[package]]