Compare commits

...
13 Commits
Author SHA1 Message Date
Mervin Praison dbd6062edf chore: bump praisonai to v2.2.70 and praisonaiagents to v0.0.142 across all files 2025-07-17 01:31:56 +01:00
Mervin Praison 7fc615ff70 Merge pull request #974 from MervinPraison/claude/pr-965-20250716-2356
fix: make TelemetryCollector.stop() consistent with shutdown behavior
2025-07-17 01:24:26 +01:00
Mervin Praison fabac466f9 Merge pull request #966 from MervinPraison/claude/issue-964-20250716-2340
fix: improve Ollama sequential tool execution to prevent redundant calls
2025-07-17 01:22:11 +01:00
github-actions[bot]andMervin Praison 8d98f27f9c fix: correct Ollama early stopping logic placement for sequential tool execution
Fixes critical issues identified in PR review:
- Move early stopping logic from inside ''if tool_calls:'' block to ''else:'' block
- Fix logic placement in both sync and async execution paths
- Remove unreachable code in _generate_ollama_tool_summary()
- Ensure early stopping triggers correctly when Ollama returns empty responses after tool execution

This ensures the fix properly handles the Ollama edge case where it returns
empty responses after successful tool execution, preventing infinite loops
while allowing proper sequential tool calls.

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

Co-authored-by: Mervin Praison <[email protected]>
2025-07-17 00:02:48 +00:00
github-actions[bot]andMervin Praison cf0af4999d fix: make TelemetryCollector.stop() consistent with shutdown behavior
Ensures TelemetryCollector.stop() calls shutdown() instead of just flush()
for consistency with the main atexit handler and proper PostHog session cleanup.

Co-authored-by: Mervin Praison <[email protected]>
2025-07-17 00:01:42 +00:00
github-actions[bot]andMervin Praison f6ecd3b4db fix: improve Ollama early stopping logic to allow proper sequential tool execution
- Only trigger early stopping when no new tool calls are present in current response
- Prevents premature termination during sequential tool calls like get_stock_price -> multiply
- Maintains backward compatibility while fixing issue #964
- Applied to both sync and async execution paths

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

Co-authored-by: Mervin Praison <[email protected]>
2025-07-16 23:56:48 +00:00
Mervin Praison a348d186c3 Merge pull request #965 from MervinPraison/claude/issue-963-20250716-2334
fix: properly shutdown telemetry aiohttp sessions to prevent ImportError during Python shutdown
2025-07-17 00:54:16 +01:00
Mervin Praison 30c0f2458f Merge pull request #961 from MervinPraison/claude/issue-958-20250716-2304
fix: ensure consistent Task/Response formatting across all LLM providers
2025-07-17 00:53:53 +01:00
Mervin Praison 668d61344f Merge branch 'main' into claude/issue-958-20250716-2304 2025-07-17 00:47:55 +01:00
github-actions[bot]andMervin Praison e8e9625f22 fix: improve Ollama sequential tool execution to prevent redundant calls
- Add early stopping logic for Ollama after first successful tool execution
- Enhance tool summary generation to create more natural, OpenAI-like responses
- Apply fix to both sync and async execution paths
- Prevent multiple tool calls when execution is complete
- Maintains full backward compatibility

Co-authored-by: Mervin Praison <[email protected]>
2025-07-16 23:47:43 +00:00
github-actions[bot]andMervin Praison 92978fdb1c fix: properly shutdown telemetry aiohttp sessions to prevent ImportError during Python shutdown
- Change atexit handler from flush() to shutdown() in telemetry/__init__.py
- This ensures PostHog aiohttp sessions are properly closed before Python shutdown
- Prevents ''ImportError: sys.meta_path is None, Python is likely shutting down'' error
- Maintains backward compatibility with existing functionality

Fixes #963

Co-authored-by: Mervin Praison <[email protected]>
2025-07-16 23:40:40 +00:00
github-actions[bot]andMervin Praison 6e664995e8 fix: add missing flag reset in async achat method for consistent display behavior
- Added _final_display_shown = False reset in achat() method to match chat() method
- Ensures proper display flag management for both sync and async agent interactions
- Maintains consistency across all agent conversation entry points

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

Co-authored-by: Mervin Praison <[email protected]>
2025-07-16 23:32:12 +00:00
github-actions[bot]andMervin Praison 1aa20de337 fix: ensure consistent Task/Response formatting across all LLM providers
- Modified display condition to show formatted output for both OpenAI and custom LLM providers (like Gemini)
- Added _final_display_shown flag to prevent duplicate displays within same execution
- Reset flag at start of each chat() call for new conversations
- Maintains backward compatibility and existing verbose control behavior

Fixes #958

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

