From ce56768c8fde6f89266369cdb055f2a0e4d6e04c Mon Sep 17 00:00:00 2001 From: MervinPraison Date: Mon, 13 Jan 2025 06:36:27 +0000 Subject: [PATCH 1/2] Update praisonaiagents to version 0.0.33 and enhance agent functionality - Bumped the version of the praisonaiagents package from 0.0.32 to 0.0.33 in pyproject.toml and related files. - Improved tool availability handling by ensuring tools from both tasks and agents are combined for better execution. - Enhanced parallel task execution logic to efficiently manage asynchronous tasks within workflows. - Updated callback execution to handle running event loops more gracefully, improving overall stability and error handling. - Adjusted dependencies in uv.lock for compatibility with the new version. --- agents/autonomous-agent.py | 59 +++++++++++++ agents/evaluator-optimizer.py | 57 ++++++++++++ agents/orchestrator-workers.py | 110 ++++++++++++++++++++++++ agents/parallelization.py | 100 +++++++++++++++++++++ agents/praisonaiagents/agents/agents.py | 46 ++++++++-- agents/pyproject.toml | 2 +- agents/uv.lock | 2 +- 7 files changed, 365 insertions(+), 11 deletions(-) create mode 100644 agents/autonomous-agent.py create mode 100644 agents/evaluator-optimizer.py create mode 100644 agents/orchestrator-workers.py create mode 100644 agents/parallelization.py diff --git a/agents/autonomous-agent.py b/agents/autonomous-agent.py new file mode 100644 index 0000000..43f7221 --- /dev/null +++ b/agents/autonomous-agent.py @@ -0,0 +1,59 @@ +from praisonaiagents import Agent, Task, PraisonAIAgents +import time + +def get_environment_feedback(): + """Simulates environment feedback based on time""" + current_time = int(time.time()) + feedback = "positive" if current_time % 2 == 0 else "negative" + print(f"Environment feedback: {feedback}") + return feedback + +# Create autonomous agent +autonomous_agent = Agent( + name="Autonomous Agent", + role="Environment interactor", + goal="Interact with environment and learn from feedback", + instructions="Take actions based on environment state and adapt from feedback", + tools=[get_environment_feedback] +) + +# Create tasks for the autonomous loop +action_task = Task( + name="take_action", + description="Analyze environment and take appropriate action", + expected_output="Action taken and its rationale", + agent=autonomous_agent, + is_start=True, + task_type="decision", + next_tasks=["process_feedback"], + condition={ + "continue": ["process_feedback"], # Continue to get feedback + "stop": [""] # Stop when goal is achieved + } +) + +feedback_task = Task( + name="process_feedback", + description="Process environment feedback and adapt strategy", + expected_output="Adapted strategy based on feedback", + agent=autonomous_agent, + next_tasks=["take_action"], + context=[action_task] # Access to previous action for learning +) + +# Create workflow manager +workflow = PraisonAIAgents( + agents=[autonomous_agent], + tasks=[action_task, feedback_task], + process="workflow", + verbose=True +) + +# Run autonomous workflow +results = workflow.start() + +# Print results +print("\nAutonomous Agent Results:") +for task_id, result in results["task_results"].items(): + if result: + print(f"Task {task_id}: {result.raw}") diff --git a/agents/evaluator-optimizer.py b/agents/evaluator-optimizer.py new file mode 100644 index 0000000..645b16e --- /dev/null +++ b/agents/evaluator-optimizer.py @@ -0,0 +1,57 @@ +from praisonaiagents import Agent, Task, PraisonAIAgents + +# Create generator and evaluator agents +generator = Agent( + name="Generator", + role="Solution generator", + goal="Generate initial solutions and incorporate feedback", + instructions="Generate solutions and refine based on evaluator feedback" +) + +evaluator = Agent( + name="Evaluator", + role="Solution evaluator", + goal="Evaluate solutions and provide improvement feedback", + instructions="Evaluate solutions for accuracy and provide specific feedback for improvements" +) + +# Create tasks for the feedback loop +generate_task = Task( + name="generate", + description="Generate or refine solution based on feedback", + expected_output="Generated solution", + agent=generator, + is_start=True, + task_type="decision", + next_tasks=["evaluate"], + condition={ + "more": ["evaluate"], # Continue to evaluation + "done": [""] # Exit when optimization complete + } +) + +evaluate_task = Task( + name="evaluate", + description="Evaluate solution and provide feedback", + expected_output="Evaluation score and feedback", + agent=evaluator, + next_tasks=["generate"], + context=[generate_task] # Access to previous generation for evaluation +) + +# Create workflow manager +workflow = PraisonAIAgents( + agents=[generator, evaluator], + tasks=[generate_task, evaluate_task], + process="workflow", + verbose=True +) + +# Run optimization workflow +results = workflow.start() + +# Print results +print("\nEvaluator-Optimizer Results:") +for task_id, result in results["task_results"].items(): + if result: + print(f"Task {task_id}: {result.raw}") diff --git a/agents/orchestrator-workers.py b/agents/orchestrator-workers.py new file mode 100644 index 0000000..45b66ad --- /dev/null +++ b/agents/orchestrator-workers.py @@ -0,0 +1,110 @@ +from praisonaiagents import Agent, Task, PraisonAIAgents +import time + +def get_time_check(): + current_time = int(time.time()) + if current_time % 3 == 0: + result = 1 + elif current_time % 3 == 1: + result = 2 + else: + result = 3 + print(f"Time: {current_time}, Result: {result}") + return result + +# Create orchestrator and worker agents +router = Agent( + name="Router", + role="Task router", + goal="Distribute tasks to based on response from get_time_check", + tools=[get_time_check] +) + +worker1 = Agent( + name="Worker 1", + role="Specialized worker", + goal="Handle specific subtask type 1", +) + +worker2 = Agent( + name="Worker 2", + role="Specialized worker", + goal="Handle specific subtask type 2", +) + +worker3 = Agent( + name="Worker 3", + role="Specialized worker", + goal="Handle specific subtask type 3", +) + +synthesizer = Agent( + name="Synthesizer", + role="Result synthesizer", + goal="Combine and synthesize worker outputs", +) + +# Create orchestrated workflow tasks +router_task = Task( + name="route_task", + description="Analyze input from get_time_check and route to appropriate workers", + expected_output="Task routing decision, 1 , 2 or 3", + agent=router, + is_start=True, + task_type="decision", + next_tasks=["worker1_task", "worker2_task", "worker3_task"], + condition={ + "1": ["worker1_task"], + "2": ["worker2_task"], + "3": ["worker3_task"] + } +) + +worker1_task = Task( + name="worker1_task", + description="Process type 1 operation", + expected_output="Worker 1 result", + agent=worker1, + next_tasks=["synthesize_task"] +) + +worker2_task = Task( + name="worker2_task", + description="Process type 2 operation", + expected_output="Worker 2 result", + agent=worker2, + next_tasks=["synthesize_task"] +) + +worker3_task = Task( + name="worker3_task", + description="Process type 3 operation", + expected_output="Worker 3 result", + agent=worker3, + next_tasks=["synthesize_task"] +) + +synthesize_task = Task( + name="synthesize_task", + description="Synthesize worker results into final output", + expected_output="Final synthesized result", + agent=synthesizer, + context=[worker1_task, worker2_task, worker3_task] +) + +# Create workflow manager +workflow = PraisonAIAgents( + agents=[router, worker1, worker2, worker3, synthesizer], + tasks=[router_task, worker1_task, worker2_task, worker3_task, synthesize_task], + process="workflow", + verbose=True +) + +# Run orchestrated workflow +results = workflow.start() + +# Print results +print("\nOrchestrator-Workers Results:") +for task_id, result in results["task_results"].items(): + if result: + print(f"Task {task_id}: {result.raw}") diff --git a/agents/parallelization.py b/agents/parallelization.py new file mode 100644 index 0000000..7231a40 --- /dev/null +++ b/agents/parallelization.py @@ -0,0 +1,100 @@ +from praisonaiagents import Agent, Task, PraisonAIAgents +from datetime import datetime +import asyncio + +def process_time(): + """Simulate processing""" + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + print(f"Processing at: {current_time}") + return f"Processed at {current_time}" + +# Create parallel processing agents +agent1 = Agent( + name="Processor 1", + role="Time collector", + goal="Get the time and return it", + tools=[process_time], + verbose=0 +) + +agent2 = Agent( + name="Processor 2", + role="Time collector", + goal="Get the time and return it", + tools=[process_time], + verbose=0 +) + +agent3 = Agent( + name="Processor 3", + role="Time collector", + goal="Get the time and return it", + tools=[process_time], + verbose=0 +) + +aggregator = Agent( + name="Aggregator", + role="Result aggregator", + goal="Collect all the processed time from all tasks", + verbose=0 +) + +# Create parallel tasks with memory disabled +task1 = Task( + name="process_1", + description="Use process_time tool to get the time", + expected_output="processed time", + agent=agent1, + is_start=True, + async_execution=True +) + +task2 = Task( + name="process_2", + description="Use process_time tool to get the time", + expected_output="processed time", + agent=agent2, + is_start=True, + async_execution=True +) + +task3 = Task( + name="process_3", + description="Use process_time tool to get the time", + expected_output="processed time", + agent=agent3, + is_start=True, + async_execution=True +) + +aggregate_task = Task( + name="aggregate", + description="Collect all the processed time from all tasks", + expected_output="Output all the processed time from all tasks and just the time", + agent=aggregator, + context=[task1, task2, task3] +) + +async def main(): + + # Create workflow manager + workflow = PraisonAIAgents( + agents=[agent1, agent2, agent3, aggregator], + tasks=[task1, task2, task3, aggregate_task], + process="workflow", + verbose=0 + ) + + # Run parallel workflow + results = await workflow.astart() + + # Print results + print("\nParallel Processing Results:") + for task_id, result in results["task_results"].items(): + if result: + print(f"Task {task_id}: {result.raw}") + +# Run the async main function +if __name__ == "__main__": + asyncio.run(main()) diff --git a/agents/praisonaiagents/agents/agents.py b/agents/praisonaiagents/agents/agents.py index aae664f..57e9f68 100644 --- a/agents/praisonaiagents/agents/agents.py +++ b/agents/praisonaiagents/agents/agents.py @@ -211,6 +211,11 @@ async def aexecute_task(self, task_id): executor_agent = task.agent + # Ensure tools are available from both task and agent + tools = task.tools or [] + if executor_agent and executor_agent.tools: + tools.extend(executor_agent.tools) + task_prompt = f""" You need to do the following task: {task.description}. Expected Output: {task.expected_output}. @@ -267,14 +272,14 @@ def _get_multimodal_message(text_prompt, images): agent_output = await executor_agent.achat( _get_multimodal_message(task_prompt, task.images), - tools=task.tools, + tools=tools, output_json=task.output_json, output_pydantic=task.output_pydantic ) else: agent_output = await executor_agent.achat( task_prompt, - tools=task.tools, + tools=tools, output_json=task.output_json, output_pydantic=task.output_pydantic ) @@ -380,11 +385,25 @@ async def arun_all_tasks(self): ) if self.process == "workflow": + # Collect all tasks that should run in parallel + parallel_tasks = [] async for task_id in process.aworkflow(): - if self.tasks[task_id].async_execution: - await self.arun_task(task_id) - else: - self.run_task(task_id) + if self.tasks[task_id].async_execution and self.tasks[task_id].is_start: + parallel_tasks.append(task_id) + elif parallel_tasks: + # Execute collected parallel tasks + await asyncio.gather(*[self.arun_task(t) for t in parallel_tasks]) + parallel_tasks = [] + # Run the current non-parallel task + if self.tasks[task_id].async_execution: + await self.arun_task(task_id) + else: + self.run_task(task_id) + + # Execute any remaining parallel tasks + if parallel_tasks: + await asyncio.gather(*[self.arun_task(t) for t in parallel_tasks]) + elif self.process == "sequential": async for task_id in process.asequential(): if self.tasks[task_id].async_execution: @@ -596,8 +615,12 @@ def run_task(self, task_id): task.status = "completed" # Run execute_callback for memory operations try: - loop = asyncio.get_event_loop() - loop.run_until_complete(task.execute_callback(task_output)) + if asyncio.get_event_loop().is_running(): + asyncio.create_task(task.execute_callback(task_output)) + else: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(task.execute_callback(task_output)) except Exception as e: logger.error(f"Error executing memory callback for task {task_id}: {e}") logger.exception(e) @@ -606,7 +629,12 @@ def run_task(self, task_id): if task.callback: try: if asyncio.iscoroutinefunction(task.callback): - loop.run_until_complete(task.callback(task_output)) + if asyncio.get_event_loop().is_running(): + asyncio.create_task(task.callback(task_output)) + else: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + loop.run_until_complete(task.callback(task_output)) else: task.callback(task_output) except Exception as e: diff --git a/agents/pyproject.toml b/agents/pyproject.toml index ac5b131..76f1233 100644 --- a/agents/pyproject.toml +++ b/agents/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "praisonaiagents" -version = "0.0.32" +version = "0.0.33" description = "Praison AI agents for completing complex tasks with Self Reflection Agents" authors = [ { name="Mervin Praison" } diff --git a/agents/uv.lock b/agents/uv.lock index eedf77e..5d5e7b8 100644 --- a/agents/uv.lock +++ b/agents/uv.lock @@ -990,7 +990,7 @@ wheels = [ [[package]] name = "praisonaiagents" -version = "0.0.32" +version = "0.0.33" source = { editable = "." } dependencies = [ { name = "openai" }, From d6f35c605dc32aef9e91a2c3ca77f9ca4c5012ac Mon Sep 17 00:00:00 2001 From: MervinPraison Date: Mon, 13 Jan 2025 07:33:15 +0000 Subject: [PATCH 2/2] Update PraisonAI to version 2.0.43 and praisonaiagents to version 0.0.33 - Bumped the PraisonAI package version from 2.0.42 to 2.0.43 in Dockerfile, pyproject.toml, and related files. - Updated the installation command in Dockerfile and deployment scripts to reflect the new version. - Updated the praisonaiagents package version from 0.0.32 to 0.0.33 in relevant files. - Modified task descriptions in evaluator-optimizer.py to enhance clarity and functionality. - Adjusted documentation to ensure consistency with the new version. - Updated dependencies in uv.lock for compatibility with the new versions. --- Dockerfile | 2 +- agents/evaluator-optimizer.py | 21 +++++++++++---------- docs/api/praisonai/deploy.html | 2 +- praisonai.rb | 2 +- praisonai/deploy.py | 2 +- pyproject.toml | 8 ++++---- uv.lock | 10 +++++----- 7 files changed, 24 insertions(+), 23 deletions(-) diff --git a/Dockerfile b/Dockerfile index 534c3c2..140efcb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.11-slim WORKDIR /app COPY . . -RUN pip install flask praisonai==2.0.42 gunicorn markdown +RUN pip install flask praisonai==2.0.43 gunicorn markdown EXPOSE 8080 CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"] diff --git a/agents/evaluator-optimizer.py b/agents/evaluator-optimizer.py index 645b16e..2464e6c 100644 --- a/agents/evaluator-optimizer.py +++ b/agents/evaluator-optimizer.py @@ -18,25 +18,26 @@ # Create tasks for the feedback loop generate_task = Task( name="generate", - description="Generate or refine solution based on feedback", - expected_output="Generated solution", + description="Write 5 points about AI", + expected_output="5 points", agent=generator, is_start=True, task_type="decision", - next_tasks=["evaluate"], - condition={ - "more": ["evaluate"], # Continue to evaluation - "done": [""] # Exit when optimization complete - } + next_tasks=["evaluate"] ) evaluate_task = Task( name="evaluate", - description="Evaluate solution and provide feedback", - expected_output="Evaluation score and feedback", + description="Check if there are 10 points about AI", + expected_output="more or done", agent=evaluator, next_tasks=["generate"], - context=[generate_task] # Access to previous generation for evaluation + context=[generate_task], + task_type="decision", + condition={ + "more": ["generate"], # Continue to generate + "done": [""] # Exit when optimization complete + } ) # Create workflow manager diff --git a/docs/api/praisonai/deploy.html b/docs/api/praisonai/deploy.html index 6417215..a533414 100644 --- a/docs/api/praisonai/deploy.html +++ b/docs/api/praisonai/deploy.html @@ -110,7 +110,7 @@

