Compare commits

...
13 Commits
Author SHA1 Message Date
Mervin Praison f36415d4ea Merge pull request #519 from MervinPraison/develop
Update version to 2.2.16 across project files
2025-05-27 06:10:32 +01:00
MervinPraison 13c3e31458 Update version to 2.2.16 across project files
- Incremented PraisonAI version from 2.2.15 to 2.2.16 in `pyproject.toml`, `uv.lock`, and all relevant Dockerfiles for consistency.
- Updated dependency version for `praisonaiagents` from 0.0.88 to 0.0.89.
- Ensured minimal changes to existing code while maintaining versioning accuracy across the project.
2025-05-27 06:10:01 +01:00
Mervin Praison 41d550c4a5 Merge pull request #518 from MervinPraison/claude/issue-484-20250527_043953
feat: Add context management for workflow loops
2025-05-27 05:54:45 +01:00
Mervin Praison ac570e5b93 Rename test_context_management.py to tests/unit/test_context_management.py 2025-05-27 05:54:18 +01:00
claude[bot]andMervinPraison a5465882ee feat: Add context management for workflow loops to prevent context length issues
- Add retain_full_context parameter to Task class (default False)
- By default, only include most recent previous task in context
- Maintain backwards compatibility with retain_full_context=True
- Add _build_task_context helper method to Process class  
- Update both async and sync workflow methods to use new context building
- Add test script to verify functionality

Fixes context accumulation issue in workflow loops where all previous
task results were being appended, causing exponential context growth.

Co-authored-by: MervinPraison <[email protected]>
2025-05-27 04:47:37 +00:00
Mervin Praison f86b03e5d8 Merge pull request #517 from MervinPraison/develop
Update version to 2.2.15 across project files
2025-05-27 05:47:27 +01:00
MervinPraison 397a466f55 Update version to 2.2.15 across project files
- Incremented PraisonAI version from 2.2.14 to 2.2.15 in `pyproject.toml`, `uv.lock`, and all relevant Dockerfiles for consistency.
- Updated dependency version for `praisonaiagents` from 0.0.87 to 0.0.88.
- Ensured minimal changes to existing code while maintaining versioning accuracy across the project.
2025-05-27 05:36:34 +01:00
Mervin Praison 36c3066ba4 Merge pull request #515 from MervinPraison/claude/issue-514-20250526_155559
fix: Add asyncio import to local scope in launch() method
2025-05-26 20:00:06 +01:00
claude[bot]andMervinPraison df06e794b3 fix: Add asyncio import to local scope in launch() method
This fixes the NameError: cannot access free variable 'asyncio' where it is not associated with a value in enclosing scope error that occurs at line 1563.

The issue was that the handle_agent_query function (containing line 1563) is defined inside a try/except block that imports FastAPI dependencies but didn't include asyncio. Added import asyncio to the try block and removed redundant import from line 1659.

Fixes #514

Co-authored-by: MervinPraison <[email protected]>
2025-05-26 15:59:29 +00:00
Mervin Praison 71fe678d66 Merge pull request #513 from MervinPraison/develop
Update version to 2.2.14 across project files
2025-05-25 06:47:58 +01:00
MervinPraison c8875eec81 Update version to 2.2.14 across project files
- Incremented PraisonAI version from 2.2.13 to 2.2.14 in `pyproject.toml`, `uv.lock`, and all relevant Dockerfiles for consistency.
- Ensured minimal changes to existing code while maintaining versioning accuracy across the project.
2025-05-25 06:47:32 +01:00
Mervin Praison f88204d2e8 Merge pull request #512 from MervinPraison/develop
Update version to 2.2.13 across project files
2025-05-24 23:18:37 +01:00
MervinPraison fc01a80956 Update version to 2.2.13 across project files
- Incremented PraisonAI version from 2.2.12 to 2.2.13 in `pyproject.toml`, `uv.lock`, and all relevant Dockerfiles for consistency.
- Ensured minimal changes to existing code while maintaining versioning accuracy across the project.
2025-05-24 23:18:09 +01:00
20 changed files with 302 additions and 94 deletions