Co-authored-by: Mervin Praison <[email protected]>
2025-07-16 23:15:08 +00:00
15 changed files with 84 additions and 37 deletions

No files matched your search

+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.69" \
"praisonai>=2.2.70" \
"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.69" \
"praisonai>=2.2.70" \
"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.69" \
"praisonai>=2.2.70" \
"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.69" \
"praisonai>=2.2.70" \
"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.69`
- PraisonAI: `>=2.2.70`
- 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.69" "praisonaiagents==0.0.92"
RUN pip install "praisonai==2.2.70" "praisonaiagents==0.0.92"
```
## 🌐 Production Deployment
@@ -354,6 +354,8 @@ class Agent:
self.instructions = instructions
# Check for model name in environment variable if not provided
self._using_custom_llm = False
# Flag to track if final result has been displayed to prevent duplicates
self._final_display_shown = False
# Store OpenAI client parameters for lazy initialization
self._openai_api_key = api_key
@@ -1173,18 +1175,23 @@ Your Goal: {self.goal}"""
task_description=task_description,
task_id=task_id
)
# 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:
# Always display final interaction when verbose is True to ensure consistent formatting
# This ensures both OpenAI and custom LLM providers (like Gemini) show formatted output
if self.verbose and not self._final_display_shown:
display_interaction(prompt, response, markdown=self.markdown,
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=task_name,
task_description=task_description,
task_id=task_id)
task_name=None, # Not available in this context
task_description=None, # Not available in this context
task_id=None) # Not available in this context
self._final_display_shown = 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):
# Reset the final display flag for each new conversation
self._final_display_shown = False
# Log all parameter values when in debug mode
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
param_info = {
@@ -1533,6 +1540,9 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
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):
"""Async version of chat method with self-reflection support."""
# Reset the final display flag for each new conversation
self._final_display_shown = False
# Log all parameter values when in debug mode
if logging.getLogger().getEffectiveLevel() == logging.DEBUG:
param_info = {
@@ -329,16 +329,29 @@ class LLM:
# For Ollama, always generate summary when we have tool results
# This prevents infinite loops caused by empty/minimal responses
# Build tool summary efficiently
summary_lines = ["Based on the tool execution results:"]
for i, result in enumerate(tool_results):
# Build tool summary more naturally to match OpenAI-style responses
if len(tool_results) == 1:
# Single tool result - create natural response
result = tool_results[0]
if isinstance(result, dict) and 'result' in result:
function_name = result.get('function_name', 'Tool')
summary_lines.append(f"- {function_name}: {result['result']}")
return str(result['result'])
else:
summary_lines.append(f"- Tool {i+1}: {result}")
return "\n".join(summary_lines)
return str(result)
else:
# Multiple tool results - create coherent summary
summary_lines = []
for i, result in enumerate(tool_results):
if isinstance(result, dict) and 'result' in result:
function_name = result.get('function_name', 'Tool')
summary_lines.append(f"{function_name}: {result['result']}")
else:
summary_lines.append(f"Tool {i+1}: {result}")
# Create more natural summary text
if len(summary_lines) == 2:
return f"{summary_lines[0]}. {summary_lines[1]}."
else:
return "Based on the tool execution: " + ". ".join(summary_lines) + "."
def _format_ollama_tool_result_message(self, function_name: str, tool_result: Any) -> Dict[str, str]:
"""
@@ -1173,6 +1186,7 @@ class LLM:
final_response_text = response_text.strip()
break
# Special handling for Ollama to prevent infinite loops
# Only generate summary after multiple iterations to allow sequential execution
should_break, tool_summary_text, iteration_count = self._handle_ollama_sequential_logic(
@@ -1198,6 +1212,17 @@ class LLM:
continue
else:
# No tool calls, we're done with this iteration
# Special early stopping logic for Ollama when tool results are available
# Ollama often provides empty responses after successful tool execution
if (self._is_ollama_provider() and accumulated_tool_results and iteration_count >= 1 and
(not response_text or response_text.strip() == "")):
# Generate coherent response from tool results
tool_summary = self._generate_ollama_tool_summary(accumulated_tool_results, response_text)
if tool_summary:
final_response_text = tool_summary
break
# If we've executed tools in previous iterations, this response contains the final answer
if iteration_count > 0 and not final_response_text:
final_response_text = response_text.strip() if response_text else ""
@@ -1956,6 +1981,7 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
final_response_text = response_text.strip()
break
# Special handling for Ollama to prevent infinite loops
# Only generate summary after multiple iterations to allow sequential execution
should_break, tool_summary_text, iteration_count = self._handle_ollama_sequential_logic(
@@ -1981,6 +2007,17 @@ Output MUST be JSON with 'reflection' and 'satisfactory'.
continue
else:
# No tool calls, we're done with this iteration
# Special early stopping logic for Ollama when tool results are available
# Ollama often provides empty responses after successful tool execution
if (self._is_ollama_provider() and accumulated_tool_results and iteration_count >= 1 and
(not response_text or response_text.strip() == "")):
# Generate coherent response from tool results
tool_summary = self._generate_ollama_tool_summary(accumulated_tool_results, response_text)
if tool_summary:
final_response_text = tool_summary
break
# If we've executed tools in previous iterations, this response contains the final answer
if iteration_count > 0 and not final_response_text:
final_response_text = response_text.strip()
@@ -65,8 +65,8 @@ def _ensure_atexit():
])
if not telemetry_disabled:
# Register atexit handler to flush telemetry on exit
atexit.register(lambda: get_telemetry().flush())
# Register atexit handler to properly shutdown telemetry on exit
atexit.register(lambda: get_telemetry().shutdown())
_atexit_registered = True
def _initialize_telemetry():
@@ -354,8 +354,8 @@ class TelemetryCollector:
pass
def stop(self):
"""Stop telemetry collection and flush data."""
self.telemetry.flush()
"""Stop telemetry collection and properly shutdown."""
self.telemetry.shutdown()
def trace_agent_execution(self, agent_name: str, **attributes):
"""Compatibility method for agent execution tracking."""
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.141"
version = "0.0.142"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
requires-python = ">=3.10"
authors = [
+1 -1
View File
@@ -2375,7 +2375,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.141"
version = "0.0.142"
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.69.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.69.tar.gz | shasum -a 256`.split.first
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.70.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v2.2.70.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.69 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.2.70 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.69"
version = "2.2.70"
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.141",
"praisonaiagents>=0.0.142",
"python-dotenv>=0.19.0",
"instructor>=1.3.3",
"PyYAML>=6.0",
@@ -102,7 +102,7 @@ autogen-v4 = [
[tool.poetry]
name = "PraisonAI"
version = "2.2.69"
version = "2.2.70"
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 = ""
@@ -120,7 +120,7 @@ python = ">=3.10,<3.13"
rich = ">=13.7"
markdown = ">=3.5"
pyparsing = ">=3.0.0"
praisonaiagents = ">=0.0.141"
praisonaiagents = ">=0.0.142"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
+5 -5
View File
@@ -4048,7 +4048,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.2.68"
version = "2.2.70"
source = { editable = "." }
dependencies = [
{ name = "instructor" },
@@ -4202,7 +4202,7 @@ requires-dist = [
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.22" },
{ name = "praisonai-tools", marker = "extra == 'autogen-v4'", specifier = ">=0.0.22" },
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.22" },
{ name = "praisonaiagents", specifier = ">=0.0.140" },
{ name = "praisonaiagents", specifier = ">=0.0.142" },
{ name = "pyautogen", marker = "extra == 'autogen'", specifier = "==0.2.29" },
{ name = "pydantic", marker = "extra == 'chat'", specifier = "<=2.10.1" },
{ name = "pydantic", marker = "extra == 'code'", specifier = "<=2.10.1" },
@@ -4257,7 +4257,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.140"
version = "0.0.142"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohttp" },
@@ -4266,9 +4266,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/65/33/1390fa209dada4871cbd6295a29e539765b52955054c458483f39d016eb2/praisonaiagents-0.0.140.tar.gz", hash = "sha256:b45e492cdc11593fddb5df8f3bbc7f558e9ec10dc025858f2c9d5e4178832b56", size = 204508, upload-time = "2025-07-16T23:18:29.748Z" }
sdist = { url = "https://files.pythonhosted.org/packages/51/2b/d341e8f774adc84c273e57f7bb6c4ec8af5f974b4d5ec0a1811ab96ef5c5/praisonaiagents-0.0.142.tar.gz", hash = "sha256:9f6dfd26d5c334eda3f0ba86c821616cb673ef230c15d57e8230e4f9bb7108a8", size = 205694, upload-time = "2025-07-17T00:31:09.554Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7a/78/92f61b70bfda2ffabeababf956e0742d952ae4728d39aaaa3f071f36714f/praisonaiagents-0.0.140-py3-none-any.whl", hash = "sha256:793a06363c1f7dfb83690a85879f307c3fc5f32e77ab1bd5f3ee2295c3fbc603", size = 223605, upload-time = "2025-07-16T23:18:28.415Z" },
{ url = "https://files.pythonhosted.org/packages/71/c9/79f2124287cc99c0d50f23b20ff2958e08eb8b0b496198607f343dfc2245/praisonaiagents-0.0.142-py3-none-any.whl", hash = "sha256:0887cf92acda4be650dd8e7a110a16e4e7624eae12a7c10e8ca8d32f1ac282cf", size = 224783, upload-time = "2025-07-17T00:31:07.946Z" },
]
[[package]]