Raises

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.0.42 gunicorn markdown\n") + file.write("RUN pip install flask praisonai==2.0.43 gunicorn markdown\n") file.write("EXPOSE 8080\n") file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n') diff --git a/praisonai.rb b/praisonai.rb index 7c1b7d2..84a5871 100644 --- a/praisonai.rb +++ b/praisonai.rb @@ -3,7 +3,7 @@ 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/2.0.42.tar.gz" + url "https://github.com/MervinPraison/PraisonAI/archive/refs/tags/2.0.43.tar.gz" sha256 "1828fb9227d10f991522c3f24f061943a254b667196b40b1a3e4a54a8d30ce32" # Replace with actual SHA256 checksum license "MIT" diff --git a/praisonai/deploy.py b/praisonai/deploy.py index 437904e..36eb9b8 100644 --- a/praisonai/deploy.py +++ b/praisonai/deploy.py @@ -56,7 +56,7 @@ def create_dockerfile(self): 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.0.42 gunicorn markdown\n") + file.write("RUN pip install flask praisonai==2.0.43 gunicorn markdown\n") file.write("EXPOSE 8080\n") file.write('CMD ["gunicorn", "-b", "0.0.0.0:8080", "api:app"]\n') diff --git a/pyproject.toml b/pyproject.toml index 203e909..6fd9e25 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "PraisonAI" -version = "2.0.42" +version = "2.0.43" 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.32", + "praisonaiagents>=0.0.33", "python-dotenv>=0.19.0", "instructor>=1.3.3", "PyYAML>=6.0", @@ -84,7 +84,7 @@ autogen = ["pyautogen>=0.2.19", "praisonai-tools>=0.0.7", "crewai"] [tool.poetry] name = "PraisonAI" -version = "2.0.42" +version = "2.0.43" 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 = "" @@ -102,7 +102,7 @@ python = ">=3.10,<3.13" rich = ">=13.7" markdown = ">=3.5" pyparsing = ">=3.0.0" -praisonaiagents = ">=0.0.32" +praisonaiagents = ">=0.0.33" python-dotenv = ">=0.19.0" instructor = ">=1.3.3" PyYAML = ">=6.0" diff --git a/uv.lock b/uv.lock index dc65e7f..efb1118 100644 --- a/uv.lock +++ b/uv.lock @@ -3060,7 +3060,7 @@ wheels = [ [[package]] name = "praisonai" -version = "2.0.42" +version = "2.0.43" source = { editable = "." } dependencies = [ { name = "instructor" }, @@ -3197,7 +3197,7 @@ requires-dist = [ { name = "plotly", marker = "extra == 'realtime'", specifier = ">=5.24.0" }, { name = "praisonai-tools", marker = "extra == 'autogen'", specifier = ">=0.0.7" }, { name = "praisonai-tools", marker = "extra == 'crewai'", specifier = ">=0.0.7" }, - { name = "praisonaiagents", specifier = ">=0.0.32" }, + { name = "praisonaiagents", specifier = ">=0.0.33" }, { 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" }, @@ -3250,16 +3250,16 @@ wheels = [ [[package]] name = "praisonaiagents" -version = "0.0.32" +version = "0.0.33" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "openai" }, { name = "pydantic" }, { name = "rich" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/96/b7/3de3e4cff09a2b21f7b8c1555a7f3de8e4a00ff99e63bce778fe9429dc23/praisonaiagents-0.0.32.tar.gz", hash = "sha256:466f43cf9638baac3f1196870c88d02718c9a2f870fe712ee6cbbc8d6c61c6bc", size = 68653 } +sdist = { url = "https://files.pythonhosted.org/packages/f2/8a/c61b36bf73bebb89054ef83a511bcf2f43c6219e9471514109e238f77c0f/praisonaiagents-0.0.33.tar.gz", hash = "sha256:cf3df6ab44b9f7b38461e6f75e19ff792dc3d1f6a05c8854439c282a2aa701ce", size = 69163 } wheels = [ - { url = "https://files.pythonhosted.org/packages/50/a6/32e90fe57973b9ebdfcca1ff80fa0142c935517456aef8bdd8ef2118e311/praisonaiagents-0.0.32-py3-none-any.whl", hash = "sha256:f0175e2a8560d1bdc7c06b788d821422d403255fa3dd964ff5857f713e4972a1", size = 85596 }, + { url = "https://files.pythonhosted.org/packages/ce/2a/ded102807af9efb0b3e0de86a906d5a0d621eb8aab2be30f9c897aad8054/praisonaiagents-0.0.33-py3-none-any.whl", hash = "sha256:f4491ded47ed879bd39bdfa0cad6589aa69fb6a72ba0f1b1e7d0c9a997824921", size = 85889 }, ] [[package]]