Compare commits

...
1 Commits
Author SHA1 Message Date
Mervin Praison f5e17c697b Release v3.5.3 2026-01-08 21:52:44 +00:00
23 changed files with 271 additions and 139 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 \
praisonai_tools \
"praisonai>=3.5.2" \
"praisonai>=3.5.3" \
"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>=3.5.2" \
"praisonai>=3.5.3" \
"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>=3.5.2" \
"praisonai>=3.5.3" \
"praisonai[ui]" \
"praisonai[crewai]"
-1
View File
@@ -134,7 +134,6 @@ def example_real_chat():
instructions="You are a helpful math tutor. Be concise.",
reflection=False,
web=False,
verbose=False,
)
response = agent.start("What is the square root of 144?")
+8 -9
View File
@@ -22,19 +22,18 @@ async def main():
print("Agent-Centric Background Tasks Demo")
print("=" * 60)
# Create a background runner with config
# Create a background runner with config (used standalone, not passed to Agent)
runner = BackgroundRunner(config=BackgroundConfig(max_concurrent_tasks=3))
# Create an Agent with background task support
# Create an Agent
agent = Agent(
name="AsyncAssistant",
instructions="You are a helpful research assistant.",
background=runner
)
print("\n--- Agent with Background Support Created ---")
print(f"Agent: {agent.name}")
print(f"Background runner: {agent.background is not None}")
print(f"Background runner: {runner is not None}")
# Define some example tasks
async def research_task(topic: str) -> str:
@@ -49,17 +48,17 @@ async def main():
await asyncio.sleep(1.0)
return f"Analysis complete: {data}"
# Submit tasks via agent's background runner
# Submit tasks via background runner
print("\n--- Submitting Background Tasks ---")
task1 = await agent.background.submit(
task1 = await runner.submit(
research_task,
args=("AI trends 2025",),
name="research_ai"
)
print(f"✅ Submitted research task: {task1.id[:8]}")
task2 = await agent.background.submit(
task2 = await runner.submit(
analysis_task,
args=("market data",),
name="analyze_market"
@@ -68,7 +67,7 @@ async def main():
# List all tasks
print("\n--- Task Status ---")
for task in agent.background.tasks:
for task in runner.tasks:
print(f" [{task.id[:8]}] {task.status.value}")
# Wait for tasks to complete
@@ -83,7 +82,7 @@ async def main():
print(f" {status} {task.result}")
# Cleanup
agent.background.clear_completed()
runner.clear_completed()
print("\n✅ Cleared completed tasks")
print("\n" + "=" * 60)
+10 -11
View File
@@ -33,29 +33,28 @@ async def main():
test_file.write_text("# Initial content\nprint('Hello, World!')\n")
print(f"\n✅ Created initial file: {test_file.name}")
# Create checkpoint service
checkpoints = CheckpointService(workspace_dir=workspace)
# Create checkpoint service (used standalone, not passed to Agent)
checkpoint_service = CheckpointService(workspace_dir=workspace)
# Create an Agent with checkpoint support
# Create an Agent
agent = Agent(
name="RefactorBot",
instructions="You are a code refactoring assistant.",
checkpoints=checkpoints
)
print("\n--- Agent with Checkpoint Support Created ---")
print(f"Agent: {agent.name}")
print(f"Checkpoints enabled: {agent.checkpoints is not None}")
print(f"Checkpoint service initialized: {checkpoint_service is not None}")
try:
# Initialize the shadow git repository
print("\n--- Initializing Checkpoint Service ---")
await agent.checkpoints.initialize()
await checkpoint_service.initialize()
print("✅ Shadow git repository initialized")
# Save first checkpoint
print("\n--- Saving Checkpoint 1: Initial State ---")
result1 = await agent.checkpoints.save("Initial state")
result1 = await checkpoint_service.save("Initial state")
print(f"✅ Checkpoint saved: {result1.checkpoint.id[:8]}")
print(f" Message: {result1.checkpoint.message}")
@@ -71,18 +70,18 @@ async def main():
# Save second checkpoint
print("\n--- Saving Checkpoint 2: After Refactoring ---")
result2 = await agent.checkpoints.save("Added features")
result2 = await checkpoint_service.save("Added features")
print(f"✅ Checkpoint saved: {result2.checkpoint.id[:8]}")
# List all checkpoints
print("\n--- Listing All Checkpoints ---")
checkpoints_list = await agent.checkpoints.list_checkpoints()
checkpoints_list = await checkpoint_service.list_checkpoints()
for i, cp in enumerate(checkpoints_list, 1):
print(f" {i}. [{cp.id[:8]}] {cp.message}")
# Show diff
print("\n--- Current Diff ---")
diff = await agent.checkpoints.diff()
diff = await checkpoint_service.diff()
if diff.files:
for f in diff.files:
print(f" {f.status}: {f.path}")
@@ -95,7 +94,7 @@ async def main():
# Restore to checkpoint 2
print(f"\n--- Restoring to Checkpoint: {result2.checkpoint.id[:8]} ---")
await agent.checkpoints.restore(result2.checkpoint.id)
await checkpoint_service.restore(result2.checkpoint.id)
print("✅ Restored successfully!")
# Verify restoration
@@ -28,7 +28,6 @@ def main():
name="CodeAssistant",
instructions="You are a helpful code assistant.",
context=True, # Enable context management with defaults
verbose=False
)
print("\n✓ Agent created with context management enabled")
+26
View File
@@ -0,0 +1,26 @@
"""
RouteLLM Integration Example
Route requests between strong and weak models to optimize cost.
Prerequisites:
1. pip install routellm
2. export OPENAI_API_KEY=your-api-key
3. Start RouteLLM server:
python -m routellm.openai_server \
--routers mf \
--strong-model gpt-4o \
--weak-model gpt-4o-mini \
--port 6060
"""
from praisonaiagents import Agent
# Basic usage with RouteLLM routing
agent = Agent(
llm="router-mf-0.5",
base_url="http://localhost:6060/v1"
)
response = agent.chat("What is 2+2?")
print(response)
+49
View File
@@ -0,0 +1,49 @@
"""
RouteLLM Multi-Agent Example
Prerequisites:
1. pip install routellm
2. export OPENAI_API_KEY=your-api-key
3. Start RouteLLM server:
python -m routellm.openai_server \
--routers mf \
--strong-model gpt-4o \
--weak-model gpt-4o-mini \
--port 6060
"""
from praisonaiagents import Agent, PraisonAIAgents, Task
ROUTELLM_URL = "http://localhost:6060/v1"
researcher = Agent(
name="Researcher",
role="Research analyst",
goal="Find and analyze information",
llm="router-mf-0.5",
base_url=ROUTELLM_URL
)
writer = Agent(
name="Writer",
role="Content writer",
goal="Write clear and engaging content",
llm="router-mf-0.5",
base_url=ROUTELLM_URL
)
task1 = Task(
description="Research the latest AI trends in 2024",
agent=researcher,
expected_output="Research summary with key findings"
)
task2 = Task(
description="Write a brief article based on the research",
agent=writer,
expected_output="Short article about AI trends"
)
agents = PraisonAIAgents(agents=[researcher, writer], tasks=[task1, task2])
result = agents.start()
print(result)
+40
View File
@@ -0,0 +1,40 @@
"""
RouteLLM Workflow Example
Prerequisites:
1. pip install routellm
2. export OPENAI_API_KEY=your-api-key
3. Start RouteLLM server:
python -m routellm.openai_server \
--routers mf \
--strong-model gpt-4o \
--weak-model gpt-4o-mini \
--port 6060
"""
from praisonaiagents import Agent, Workflow
ROUTELLM_URL = "http://localhost:6060/v1"
workflow = Workflow(
name="Analysis Pipeline",
steps=[
Agent(
name="Analyzer",
role="Data analyst",
goal="Analyze data and extract insights",
llm="router-mf-0.5",
base_url=ROUTELLM_URL
),
Agent(
name="Reporter",
role="Report writer",
goal="Create clear reports from analysis",
llm="router-mf-0.5",
base_url=ROUTELLM_URL
)
]
)
result = workflow.run("Analyze the impact of AI on software development")
print(result["output"])
+3 -9
View File
@@ -1,21 +1,15 @@
#!/usr/bin/env python3
from praisonaiagents import Agent, force_shutdown_telemetry
from praisonaiagents import Agent
print("Creating agent...")
agent = Agent(
instructions="You are a helpful assistant",
llm="gpt-5-nano"
llm="gpt-4o-mini"
)
print("Starting agent...")
result = agent.start("Why is the sky blue?")
print("Agent completed successfully!")
print(f"Result: {result}")
# Optional: Clean up telemetry system when script is completely done
print("Cleaning up telemetry...")
force_shutdown_telemetry()
print("Script finished normally.")
print(f"Result: {result}")
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "praisonaiagents"
version = "0.10.1"
version = "0.10.2"
description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
readme = "README.md"
requires-python = ">=3.10"
+5 -4
View File
@@ -2,9 +2,10 @@ from praisonaiagents import Agent
agent = Agent(
instructions="You are a helpful assistant",
llm="gemini/gemini-2.5-flash",
stream=True
llm="gemini/gemini-2.5-flash"
)
for chunk in agent.start("Write a report on about the history of the world"):
print(chunk, end="", flush=True)
# Use chat method with stream=True for streaming responses
for chunk in agent.chat("Write a short paragraph about the history of computing", stream=True):
print(chunk, end="", flush=True)
print() # Final newline
+5 -4
View File
@@ -2,9 +2,10 @@ from praisonaiagents import Agent
agent = Agent(
instructions="You are a helpful assistant",
llm="gpt-5-nano",
stream=True
llm="gpt-4o-mini"
)
for chunk in agent.start("Write a report on about the history of the world"):
print(chunk, end="", flush=True)
# Use chat method with stream=True for streaming responses
for chunk in agent.chat("Write a short paragraph about the history of computing", stream=True):
print(chunk, end="", flush=True)
print() # Final newline
+1 -1
View File
@@ -2422,7 +2422,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.10.1"
version = "0.10.2"
source = { editable = "." }
dependencies = [
{ name = "aiohttp" },
+25 -47
View File
@@ -1,49 +1,27 @@
# Movie Script Workflow - Cat in Mars
# Uses the modern agents + steps format
name: Create Movie Script About Cat in Mars
description: A sequential workflow to research, design narrative, and write a movie script
framework: praisonai
workflow:
verbose: true
agents:
researcher:
name: Researcher
role: Research Analyst
goal: Gather information about Mars and cats
backstory: Skilled in research, with a focus on gathering accurate and relevant information.
instructions: |
Research about Mars, its environment, and the feasibility of a cat being on Mars.
Also, research about cat behavior and characteristics.
topic: --help
roles:
file_reader:
backstory: A diligent agent skilled in parsing and extracting data from various
file types.
goal: Extract relevant information from the input file
role: File Reader
tasks:
read_task:
description: Read the contents of a specified file to retrieve necessary information.
expected_output: Extracted content from the file ready for processing.
tools:
- search_tool
narrative_designer:
name: Narrative Designer
role: Story Concept Developer
goal: Create a story concept for a movie about a cat in Mars
backstory: Skilled in narrative development, with a focus on creating engaging stories.
instructions: |
Use the research findings to craft a unique story concept for a movie about a cat in Mars.
Create narrative arcs, character bios, and settings.
scriptwriter:
name: Scriptwriter
role: Script Writer
goal: Write a movie script about a cat in Mars
backstory: Expert in dialogue and script structure, translating concepts into scripts.
instructions: |
Turn the story concept into a production-ready movie script,
including dialogue and scene details.
steps:
- agent: researcher
action: "Research about Mars environment and cat behavior for the movie concept"
- agent: narrative_designer
action: "Create story concept based on research: {{previous_output}}"
- agent: scriptwriter
action: "Write the final movie script based on the story concept: {{previous_output}}"
- read_file
command_executor:
backstory: An experienced agent with expertise in command execution and automation.
goal: Execute commands based on the information gathered
role: Command Executor
tasks:
execute_task:
description: Take the extracted information and execute relevant commands,
logging outputs to a file.
expected_output: Execution results written to the output file.
tools:
- execute_command
- write_file
dependencies: []
+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/v3.5.2.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.5.2.tar.gz | shasum -a 256`.split.first
url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.5.3.tar.gz"
sha256 `curl -sL https://github.com/MervinPraison/PraisonAI/archive/refs/tags/v3.5.3.tar.gz | shasum -a 256`.split.first
license "MIT"
depends_on "[email protected]"
+64 -38
View File
@@ -327,6 +327,24 @@ class AgentsGenerator:
# Route to YAMLWorkflowParser for advanced workflow patterns
return self._run_yaml_workflow(config)
# Canonical format conversion: 'agents' -> 'roles', 'instructions' -> 'backstory'
# This ensures backward compatibility while supporting the new canonical format
if 'agents' in config and 'roles' not in config:
config['roles'] = {}
for agent_name, agent_config in config['agents'].items():
role_config = dict(agent_config) if agent_config else {}
# Convert 'instructions' to 'backstory' if present
if 'instructions' in role_config and 'backstory' not in role_config:
role_config['backstory'] = role_config.pop('instructions')
# Ensure required fields have defaults
if 'role' not in role_config:
role_config['role'] = agent_name.replace('_', ' ').title()
if 'goal' not in role_config:
role_config['goal'] = role_config.get('backstory', 'Complete the assigned task')
if 'backstory' not in role_config:
role_config['backstory'] = f'You are a {role_config["role"]}'
config['roles'][agent_name] = role_config
# Get workflow input: 'input' is canonical, 'topic' is alias for backward compatibility
topic = config.get('input', config.get('topic', ''))
tools_dict = {}
@@ -805,26 +823,21 @@ class AgentsGenerator:
backstory_filled = details['backstory'].format(topic=topic)
# Pass all loaded tools to the agent
# Get LLM from config or environment
llm_config = details.get('llm', {})
llm_model = llm_config.get("model") if isinstance(llm_config, dict) else llm_config
llm_model = llm_model or os.environ.get("MODEL_NAME") or "gpt-4o-mini"
agent = PraisonAgent(
name=role_filled,
role=role_filled,
goal=goal_filled,
backstory=backstory_filled,
instructions=details.get('instructions'),
tools=tools_list, # Pass the entire tools list to the agent
allow_delegation=details.get('allow_delegation', False),
llm=details.get('llm', {}).get("model") or os.environ.get("MODEL_NAME") or "openai/gpt-5-nano",
function_calling_llm=details.get('function_calling_llm', {}).get("model") or os.environ.get("MODEL_NAME") or "openai/gpt-5-nano",
max_iter=details.get('max_iter', 15),
max_rpm=details.get('max_rpm'),
max_execution_time=details.get('max_execution_time'),
verbose=details.get('verbose', True),
cache=details.get('cache', True),
system_template=details.get('system_template'),
prompt_template=details.get('prompt_template'),
response_template=details.get('response_template'),
reflect_llm=details.get('reflect_llm', {}).get("model") or os.environ.get("MODEL_NAME") or "openai/gpt-5-nano",
min_reflect=details.get('min_reflect', 1),
max_reflect=details.get('max_reflect', 3),
llm=llm_model,
reflection=details.get('reflection', False),
)
if self.agent_callback:
@@ -834,32 +847,47 @@ class AgentsGenerator:
self.logger.debug(f"Created agent {role_filled} with tools: {agent.tools}")
# Create tasks for the agent
for task_name, task_details in details.get('tasks', {}).items():
description_filled = task_details['description'].format(topic=topic)
expected_output_filled = task_details['expected_output'].format(topic=topic)
task = PraisonTask(
description=description_filled,
expected_output=expected_output_filled,
agent_tasks = details.get('tasks', {})
# If no tasks defined, auto-generate one from instructions/backstory
if not agent_tasks:
# Use instructions or backstory as the task description
task_description = details.get('instructions') or backstory_filled
auto_task = PraisonTask(
description=task_description,
expected_output="Complete the assigned task successfully.",
agent=agent,
tools=tools_list, # Pass the same tools list to the task
async_execution=task_details.get('async_execution', False),
context=[],
config=task_details.get('config', {}),
output_json=task_details.get('output_json'),
output_pydantic=task_details.get('output_pydantic'),
output_file=task_details.get('output_file', ""),
callback=task_details.get('callback'),
create_directory=task_details.get('create_directory', False)
)
tasks.append(auto_task)
tasks_dict[f"{role}_auto_task"] = auto_task
self.logger.debug(f"Auto-generated task for agent {role_filled}")
else:
for task_name, task_details in agent_tasks.items():
description_filled = task_details['description'].format(topic=topic)
expected_output_filled = task_details['expected_output'].format(topic=topic)
self.logger.debug(f"Created task {task_name} with tools: {task.tools}")
if self.task_callback:
task.callback = self.task_callback
task = PraisonTask(
description=description_filled,
expected_output=expected_output_filled,
agent=agent,
tools=tools_list, # Pass the same tools list to the task
async_execution=task_details.get('async_execution', False),
context=[],
config=task_details.get('config', {}),
output_json=task_details.get('output_json'),
output_pydantic=task_details.get('output_pydantic'),
output_file=task_details.get('output_file', ""),
callback=task_details.get('callback'),
create_directory=task_details.get('create_directory', False)
)
tasks.append(task)
tasks_dict[task_name] = task
self.logger.debug(f"Created task {task_name} with tools: {task.tools}")
if self.task_callback:
task.callback = self.task_callback
tasks.append(task)
tasks_dict[task_name] = task
# Set up task contexts
for role, details in config['roles'].items():
@@ -876,16 +904,14 @@ class AgentsGenerator:
agents = Agents(
agents=list(agents.values()),
tasks=tasks,
verbose=True,
process="hierarchical",
manager_llm=config.get('manager_llm') or os.environ.get("MODEL_NAME") or "openai/gpt-5-nano",
manager_llm=config.get('manager_llm') or os.environ.get("MODEL_NAME") or "gpt-4o-mini",
memory=memory
)
else:
agents = Agents(
agents=list(agents.values()),
tasks=tasks,
verbose=True,
memory=memory
)
+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==3.5.2 gunicorn markdown\n")
file.write("RUN pip install flask praisonai==3.5.3 gunicorn markdown\n")
file.write("EXPOSE 8080\n")
file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n')
@@ -6,22 +6,43 @@ CREWAI_AVAILABLE = importlib.util.find_spec("crewai") is not None
AUTOGEN_AVAILABLE = importlib.util.find_spec("autogen") is not None
PRAISONAI_TOOLS_AVAILABLE = False
# Guard against recursive imports
_importing_autogen_tools = False
_autogen_tools_module = None
# Lazy import helper - only imports when actually needed
def _get_autogen_tools():
"""Lazy import autogen_tools only when needed."""
global PRAISONAI_TOOLS_AVAILABLE
global PRAISONAI_TOOLS_AVAILABLE, _importing_autogen_tools, _autogen_tools_module
# Return cached module if already imported
if _autogen_tools_module is not None:
return _autogen_tools_module
# Prevent recursive import
if _importing_autogen_tools:
return None
if CREWAI_AVAILABLE or AUTOGEN_AVAILABLE:
try:
_importing_autogen_tools = True
from . import autogen_tools
_autogen_tools_module = autogen_tools
PRAISONAI_TOOLS_AVAILABLE = True
return autogen_tools
except ImportError:
pass
finally:
_importing_autogen_tools = False
return None
# For backward compatibility, provide __getattr__ for lazy access
def __getattr__(name):
"""Lazy load autogen_tools exports on demand."""
# Avoid recursion during import
if _importing_autogen_tools:
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
tools_module = _get_autogen_tools()
if tools_module and hasattr(tools_module, name):
return getattr(tools_module, name)
+1 -1
View File
@@ -1 +1 @@
__version__ = "3.5.2"
__version__ = "3.5.3"
+1 -1
View File
@@ -12,7 +12,7 @@ dependencies = [
"rich>=13.7",
"markdown>=3.5",
"pyparsing>=3.0.0",
"praisonaiagents>=0.10.1",
"praisonaiagents>=0.10.2",
"python-dotenv>=0.19.0",
"instructor>=1.3.3",
"PyYAML>=6.0",
+4 -4
View File
@@ -4276,7 +4276,7 @@ requires-dist = [
{ name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.1.0" },
{ name = "praisonai-tools", marker = "extra == 'autogen-v4'", specifier = ">=0.1.0" },
{ name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.1.0" },
{ name = "praisonaiagents", specifier = ">=0.10.1" },
{ name = "praisonaiagents", specifier = ">=0.10.2" },
{ 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" },
@@ -4324,7 +4324,7 @@ wheels = [
[[package]]
name = "praisonaiagents"
version = "0.10.1"
version = "0.10.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "aiohttp" },
@@ -4333,9 +4333,9 @@ dependencies = [
{ name = "pydantic" },
{ name = "rich" },
]
sdist = { url = "https://files.pythonhosted.org/packages/02/20/d7c02b4dc2da4c4427046b60286858288c44c5cae25ad08c3fc0727f8ebb/praisonaiagents-0.10.1.tar.gz", hash = "sha256:6d21ce2cbf5f0a4f56798f731a79d703536e1dc8706f1b00915d772e4d4084ea", size = 945938, upload-time = "2026-01-08T21:08:39.706Z" }
sdist = { url = "https://files.pythonhosted.org/packages/61/08/2829365c7cbb70fb7a4397ae39176785d346cd26fd5789657a2859d03204/praisonaiagents-0.10.2.tar.gz", hash = "sha256:ca62cfdcffc7702b66e205ad1ea82363707db9e06bad8818b8b4d1de875a447c", size = 945964, upload-time = "2026-01-08T21:47:37.191Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/53/d6/b381217a998847756ad9b423fc0cb0c2134e09971abfd333698abf809fae/praisonaiagents-0.10.1-py3-none-any.whl", hash = "sha256:6202ed7a3a46d299719a8b0470b48fcbac9f0989ac9fc19406065749ca5f6527", size = 923192, upload-time = "2026-01-08T21:08:37.808Z" },
{ url = "https://files.pythonhosted.org/packages/27/58/791f31cb690c70ac507a393729530d83eee6af2fc5a3f55651bfbf40f7b9/praisonaiagents-0.10.2-py3-none-any.whl", hash = "sha256:0a5b1ce58a08f5cc377c6175c78e15925edce9a30d57e28c7f7cc50a64d61a3b", size = 923193, upload-time = "2026-01-08T21:47:35.032Z" },
]
[[package]]