No files matched your search

+1 -1
View File
@@ -1,6 +1,6 @@
FROM python:3.11-slim
WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.2.12 gunicorn markdown
RUN pip install flask praisonai==2.2.16 gunicorn markdown
EXPOSE 8080
CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]
+1 -1
View File
@@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN pip install --no-cache-dir \
praisonaiagents>=0.0.4 \
praisonai_tools \
"praisonai==2.2.12" \
"praisonai==2.2.16" \
"praisonai[chat]" \
"embedchain[github,youtube]"
+1 -1
View File
@@ -15,7 +15,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN pip install --no-cache-dir \
praisonaiagents>=0.0.4 \
praisonai_tools \
"praisonai==2.2.12" \
"praisonai==2.2.16" \
"praisonai[ui]" \
"praisonai[chat]" \
"praisonai[realtime]" \
+1 -1
View File
@@ -13,7 +13,7 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
RUN pip install --no-cache-dir \
praisonaiagents>=0.0.4 \
praisonai_tools \
"praisonai==2.2.12" \
"praisonai==2.2.16" \
"praisonai[ui]" \
"praisonai[crewai]"
+1 -1
View File
@@ -110,7 +110,7 @@ Loads environment variables from .env file or system and sets them.</p>
file.write(&#34;FROM python:3.11-slim\n&#34;)
file.write(&#34;WORKDIR /app\n&#34;)
file.write(&#34;COPY . .\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.2.12 gunicorn markdown\n&#34;)
file.write(&#34;RUN pip install flask praisonai==2.2.16 gunicorn markdown\n&#34;)
file.write(&#34;EXPOSE 8080\n&#34;)
file.write(&#39;CMD [&#34;gunicorn&#34;, &#34;-b&#34;, &#34;0.0.0.0:8080&#34;, &#34;api:app&#34;]\n&#39;)
+1 -1
View File
@@ -27,7 +27,7 @@ WORKDIR /app
COPY . .
RUN pip install flask praisonai==2.2.12 watchdog
RUN pip install flask praisonai==2.2.16 watchdog
EXPOSE 5555
+1 -1
View File
@@ -155,7 +155,7 @@ To facilitate local development with live reload, you can use Docker. Follow the
COPY . .
RUN pip install flask praisonai==2.2.12 watchdog
RUN pip install flask praisonai==2.2.16 watchdog
EXPOSE 5555
+1 -1
View File
@@ -208,7 +208,7 @@ To facilitate local development with live reload, you can use Docker. Follow the
COPY . .
RUN pip install flask praisonai==2.2.12 watchdog
RUN pip install flask praisonai==2.2.16 watchdog
EXPOSE 5555
+4 -1
View File
@@ -113,11 +113,14 @@ class PraisonAI:
Initialize the PraisonAI object with default parameters.
"""
self.agent_yaml = agent_yaml
# Create config_list with AutoGen compatibility
api_key = os.environ.get("OPENAI_API_KEY")
self.config_list = [
{
'model': os.environ.get("OPENAI_MODEL_NAME", "gpt-4o"),
'base_url': os.environ.get("OPENAI_API_BASE", "https://api.openai.com/v1"),
'api_key': os.environ.get("OPENAI_API_KEY")
'api_key': api_key,
'api_type': 'openai' # AutoGen expects this field
}
]
self.agent_file = agent_file
+1 -1
View File
@@ -56,7 +56,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.12 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==2.2.16 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.12"
version = "2.2.16"
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.87",
"praisonaiagents>=0.0.89",
"python-dotenv>=0.19.0",
"instructor>=1.3.3",
"PyYAML>=6.0",
@@ -89,7 +89,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.15", "crewai"]
[tool.poetry]
name = "PraisonAI"
version = "2.2.12"
version = "2.2.16"
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 = ""
@@ -107,7 +107,7 @@ python = ">=3.10,<3.13"
rich = ">=13.7"
markdown = ">=3.5"
pyparsing = ">=3.0.0"
praisonaiagents = ">=0.0.87"
praisonaiagents = ">=0.0.89"
python-dotenv = ">=0.19.0"
instructor = ">=1.3.3"
PyYAML = ">=6.0"
@@ -1478,6 +1478,7 @@ Your Goal: {self.goal}
from pydantic import BaseModel
import threading
import time
import asyncio
# Define the request model here since we need pydantic
class AgentQuery(BaseModel):
@@ -1656,7 +1657,6 @@ Your Goal: {self.goal}
import threading
import time
import inspect
import asyncio # Ensure asyncio is imported
# logging is already imported at the module level
except ImportError as e:
@@ -30,6 +30,44 @@ class Process:
self.task_retry_counter: Dict[str, int] = {} # Initialize retry counter
self.workflow_finished = False # ADDED: Workflow finished flag
def _build_task_context(self, current_task: Task) -> str:
"""Build context for a task based on its retain_full_context setting"""
if not (current_task.previous_tasks or current_task.context):
return ""
context = "\nInput data from previous tasks:"
if current_task.retain_full_context:
# Original behavior: include all previous tasks
for prev_name in current_task.previous_tasks:
prev_task = next((t for t in self.tasks.values() if t.name == prev_name), None)
if prev_task and prev_task.result:
context += f"\n{prev_name}: {prev_task.result.raw}"
# Add data from context tasks
if current_task.context:
for ctx_task in current_task.context:
if ctx_task.result and ctx_task.name != current_task.name:
context += f"\n{ctx_task.name}: {ctx_task.result.raw}"
else:
# New behavior: only include the most recent previous task
if current_task.previous_tasks:
# Get the most recent previous task (last in the list)
prev_name = current_task.previous_tasks[-1]
prev_task = next((t for t in self.tasks.values() if t.name == prev_name), None)
if prev_task and prev_task.result:
context += f"\n{prev_name}: {prev_task.result.raw}"
# For context tasks, still include the most recent one
if current_task.context:
# Get the most recent context task with a result
for ctx_task in reversed(current_task.context):
if ctx_task.result and ctx_task.name != current_task.name:
context += f"\n{ctx_task.name}: {ctx_task.result.raw}"
break # Only include the most recent one
return context
def _find_next_not_started_task(self) -> Optional[Task]:
"""Fallback mechanism to find the next 'not started' task."""
fallback_attempts = 0
@@ -147,25 +185,8 @@ Description length: {len(current_task.description)}
""")
# Add context from previous tasks to description
if current_task.previous_tasks or current_task.context:
context = "\nInput data from previous tasks:"
# Add data from previous tasks in workflow
for prev_name in current_task.previous_tasks:
prev_task = next((t for t in self.tasks.values() if t.name == prev_name), None)
if prev_task and prev_task.result:
# Handle loop data
if current_task.task_type == "loop":
context += f"\n{prev_name}: {prev_task.result.raw}"
else:
context += f"\n{prev_name}: {prev_task.result.raw}"
# Add data from context tasks
if current_task.context:
for ctx_task in current_task.context:
if ctx_task.result and ctx_task.name != current_task.name:
context += f"\n{ctx_task.name}: {ctx_task.result.raw}"
context = self._build_task_context(current_task)
if context:
# Update task description with context
current_task.description = current_task.description + context
@@ -778,25 +799,8 @@ Description length: {len(current_task.description)}
""")
# Add context from previous tasks to description
if current_task.previous_tasks or current_task.context:
context = "\nInput data from previous tasks:"
# Add data from previous tasks in workflow
for prev_name in current_task.previous_tasks:
prev_task = next((t for t in self.tasks.values() if t.name == prev_name), None)
if prev_task and prev_task.result:
# Handle loop data
if current_task.task_type == "loop":
context += f"\n{prev_name}: {prev_task.result.raw}"
else:
context += f"\n{prev_name}: {prev_task.result.raw}"
# Add data from context tasks
if current_task.context:
for ctx_task in current_task.context:
if ctx_task.result and ctx_task.name != current_task.name:
context += f"\n{ctx_task.name}: {ctx_task.result.raw}"
context = self._build_task_context(current_task)
if context:
# Update task description with context
current_task.description = current_task.description + context
@@ -39,7 +39,8 @@ class Task:
memory=None,
quality_check=True,
input_file: Optional[str] = None,
rerun: bool = False # Renamed from can_rerun and logic inverted, default True for backward compatibility
rerun: bool = False, # Renamed from can_rerun and logic inverted, default True for backward compatibility
retain_full_context: bool = False # By default, only use previous task output, not all previous tasks
):
# Add check if memory config is provided
if memory is not None or (config and config.get('memory_config')):
@@ -78,6 +79,7 @@ class Task:
self.memory = memory
self.quality_check = quality_check
self.rerun = rerun # Assigning the rerun parameter
self.retain_full_context = retain_full_context
# Set logger level based on config verbose level
verbose = self.config.get("verbose", 0)
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.0.87"
version = "0.0.89"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
authors = [
{ name="Mervin Praison" }
+1 -1
View File
@@ -1457,7 +1457,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.87"
version = "0.0.89"
source = { editable = "." }
dependencies = [
{ name = "mcp" },
+82 -32
View File
@@ -18,10 +18,15 @@ class TestPraisonAIFramework(unittest.TestCase):
result = praisonai.run()
self.assertIn('Task Output', result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise
def test_main_with_autogen_framework(self):
@@ -30,10 +35,15 @@ class TestPraisonAIFramework(unittest.TestCase):
result = praisonai.run()
self.assertTrue('Task Output' in result or '### Output ###' in result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise
def test_main_with_custom_framework(self):
@@ -42,10 +52,15 @@ class TestPraisonAIFramework(unittest.TestCase):
result = praisonai.run()
self.assertIn('Task Output', result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise
def test_main_with_internet_search_tool(self):
@@ -54,10 +69,15 @@ class TestPraisonAIFramework(unittest.TestCase):
result = praisonai.run()
self.assertIn('Task Output', result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise
def test_main_with_built_in_tool(self):
@@ -66,10 +86,15 @@ class TestPraisonAIFramework(unittest.TestCase):
result = praisonai.run()
self.assertIn('Task Output', result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise
@@ -88,19 +113,29 @@ class TestPraisonAICommandLine(unittest.TestCase):
def test_praisonai_command(self):
command = "praisonai --framework autogen --auto create movie script about cat in mars"
result = self.run_command(command)
# Handle API authentication errors in command line output
if ('Invalid API Key' in result or 'AuthenticationError' in result or
'InstructorRetryException' in result or '401' in result):
self.skipTest(f"Skipping due to API authentication in command output")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
if ('Invalid API Key' in result or 'AuthenticationError' in result or
'InstructorRetryException' in result or '401' in result):
self.skipTest(f"Skipping due to no valid API key provided")
self.assertIn('TERMINATE', result)
def test_praisonai_init_command(self):
command = "praisonai --framework autogen --init create movie script about cat in mars"
result = self.run_command(command)
# Handle API authentication errors in command line output
if ('Invalid API Key' in result or 'AuthenticationError' in result or
'InstructorRetryException' in result or '401' in result):
self.skipTest(f"Skipping due to API authentication in command output")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
if ('Invalid API Key' in result or 'AuthenticationError' in result or
'InstructorRetryException' in result or '401' in result):
self.skipTest(f"Skipping due to no valid API key provided")
self.assertIn('created successfully', result)
class TestExamples(unittest.TestCase):
@@ -119,10 +154,15 @@ class TestExamples(unittest.TestCase):
f"Expected meaningful result, got: {result}"
)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise
def test_advanced_example(self):
@@ -140,10 +180,15 @@ class TestExamples(unittest.TestCase):
f"Expected meaningful result, got: {result}"
)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise
def test_auto_example(self):
@@ -161,10 +206,15 @@ class TestExamples(unittest.TestCase):
f"Expected meaningful result, got: {result}"
)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
self.skipTest(f"Skipping due to API authentication: {e}")
# Only skip if no API key provided or using test/fallback key
api_key = os.environ.get('OPENAI_API_KEY', '')
if (not api_key or
api_key.startswith('sk-test-') or
api_key == 'nokey' or
'fallback' in api_key):
self.skipTest(f"Skipping due to no valid API key provided: {e}")
else:
# Real API key provided - test should fail, not skip
raise
if __name__ == '__main__':
+32 -1
View File
@@ -1,14 +1,24 @@
# tests/test_agents_playbook.py
import unittest
import subprocess
import pytest
import os
from praisonai import PraisonAI
class TestPraisonAIFramework(unittest.TestCase):
@pytest.mark.real
def test_main_with_autogen_framework(self):
"""Test AutoGen framework integration with real API calls"""
# Check if we have a real API key
api_key = os.environ.get('OPENAI_API_KEY')
if not api_key or api_key == 'test-key' or api_key.startswith('sk-test-'):
self.skipTest("Skipping real test - no valid OPENAI_API_KEY provided")
praisonai = PraisonAI(agent_file='tests/autogen-agents.yaml')
try:
result = praisonai.run()
self.assertIn('### Task Output ###', result)
self.assertIn('### Output ###', result)
except Exception as e:
if ('Invalid API Key' in str(e) or 'AuthenticationError' in str(e) or
'InstructorRetryException' in str(e) or '401' in str(e)):
@@ -16,7 +26,14 @@ class TestPraisonAIFramework(unittest.TestCase):
else:
raise
@pytest.mark.real
def test_main_with_custom_framework(self):
"""Test CrewAI framework integration with real API calls"""
# Check if we have a real API key
api_key = os.environ.get('OPENAI_API_KEY')
if not api_key or api_key == 'test-key' or api_key.startswith('sk-test-'):
self.skipTest("Skipping real test - no valid OPENAI_API_KEY provided")
praisonai = PraisonAI(agent_file='tests/crewai-agents.yaml')
try:
result = praisonai.run()
@@ -28,7 +45,14 @@ class TestPraisonAIFramework(unittest.TestCase):
else:
raise
@pytest.mark.real
def test_main_with_internet_search_tool(self):
"""Test internet search tool integration with real API calls"""
# Check if we have a real API key
api_key = os.environ.get('OPENAI_API_KEY')
if not api_key or api_key == 'test-key' or api_key.startswith('sk-test-'):
self.skipTest("Skipping real test - no valid OPENAI_API_KEY provided")
praisonai = PraisonAI(agent_file='tests/search-tool-agents.yaml')
try:
result = praisonai.run()
@@ -40,7 +64,14 @@ class TestPraisonAIFramework(unittest.TestCase):
else:
raise
@pytest.mark.real
def test_main_with_built_in_tool(self):
"""Test built-in tool integration with real API calls"""
# Check if we have a real API key
api_key = os.environ.get('OPENAI_API_KEY')
if not api_key or api_key == 'test-key' or api_key.startswith('sk-test-'):
self.skipTest("Skipping real test - no valid OPENAI_API_KEY provided")
praisonai = PraisonAI(agent_file='tests/inbuilt-tool-agents.yaml')
try:
result = praisonai.run()
+118
View File
@@ -0,0 +1,118 @@
#!/usr/bin/env python3
"""
Test script to verify context management functionality
"""
import sys
import os
sys.path.append(os.path.join(os.path.dirname(__file__), 'src', 'praisonai-agents'))
from praisonaiagents.task.task import Task
from praisonaiagents.agent.agent import Agent
from praisonaiagents.process.process import Process
from praisonaiagents.main import TaskOutput
def test_context_management():
"""Test context management with and without retain_full_context"""
print("Starting context management test...")
try:
# Create a mock agent
agent = Agent(name="Test Agent", role="Tester", goal="Test context management")
print("✓ Created test agent")
# Create tasks with results
task1 = Task(
name="task1",
description="First task",
agent=agent,
status="completed"
)
task1.result = TaskOutput(
description="First task",
raw="Result from task 1",
agent="Test Agent"
)
print("✓ Created task1")
task2 = Task(
name="task2",
description="Second task",
agent=agent,
status="completed"
)
task2.result = TaskOutput(
description="Second task",
raw="Result from task 2",
agent="Test Agent"
)
# Set up the previous_tasks manually since it's not a constructor parameter
task2.previous_tasks = ["task1"]
print("✓ Created task2")
# Test case 1: Default behavior (retain_full_context=False)
task3_limited = Task(
name="task3_limited",
description="Third task with limited context",
agent=agent,
retain_full_context=False # Default behavior
)
# Set up the previous_tasks manually
task3_limited.previous_tasks = ["task1", "task2"]
print("✓ Created task3_limited")
# Test case 2: Full context retention (retain_full_context=True)
task3_full = Task(
name="task3_full",
description="Third task with full context",
agent=agent,
retain_full_context=True # Original behavior
)
# Set up the previous_tasks manually
task3_full.previous_tasks = ["task1", "task2"]
print("✓ Created task3_full")
# Create process and test context building
tasks_dict = {
task1.id: task1,
task2.id: task2,
task3_limited.id: task3_limited,
task3_full.id: task3_full
}
process = Process(tasks=tasks_dict, agents=[agent])
print("✓ Created process")
# Test limited context
limited_context = process._build_task_context(task3_limited)
print("Limited context (retain_full_context=False):")
print(f"'{limited_context}'")
print()
# Test full context
full_context = process._build_task_context(task3_full)
print("Full context (retain_full_context=True):")
print(f"'{full_context}'")
print()
# Verify results
assert "task2" in limited_context, "Limited context should include most recent task"
assert "task1" not in limited_context, "Limited context should NOT include earlier tasks"
assert "task1" in full_context, "Full context should include all previous tasks"
assert "task2" in full_context, "Full context should include all previous tasks"
print("✅ All tests passed!")
print("- Limited context only includes most recent previous task")
print("- Full context includes all previous tasks")
print("- Backwards compatibility maintained")
except Exception as e:
print(f"❌ Test failed with error: {e}")
import traceback
traceback.print_exc()
raise
if __name__ == "__main__":
test_context_management()
Generated
+5 -5
View File
@@ -3614,7 +3614,7 @@ wheels = [
[[package]]
name = "praisonai"
version = "2.2.12"
version = "2.2.16"
source = { editable = "." }
dependencies = [
{ name = "instructor" },
@@ -3756,7 +3756,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.87" },
{ name = "praisonaiagents", specifier = ">=0.0.89" },
{ 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" },
@@ -3813,7 +3813,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.0.87"
version = "0.0.89"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mcp" },
@@ -3821,9 +3821,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/21/46/9761f0fc2d21361d418df9978ec71de74fd5a24e324c9ee7c8b0ad0f1965/praisonaiagents-0.0.87.tar.gz", hash = "sha256:d86c223e5b6f03c83a144270128e0b3f8b0929fb0e8389a85939b659048c04d1", size = 126602 }
sdist = { url = "https://files.pythonhosted.org/packages/3f/78/d374e38d8caa19fb4f6e64fc2642bddcdbaba9d9abd490bf638fbffe44ab/praisonaiagents-0.0.89.tar.gz", hash = "sha256:a3fed2a23ea0087f06316445f298fe3ca71cec6036a53cf02c2d4932ffea4572", size = 123918 }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fe/6f/6e1253bacf0026a57ec4bb2467401f5e541082d5cc8580e22cdc483b6419/praisonaiagents-0.0.87-py3-none-any.whl", hash = "sha256:fa6779c4980cd1318de33489552b69e612f1ca7aae482a4788c6d89e5b974c08", size = 146337 },
{ url = "https://files.pythonhosted.org/packages/af/af/e6991e977b3b74826736ef8fbc3dfdaf6eaa3987d60a814ca5bcd2c4a8b1/praisonaiagents-0.0.89-py3-none-any.whl", hash = "sha256:32cf8e2781fe03eec9c148ad99cc342c9cd0686cce5ff5c39545b730ac986fbb", size = 143626 },
]
[[package]]