Repository: MervinPraison/PraisonAI
Pull request #476: Add multi-agent API functionality in `multi-agents-api.py` and `multi…
Author: login: MervinPraison
Created: 2025-05-14T11:50:08Z | Merged: 2025-05-14T11:50:13Z

…-agents-group-api.py`, increment version to 0.0.80 in `pyproject.toml` and `uv.lock`, and enhance agent launch logic in `agents.py` with improved endpoint management and healthcheck capabilities.

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->

## Summary by CodeRabbit

- **New Features**
  - Introduced web services that allow users to interact with multi-agent systems for AI research and summarization tasks through dedicated API endpoints.
  - Users can access different agent groups via separate URLs for tailored information retrieval and summarization.
- **Enhancements**
  - Added the ability to launch agent collections as API endpoints, enabling sequential query processing and easy integration.
- **Chores**
  - Updated the project version to 0.0.80.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->


Diff:
diff --git a/src/praisonai-agents/multi-agents-api.py b/src/praisonai-agents/multi-agents-api.py
new file mode 100644
index 000000000..8f6ac2444
--- /dev/null
+++ b/src/praisonai-agents/multi-agents-api.py
@@ -0,0 +1,6 @@
+from praisonaiagents import Agent, Agents, Tools
+
+research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
+summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
+agents = Agents(agents=[research_agent, summarise_agent])
+agents.launch(path="/agents", port=3030)
\ No newline at end of file
diff --git a/src/praisonai-agents/multi-agents-group-api.py b/src/praisonai-agents/multi-agents-group-api.py
new file mode 100644
index 000000000..8888cb1cf
--- /dev/null
+++ b/src/praisonai-agents/multi-agents-group-api.py
@@ -0,0 +1,8 @@
+from praisonaiagents import Agent, Agents, Tools
+
+research_agent = Agent(name="Research", instructions="You are a research agent to search internet about AI 2024", tools=[Tools.internet_search])
+summarise_agent = Agent(name="Summarise", instructions="You are a summarize agent to summarise in points")
+agents = Agents(agents=[research_agent, summarise_agent])
+agents2 = Agents(agents=[research_agent])
+agents.launch(path="/agents", port=3030)
+agents2.launch(path="/agents2", port=3030)
\ No newline at end of file
diff --git a/src/praisonai-agents/praisonaiagents/agents/agents.py b/src/praisonai-agents/praisonaiagents/agents/agents.py
index 354a90c3e..31b86481d 100644
--- a/src/praisonai-agents/praisonaiagents/agents/agents.py
+++ b/src/praisonai-agents/praisonaiagents/agents/agents.py
@@ -17,6 +17,11 @@
 # Set up logger
 logger = logging.getLogger(__name__)
 
+# Global variables for managing the shared servers
+_agents_server_started = {}  # Dict of port -> started boolean
+_agents_registered_endpoints = {}  # Dict of port -> Dict of path -> endpoint_id
+_agents_shared_apps = {}  # Dict of port -> FastAPI app
+
 def encode_file_to_base64(file_path: str) -> str:
     """Base64-encode a file."""
     import base64
@@ -878,4 +883,230 @@ def update_state(self, updates: Dict) -> None:
 
     def clear_state(self) -> None:
         """Clear all state values"""
-        self._state.clear() 
\ No newline at end of file
+        self._state.clear()
+        
+    def launch(self, path: str = '/agents', port: int = 8000, host: str = '0.0.0.0', debug: bool = False):
+        """
+        Launch all agents as a single API endpoint. The endpoint accepts a query and processes it through
+        all agents in sequence, with the output of each agent feeding into the next.
+        
+        Args:
+            path: API endpoint path (default: '/agents')
+            port: Server port (default: 8000)
+            host: Server host (default: '0.0.0.0')
+            debug: Enable debug mode for uvicorn (default: False)
+            
+        Returns:
+            None
+        """
+        global _agents_server_started, _agents_registered_endpoints, _agents_shared_apps
+        
+        if not self.agents:
+            logging.warning("No agents to launch. Add agents to the Agents instance first.")
+            return
+            
+        # Try to import FastAPI dependencies - lazy loading
+        try:
+            import uvicorn
+            from fastapi import FastAPI, HTTPException, Request
+            from fastapi.responses import JSONResponse
+            from pydantic import BaseModel
+            import threading
+            import time
+            
+            # Define the request model here since we need pydantic
+            class AgentQuery(BaseModel):
+                query: str
+                
+        except ImportError as e:
+            # Check which specific module is missing
+            missing_module = str(e).split("No module named '")[-1].rstrip("'")
+            display_error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            logging.error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            print(f"\nTo add API capabilities, install the required dependencies:")
+            print(f"pip install {missing_module}")
+            print("\nOr install all API dependencies with:")
+            print("pip install 'praisonaiagents[api]'")
+            return None
+        
+        # Initialize port-specific collections if needed
+        if port not in _agents_registered_endpoints:
+            _agents_registered_endpoints[port] = {}
+            
+        # Initialize shared FastAPI app if not already created for this port
+        if _agents_shared_apps.get(port) is None:
+            _agents_shared_apps[port] = FastAPI(
+                title=f"PraisonAI Agents API (Port {port})",
+                description="API for interacting with multiple PraisonAI Agents"
+            )
+            
+            # Add a root endpoint with a welcome message
+            @_agents_shared_apps[port].get("/")
+            async def root():
+                return {
+                    "message": f"Welcome to PraisonAI Agents API on port {port}. See /docs for usage.",
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+            
+            # Add healthcheck endpoint
+            @_agents_shared_apps[port].get("/health")
+            async def healthcheck():
+                return {
+                    "status": "ok", 
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+        
+        # Normalize path to ensure it starts with /
+        if not path.startswith('/'):
+            path = f'/{path}'
+            
+        # Check if path is already registered for this port
+        if path in _agents_registered_endpoints[port]:
+            logging.warning(f"Path '{path}' is already registered on port {port}. Please use a different path.")
+            print(f"⚠️ Warning: Path '{path}' is already registered on port {port}.")
+            # Use a modified path to avoid conflicts
+            original_path = path
+            instance_id = str(uuid.uuid4())[:6]
+            path = f"{path}_{instance_id}"
+            logging.warning(f"Using '{path}' instead of '{original_path}'")
+            print(f"🔄 Using '{path}' instead")
+        
+        # Generate a unique ID for this agent group's endpoint
+        endpoint_id = str(uuid.uuid4())
+        _agents_registered_endpoints[port][path] = endpoint_id
+        
+        # Define the endpoint handler
+        @_agents_shared_apps[port].post(path)
+        async def handle_query(request: Request, query_data: Optional[AgentQuery] = None):
+            # Handle both direct JSON with query field and form data
+            if query_data is None:
+                try:
+                    request_data = await request.json()
+                    if "query" not in request_data:
+                        raise HTTPException(status_code=400, detail="Missing 'query' field in request")
+                    query = request_data["query"]
+                except:
+                    # Fallback to form data or query params
+                    form_data = await request.form()
+                    if "query" in form_data:
+                        query = form_data["query"]
+                    else:
+                        raise HTTPException(status_code=400, detail="Missing 'query' field in request")
+            else:
+                query = query_data.query
+            
+            try:
+                # Process the query sequentially through all agents
+                current_input = query
+                results = []
+                
+                for agent in self.agents:
+                    try:
+                        # Use async version if available, otherwise use sync version
+                        if asyncio.iscoroutinefunction(agent.chat):
+                            response = await agent.achat(current_input)
+                        else:
+                            # Run sync function in a thread to avoid blocking
+                            loop = asyncio.get_event_loop()
+                            response = await loop.run_in_executor(None, lambda: agent.chat(current_input))
+                        
+                        # Store this agent's result
+                        results.append({
+                            "agent": agent.name,
+                            "response": response
+                        })
+                        
+                        # Use this response as input to the next agent
+                        current_input = response
+                    except Exception as e:
+                        logging.error(f"Error with agent {agent.name}: {str(e)}", exc_info=True)
+                        results.append({
+                            "agent": agent.name,
+                            "error": str(e)
+                        })
+                        # Continue with original input if there's an error
+                
+                # Return all results and the final output
+                return {
+                    "query": query,
+                    "results": results,
+                    "final_response": current_input
+                }
+            except Exception as e:
+                logging.error(f"Error processing query: {str(e)}", exc_info=True)
+                return JSONResponse(
+                    status_code=500,
+                    content={"error": f"Error processing query: {str(e)}"}
+                )
+        
+        print(f"🚀 Multi-Agent API available at http://{host}:{port}{path}")
+        agent_names = ", ".join([agent.name for agent in self.agents])
+        print(f"📊 Available agents ({len(self.agents)}): {agent_names}")
+        
+        # Start the server if it's not already running for this port
+        if not _agents_server_started.get(port, False):
+            # Mark the server as started first to prevent duplicate starts
+            _agents_server_started[port] = True
+            
+            # Start the server in a separate thread
+            def run_server():
+                try:
+                    print(f"✅ FastAPI server started at http://{host}:{port}")
+                    print(f"📚 API documentation available at http://{host}:{port}/docs")
+                    print(f"🔌 Available endpoints: {', '.join(list(_agents_registered_endpoints[port].keys()))}")
+                    uvicorn.run(_agents_shared_apps[port], host=host, port=port, log_level="debug" if debug else "info")
+                except Exception as e:
+                    logging.error(f"Error starting server: {str(e)}", exc_info=True)
+                    print(f"❌ Error starting server: {str(e)}")
+            
+            # Run server in a background thread
+            server_thread = threading.Thread(target=run_server, daemon=True)
+            server_thread.start()
+            
+            # Wait for a moment to allow the server to start and register endpoints
+            time.sleep(0.5)
+        else:
+            # If server is already running, wait a moment to make sure the endpoint is registered
+            time.sleep(0.1)
+            print(f"🔌 Available endpoints on port {port}: {', '.join(list(_agents_registered_endpoints[port].keys()))}")
+        
+        # Get the stack frame to check if this is the last launch() call in the script
+        import inspect
+        stack = inspect.stack()
+        
+        # If this is called from a Python script (not interactive), try to detect if it's the last launch call
+        if len(stack) > 1 and stack[1].filename.endswith('.py'):
+            caller_frame = stack[1]
+            caller_line = caller_frame.lineno
+            
+            try:
+                # Read the file to check if there are more launch calls after this one
+                with open(caller_frame.filename, 'r') as f:
+                    lines = f.readlines()
+                
+                # Check if there are more launch() calls after the current line
+                has_more_launches = False
+                for line in lines[caller_line:]:
+                    if '.launch(' in line and not line.strip().startswith('#'):
+                        has_more_launches = True
+                        break
+                
+                # If this is the last launch call, block the main thread
+                if not has_more_launches:
+                    try:
+                        print("\nAll agents registered. Press Ctrl+C to stop the servers.")
+                        while True:
+                            time.sleep(1)
+                    except KeyboardInterrupt:
+                        print("\nServers stopped")
+            except Exception as e:
+                # If something goes wrong with detection, block anyway to be safe
+                logging.error(f"Error in launch detection: {e}")
+                try:
+                    print("\nKeeping servers alive. Press Ctrl+C to stop.")
+                    while True:
+                        time.sleep(1)
+                except KeyboardInterrupt:
+                    print("\nServers stopped")
+        
+        return None 
\ No newline at end of file
diff --git a/src/praisonai-agents/pyproject.toml b/src/praisonai-agents/pyproject.toml
index 89580a9d7..0c2b1a110 100644
--- a/src/praisonai-agents/pyproject.toml
+++ b/src/praisonai-agents/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
 
 [project]
 name = "praisonaiagents"
-version = "0.0.79"
+version = "0.0.80"
 description = "Praison AI agents for completing complex tasks with Self Reflection Agents"
 authors = [
     { name="Mervin Praison" }
diff --git a/src/praisonai-agents/uv.lock b/src/praisonai-agents/uv.lock
index 9ce250061..55b41153f 100644
--- a/src/praisonai-agents/uv.lock
+++ b/src/praisonai-agents/uv.lock
@@ -1883,7 +1883,7 @@ wheels = [
 
 [[package]]
 name = "praisonaiagents"
-version = "0.0.79"
+version = "0.0.80"
 source = { editable = "." }
 dependencies = [
     { name = "mcp" },


Reviews:
user: login: gemini-code-assist[bot]
body: Hello @MervinPraison, I'm Gemini Code Assist[^1]! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

## Summary of Changes
This pull request introduces multi-agent API functionality, enhancing the `praisonaiagents` library. It adds new API endpoints for launching and interacting with groups of agents. Specifically, it creates `multi-agents-api.py` and `multi-agents-group-api.py` to define agent configurations and launch them as API services. The `agents.py` file is modified to include improved endpoint management and health check capabilities. The version number is also incremented from 0.0.79 to 0.0.80 in `pyproject.toml` and `uv.lock`.

### Highlights
* **Multi-Agent API Creation**: Introduces `multi-agents-api.py` and `multi-agents-group-api.py` to define and launch groups of agents as API endpoints.
* **Enhanced Agent Launch Logic**: Modifies `agents.py` to include improved endpoint management, health check capabilities, and better handling of FastAPI dependencies.
* **Version Increment**: Increments the library version from 0.0.79 to 0.0.80 in `pyproject.toml` and `uv.lock`.

### Changelog
<details>
<summary>Click here to see the changelog</summary>

* **src/praisonai-agents/multi-agents-api.py**
  * Creates a new file to define and launch a research agent and a summarization agent as a single API endpoint.
  * Defines a research agent with internet search tool and a summarization agent.
  * Launches the agents on port 3030 at the /agents path.
* **src/praisonai-agents/multi-agents-group-api.py**
  * Creates a new file to define and launch multiple groups of agents as separate API endpoints.
  * Defines a research agent and a summarization agent, then creates two agent groups.
  * Launches the first group on port 3030 at the /agents path and the second group at the /agents2 path.
* **src/praisonai-agents/praisonaiagents/agents/agents.py**
  * Adds global variables to manage shared servers, registered endpoints, and shared FastAPI apps to handle multiple agent groups.
  * Introduces a `launch` method to launch agents as a single API endpoint, processing queries sequentially through all agents.
  * Includes error handling, dependency checks (FastAPI, uvicorn), and dynamic endpoint registration.
  * Adds health check endpoint at `/health`.
  * Adds root endpoint with welcome message at `/`.
  * Handles both direct JSON and form data for queries.
  * Starts the server in a separate thread to avoid blocking the main thread.
  * Detects if it's the last launch call in the script and blocks the main thread to keep the server alive.
* **src/praisonai-agents/pyproject.toml**
  * Increments the version number from 0.0.79 to 0.0.80.
* **src/praisonai-agents/uv.lock**
  * Updates the version number from 0.0.79 to 0.0.80 to match the `pyproject.toml` file.

</details>


<details>

<summary><b>Using Gemini Code Assist</b></summary>
<br>

The full guide for Gemini Code Assist can be found on our [documentation page](https://developers.google.com/gemini-code-assist/docs/review-github-code), here are some quick tips.

<b>Invoking Gemini</b>

You can request assistance from Gemini at any point in your pull request via creating an issue comment (i.e. comment on the pull request page) using either `/gemini <command>` or `@gemini-code-assist <command>`. Below is a summary of the supported commands.

Feature | Command | Description
--- | --- | ---
Code Review | `/gemini review` | Performs a code review for the current pull request in its current state.
Pull Request Summary | `/gemini summary` | Provides a summary of the current pull request in its current state.
Comment | @gemini-code-assist | Responds in comments when explicitly tagged, both in issue comments and review comments.
Help | `/gemini help` | Displays a list of available commands.

<b>Customization</b>

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a `.gemini/` folder in the base of the repository. Detailed instructions can be found [here](https://developers.google.com/gemini-code-assist/docs/customize-gemini-behavior-github).

<b>Limitations & Feedback</b>

Gemini Code Assist is currently in preview and may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with :thumbsup: and :thumbsdown: on @gemini-code-assist comments to provide feedback.

</details>

<b>You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the [Gemini Code Assist IDE Extension](https://cloud.google.com/products/gemini/code-assist).</b>


[^1]: Review the [Privacy Notices](https://policies.google.com/privacy), [Generative AI Prohibited Use Policy](https://policies.google.com/terms/generative-ai/use-policy), [Terms of Service](https://policies.google.com/terms), and learn how to configure Gemini Code Assist in GitHub [here](https://developers.google.com/gemini-code-assist/docs/review-github-code). Gemini can make mistakes, so double check it and [use code with caution](https://support.google.com/legal/answer/13505487).

---

> Agents in code, a digital dance,
> Each with a task, a focused trance.
> APIs hum, a network's grace,
> Solving problems at a rapid pace.


state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T11:50:20Z
user: login: gemini-code-assist[bot]
body: ## Code Review
The pull request introduces multi-agent API functionality, enhances agent launch logic, and increments the project version. The new `launch` method in `agents.py` is comprehensive, but there are areas where improvements can be made to enhance maintainability and error handling. The addition of new API files is a good step towards providing more accessibility to the agents.

### Summary of Findings
* **Global Variable Naming**: The global variables for managing shared servers could benefit from more descriptive names and explicit type hinting using `typing.Dict`.
* **Exception Handling**: The `except` block in the `handle_query` function is too broad and should catch specific exceptions for better error handling.
* **Path Conflict Resolution**: The path conflict resolution strategy is basic. Consider suggesting available paths or providing a configuration option for specifying a different path.
* **Agent Error Handling**: The error handling within the agent processing loop could be improved to allow for halting or retrying agents instead of always continuing with the original input.
* **Launch Detection Logic**: The logic for detecting the last `launch()` call is complex and potentially unreliable. A more explicit method for signaling completion should be considered.

### Merge Readiness
The pull request introduces valuable functionality but requires attention to error handling, naming conventions, and conflict resolution. Addressing these points will improve the code's robustness and maintainability. I am unable to directly approve this pull request, and recommend that others review and approve this code before merging. At a minimum, the HIGH severity comments should be addressed before merging.
state: CHANGES_REQUESTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-14T11:50:46Z

Review comments:
diff_hunk: @@ -17,6 +17,11 @@
 # Set up logger
 logger = logging.getLogger(__name__)
 
+# Global variables for managing the shared servers
+_agents_server_started = {}  # Dict of port -> started boolean
+_agents_registered_endpoints = {}  # Dict of port -> Dict of path -> endpoint_id
+_agents_shared_apps = {}  # Dict of port -> FastAPI app
path: src/praisonai-agents/praisonaiagents/agents/agents.py
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Consider using a more descriptive name for these global variables to improve readability and avoid potential naming conflicts. Also, consider using `typing.Dict` for type hinting to be more explicit about the types involved.[^1]

#### Style Guide References
[^1]: Use descriptive names for variables to improve readability and maintainability. Use `typing.Dict` for explicit type hinting.

```suggestion
agent_servers_started: typing.Dict[int, bool] = {}
agent_registered_endpoints: typing.Dict[int, typing.Dict[str, str]] = {}
agent_shared_apps: typing.Dict[int, FastAPI] = {}
```
created_at: 2025-05-14T11:50:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -878,4 +883,230 @@
 
     def clear_state(self) -> None:
         """Clear all state values"""
-        self._state.clear() 
+        self._state.clear()
+        
+    def launch(self, path: str = '/agents', port: int = 8000, host: str = '0.0.0.0', debug: bool = False):
+        """
+        Launch all agents as a single API endpoint. The endpoint accepts a query and processes it through
+        all agents in sequence, with the output of each agent feeding into the next.
+        
+        Args:
+            path: API endpoint path (default: '/agents')
+            port: Server port (default: 8000)
+            host: Server host (default: '0.0.0.0')
+            debug: Enable debug mode for uvicorn (default: False)
+            
+        Returns:
+            None
+        """
+        global _agents_server_started, _agents_registered_endpoints, _agents_shared_apps
+        
+        if not self.agents:
+            logging.warning("No agents to launch. Add agents to the Agents instance first.")
+            return
+            
+        # Try to import FastAPI dependencies - lazy loading
+        try:
+            import uvicorn
+            from fastapi import FastAPI, HTTPException, Request
+            from fastapi.responses import JSONResponse
+            from pydantic import BaseModel
+            import threading
+            import time
+            
+            # Define the request model here since we need pydantic
+            class AgentQuery(BaseModel):
+                query: str
+                
+        except ImportError as e:
+            # Check which specific module is missing
+            missing_module = str(e).split("No module named '")[-1].rstrip("'")
+            display_error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            logging.error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            print(f"\nTo add API capabilities, install the required dependencies:")
+            print(f"pip install {missing_module}")
+            print("\nOr install all API dependencies with:")
+            print("pip install 'praisonaiagents[api]'")
+            return None
+        
+        # Initialize port-specific collections if needed
+        if port not in _agents_registered_endpoints:
+            _agents_registered_endpoints[port] = {}
+            
+        # Initialize shared FastAPI app if not already created for this port
+        if _agents_shared_apps.get(port) is None:
+            _agents_shared_apps[port] = FastAPI(
+                title=f"PraisonAI Agents API (Port {port})",
+                description="API for interacting with multiple PraisonAI Agents"
+            )
+            
+            # Add a root endpoint with a welcome message
+            @_agents_shared_apps[port].get("/")
+            async def root():
+                return {
+                    "message": f"Welcome to PraisonAI Agents API on port {port}. See /docs for usage.",
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+            
+            # Add healthcheck endpoint
+            @_agents_shared_apps[port].get("/health")
+            async def healthcheck():
+                return {
+                    "status": "ok", 
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+        
+        # Normalize path to ensure it starts with /
+        if not path.startswith('/'):
+            path = f'/{path}'
+            
+        # Check if path is already registered for this port
+        if path in _agents_registered_endpoints[port]:
+            logging.warning(f"Path '{path}' is already registered on port {port}. Please use a different path.")
+            print(f"⚠️ Warning: Path '{path}' is already registered on port {port}.")
+            # Use a modified path to avoid conflicts
+            original_path = path
+            instance_id = str(uuid.uuid4())[:6]
+            path = f"{path}_{instance_id}"
+            logging.warning(f"Using '{path}' instead of '{original_path}'")
+            print(f"🔄 Using '{path}' instead")
path: src/praisonai-agents/praisonaiagents/agents/agents.py
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The logic for handling path conflicts could be improved. Instead of simply appending a UUID, consider implementing a more user-friendly approach, such as suggesting available paths or providing a configuration option to specify a different path.[^1]

```python
# Suggest available paths or provide a configuration option
logging.warning(f"Path '{path}' is already registered on port {port}. Please use a different path or configure a unique path.")
print(f"⚠️ Warning: Path '{path}' is already registered on port {port}. Please use a different path or configure a unique path.")
return  # Stop further execution to avoid conflicts
```

#### Style Guide References
[^1]: Implement user-friendly conflict resolution strategies.
created_at: 2025-05-14T11:50:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -878,4 +883,230 @@
 
     def clear_state(self) -> None:
         """Clear all state values"""
-        self._state.clear() 
+        self._state.clear()
+        
+    def launch(self, path: str = '/agents', port: int = 8000, host: str = '0.0.0.0', debug: bool = False):
+        """
+        Launch all agents as a single API endpoint. The endpoint accepts a query and processes it through
+        all agents in sequence, with the output of each agent feeding into the next.
+        
+        Args:
+            path: API endpoint path (default: '/agents')
+            port: Server port (default: 8000)
+            host: Server host (default: '0.0.0.0')
+            debug: Enable debug mode for uvicorn (default: False)
+            
+        Returns:
+            None
+        """
+        global _agents_server_started, _agents_registered_endpoints, _agents_shared_apps
+        
+        if not self.agents:
+            logging.warning("No agents to launch. Add agents to the Agents instance first.")
+            return
+            
+        # Try to import FastAPI dependencies - lazy loading
+        try:
+            import uvicorn
+            from fastapi import FastAPI, HTTPException, Request
+            from fastapi.responses import JSONResponse
+            from pydantic import BaseModel
+            import threading
+            import time
+            
+            # Define the request model here since we need pydantic
+            class AgentQuery(BaseModel):
+                query: str
+                
+        except ImportError as e:
+            # Check which specific module is missing
+            missing_module = str(e).split("No module named '")[-1].rstrip("'")
+            display_error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            logging.error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            print(f"\nTo add API capabilities, install the required dependencies:")
+            print(f"pip install {missing_module}")
+            print("\nOr install all API dependencies with:")
+            print("pip install 'praisonaiagents[api]'")
+            return None
+        
+        # Initialize port-specific collections if needed
+        if port not in _agents_registered_endpoints:
+            _agents_registered_endpoints[port] = {}
+            
+        # Initialize shared FastAPI app if not already created for this port
+        if _agents_shared_apps.get(port) is None:
+            _agents_shared_apps[port] = FastAPI(
+                title=f"PraisonAI Agents API (Port {port})",
+                description="API for interacting with multiple PraisonAI Agents"
+            )
+            
+            # Add a root endpoint with a welcome message
+            @_agents_shared_apps[port].get("/")
+            async def root():
+                return {
+                    "message": f"Welcome to PraisonAI Agents API on port {port}. See /docs for usage.",
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+            
+            # Add healthcheck endpoint
+            @_agents_shared_apps[port].get("/health")
+            async def healthcheck():
+                return {
+                    "status": "ok", 
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+        
+        # Normalize path to ensure it starts with /
+        if not path.startswith('/'):
+            path = f'/{path}'
+            
+        # Check if path is already registered for this port
+        if path in _agents_registered_endpoints[port]:
+            logging.warning(f"Path '{path}' is already registered on port {port}. Please use a different path.")
+            print(f"⚠️ Warning: Path '{path}' is already registered on port {port}.")
+            # Use a modified path to avoid conflicts
+            original_path = path
+            instance_id = str(uuid.uuid4())[:6]
+            path = f"{path}_{instance_id}"
+            logging.warning(f"Using '{path}' instead of '{original_path}'")
+            print(f"🔄 Using '{path}' instead")
+        
+        # Generate a unique ID for this agent group's endpoint
+        endpoint_id = str(uuid.uuid4())
+        _agents_registered_endpoints[port][path] = endpoint_id
+        
+        # Define the endpoint handler
+        @_agents_shared_apps[port].post(path)
+        async def handle_query(request: Request, query_data: Optional[AgentQuery] = None):
+            # Handle both direct JSON with query field and form data
+            if query_data is None:
+                try:
+                    request_data = await request.json()
+                    if "query" not in request_data:
+                        raise HTTPException(status_code=400, detail="Missing 'query' field in request")
+                    query = request_data["query"]
+                except:
+                    # Fallback to form data or query params
+                    form_data = await request.form()
+                    if "query" in form_data:
+                        query = form_data["query"]
+                    else:
+                        raise HTTPException(status_code=400, detail="Missing 'query' field in request")
path: src/praisonai-agents/praisonaiagents/agents/agents.py
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The `except` block is too broad. It should catch specific exceptions like `json.JSONDecodeError` and `KeyError` to handle different error scenarios more precisely. This will help in debugging and prevent masking unexpected errors.[^1]

```python
except json.JSONDecodeError:
    logging.error("Invalid JSON format in request", exc_info=True)
    raise HTTPException(status_code=400, detail="Invalid JSON format in request")
except KeyError:
    logging.error("Missing 'query' field in request", exc_info=True)
    raise HTTPException(status_code=400, detail="Missing 'query' field in request")
```

#### Style Guide References
[^1]: Catch specific exceptions to handle different error scenarios more precisely.
created_at: 2025-05-14T11:50:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -878,4 +883,230 @@
 
     def clear_state(self) -> None:
         """Clear all state values"""
-        self._state.clear() 
+        self._state.clear()
+        
+    def launch(self, path: str = '/agents', port: int = 8000, host: str = '0.0.0.0', debug: bool = False):
+        """
+        Launch all agents as a single API endpoint. The endpoint accepts a query and processes it through
+        all agents in sequence, with the output of each agent feeding into the next.
+        
+        Args:
+            path: API endpoint path (default: '/agents')
+            port: Server port (default: 8000)
+            host: Server host (default: '0.0.0.0')
+            debug: Enable debug mode for uvicorn (default: False)
+            
+        Returns:
+            None
+        """
+        global _agents_server_started, _agents_registered_endpoints, _agents_shared_apps
+        
+        if not self.agents:
+            logging.warning("No agents to launch. Add agents to the Agents instance first.")
+            return
+            
+        # Try to import FastAPI dependencies - lazy loading
+        try:
+            import uvicorn
+            from fastapi import FastAPI, HTTPException, Request
+            from fastapi.responses import JSONResponse
+            from pydantic import BaseModel
+            import threading
+            import time
+            
+            # Define the request model here since we need pydantic
+            class AgentQuery(BaseModel):
+                query: str
+                
+        except ImportError as e:
+            # Check which specific module is missing
+            missing_module = str(e).split("No module named '")[-1].rstrip("'")
+            display_error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            logging.error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            print(f"\nTo add API capabilities, install the required dependencies:")
+            print(f"pip install {missing_module}")
+            print("\nOr install all API dependencies with:")
+            print("pip install 'praisonaiagents[api]'")
+            return None
+        
+        # Initialize port-specific collections if needed
+        if port not in _agents_registered_endpoints:
+            _agents_registered_endpoints[port] = {}
+            
+        # Initialize shared FastAPI app if not already created for this port
+        if _agents_shared_apps.get(port) is None:
+            _agents_shared_apps[port] = FastAPI(
+                title=f"PraisonAI Agents API (Port {port})",
+                description="API for interacting with multiple PraisonAI Agents"
+            )
+            
+            # Add a root endpoint with a welcome message
+            @_agents_shared_apps[port].get("/")
+            async def root():
+                return {
+                    "message": f"Welcome to PraisonAI Agents API on port {port}. See /docs for usage.",
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+            
+            # Add healthcheck endpoint
+            @_agents_shared_apps[port].get("/health")
+            async def healthcheck():
+                return {
+                    "status": "ok", 
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+        
+        # Normalize path to ensure it starts with /
+        if not path.startswith('/'):
+            path = f'/{path}'
+            
+        # Check if path is already registered for this port
+        if path in _agents_registered_endpoints[port]:
+            logging.warning(f"Path '{path}' is already registered on port {port}. Please use a different path.")
+            print(f"⚠️ Warning: Path '{path}' is already registered on port {port}.")
+            # Use a modified path to avoid conflicts
+            original_path = path
+            instance_id = str(uuid.uuid4())[:6]
+            path = f"{path}_{instance_id}"
+            logging.warning(f"Using '{path}' instead of '{original_path}'")
+            print(f"🔄 Using '{path}' instead")
+        
+        # Generate a unique ID for this agent group's endpoint
+        endpoint_id = str(uuid.uuid4())
+        _agents_registered_endpoints[port][path] = endpoint_id
+        
+        # Define the endpoint handler
+        @_agents_shared_apps[port].post(path)
+        async def handle_query(request: Request, query_data: Optional[AgentQuery] = None):
+            # Handle both direct JSON with query field and form data
+            if query_data is None:
+                try:
+                    request_data = await request.json()
+                    if "query" not in request_data:
+                        raise HTTPException(status_code=400, detail="Missing 'query' field in request")
+                    query = request_data["query"]
+                except:
+                    # Fallback to form data or query params
+                    form_data = await request.form()
+                    if "query" in form_data:
+                        query = form_data["query"]
+                    else:
+                        raise HTTPException(status_code=400, detail="Missing 'query' field in request")
+            else:
+                query = query_data.query
+            
+            try:
+                # Process the query sequentially through all agents
+                current_input = query
+                results = []
+                
+                for agent in self.agents:
+                    try:
+                        # Use async version if available, otherwise use sync version
+                        if asyncio.iscoroutinefunction(agent.chat):
+                            response = await agent.achat(current_input)
+                        else:
+                            # Run sync function in a thread to avoid blocking
+                            loop = asyncio.get_event_loop()
+                            response = await loop.run_in_executor(None, lambda: agent.chat(current_input))
+                        
+                        # Store this agent's result
+                        results.append({
+                            "agent": agent.name,
+                            "response": response
+                        })
+                        
+                        # Use this response as input to the next agent
+                        current_input = response
+                    except Exception as e:
+                        logging.error(f"Error with agent {agent.name}: {str(e)}", exc_info=True)
+                        results.append({
+                            "agent": agent.name,
+                            "error": str(e)
+                        })
+                        # Continue with original input if there's an error
path: src/praisonai-agents/praisonaiagents/agents/agents.py
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

Instead of continuing with the original input when an agent encounters an error, consider adding a mechanism to halt the sequence or retry the agent with a modified input. This could prevent cascading errors and improve the overall reliability of the multi-agent system.[^1]

```python
except Exception as e:
    logging.error(f"Error with agent {agent.name}: {str(e)}", exc_info=True)
    results.append({
        "agent": agent.name,
        "error": str(e)
    })
    # Option to halt or retry instead of continuing
    break  # Example: Halt on error
```

#### Style Guide References
[^1]: Implement mechanisms to handle agent errors and prevent cascading failures.
created_at: 2025-05-14T11:50:46Z
author_association: CONTRIBUTOR
diff_hunk: @@ -878,4 +883,230 @@
 
     def clear_state(self) -> None:
         """Clear all state values"""
-        self._state.clear() 
+        self._state.clear()
+        
+    def launch(self, path: str = '/agents', port: int = 8000, host: str = '0.0.0.0', debug: bool = False):
+        """
+        Launch all agents as a single API endpoint. The endpoint accepts a query and processes it through
+        all agents in sequence, with the output of each agent feeding into the next.
+        
+        Args:
+            path: API endpoint path (default: '/agents')
+            port: Server port (default: 8000)
+            host: Server host (default: '0.0.0.0')
+            debug: Enable debug mode for uvicorn (default: False)
+            
+        Returns:
+            None
+        """
+        global _agents_server_started, _agents_registered_endpoints, _agents_shared_apps
+        
+        if not self.agents:
+            logging.warning("No agents to launch. Add agents to the Agents instance first.")
+            return
+            
+        # Try to import FastAPI dependencies - lazy loading
+        try:
+            import uvicorn
+            from fastapi import FastAPI, HTTPException, Request
+            from fastapi.responses import JSONResponse
+            from pydantic import BaseModel
+            import threading
+            import time
+            
+            # Define the request model here since we need pydantic
+            class AgentQuery(BaseModel):
+                query: str
+                
+        except ImportError as e:
+            # Check which specific module is missing
+            missing_module = str(e).split("No module named '")[-1].rstrip("'")
+            display_error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            logging.error(f"Missing dependency: {missing_module}. Required for launch() method.")
+            print(f"\nTo add API capabilities, install the required dependencies:")
+            print(f"pip install {missing_module}")
+            print("\nOr install all API dependencies with:")
+            print("pip install 'praisonaiagents[api]'")
+            return None
+        
+        # Initialize port-specific collections if needed
+        if port not in _agents_registered_endpoints:
+            _agents_registered_endpoints[port] = {}
+            
+        # Initialize shared FastAPI app if not already created for this port
+        if _agents_shared_apps.get(port) is None:
+            _agents_shared_apps[port] = FastAPI(
+                title=f"PraisonAI Agents API (Port {port})",
+                description="API for interacting with multiple PraisonAI Agents"
+            )
+            
+            # Add a root endpoint with a welcome message
+            @_agents_shared_apps[port].get("/")
+            async def root():
+                return {
+                    "message": f"Welcome to PraisonAI Agents API on port {port}. See /docs for usage.",
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+            
+            # Add healthcheck endpoint
+            @_agents_shared_apps[port].get("/health")
+            async def healthcheck():
+                return {
+                    "status": "ok", 
+                    "endpoints": list(_agents_registered_endpoints[port].keys())
+                }
+        
+        # Normalize path to ensure it starts with /
+        if not path.startswith('/'):
+            path = f'/{path}'
+            
+        # Check if path is already registered for this port
+        if path in _agents_registered_endpoints[port]:
+            logging.warning(f"Path '{path}' is already registered on port {port}. Please use a different path.")
+            print(f"⚠️ Warning: Path '{path}' is already registered on port {port}.")
+            # Use a modified path to avoid conflicts
+            original_path = path
+            instance_id = str(uuid.uuid4())[:6]
+            path = f"{path}_{instance_id}"
+            logging.warning(f"Using '{path}' instead of '{original_path}'")
+            print(f"🔄 Using '{path}' instead")
+        
+        # Generate a unique ID for this agent group's endpoint
+        endpoint_id = str(uuid.uuid4())
+        _agents_registered_endpoints[port][path] = endpoint_id
+        
+        # Define the endpoint handler
+        @_agents_shared_apps[port].post(path)
+        async def handle_query(request: Request, query_data: Optional[AgentQuery] = None):
+            # Handle both direct JSON with query field and form data
+            if query_data is None:
+                try:
+                    request_data = await request.json()
+                    if "query" not in request_data:
+                        raise HTTPException(status_code=400, detail="Missing 'query' field in request")
+                    query = request_data["query"]
+                except:
+                    # Fallback to form data or query params
+                    form_data = await request.form()
+                    if "query" in form_data:
+                        query = form_data["query"]
+                    else:
+                        raise HTTPException(status_code=400, detail="Missing 'query' field in request")
+            else:
+                query = query_data.query
+            
+            try:
+                # Process the query sequentially through all agents
+                current_input = query
+                results = []
+                
+                for agent in self.agents:
+                    try:
+                        # Use async version if available, otherwise use sync version
+                        if asyncio.iscoroutinefunction(agent.chat):
+                            response = await agent.achat(current_input)
+                        else:
+                            # Run sync function in a thread to avoid blocking
+                            loop = asyncio.get_event_loop()
+                            response = await loop.run_in_executor(None, lambda: agent.chat(current_input))
+                        
+                        # Store this agent's result
+                        results.append({
+                            "agent": agent.name,
+                            "response": response
+                        })
+                        
+                        # Use this response as input to the next agent
+                        current_input = response
+                    except Exception as e:
+                        logging.error(f"Error with agent {agent.name}: {str(e)}", exc_info=True)
+                        results.append({
+                            "agent": agent.name,
+                            "error": str(e)
+                        })
+                        # Continue with original input if there's an error
+                
+                # Return all results and the final output
+                return {
+                    "query": query,
+                    "results": results,
+                    "final_response": current_input
+                }
+            except Exception as e:
+                logging.error(f"Error processing query: {str(e)}", exc_info=True)
+                return JSONResponse(
+                    status_code=500,
+                    content={"error": f"Error processing query: {str(e)}"}
+                )
+        
+        print(f"🚀 Multi-Agent API available at http://{host}:{port}{path}")
+        agent_names = ", ".join([agent.name for agent in self.agents])
+        print(f"📊 Available agents ({len(self.agents)}): {agent_names}")
+        
+        # Start the server if it's not already running for this port
+        if not _agents_server_started.get(port, False):
+            # Mark the server as started first to prevent duplicate starts
+            _agents_server_started[port] = True
+            
+            # Start the server in a separate thread
+            def run_server():
+                try:
+                    print(f"✅ FastAPI server started at http://{host}:{port}")
+                    print(f"📚 API documentation available at http://{host}:{port}/docs")
+                    print(f"🔌 Available endpoints: {', '.join(list(_agents_registered_endpoints[port].keys()))}")
+                    uvicorn.run(_agents_shared_apps[port], host=host, port=port, log_level="debug" if debug else "info")
+                except Exception as e:
+                    logging.error(f"Error starting server: {str(e)}", exc_info=True)
+                    print(f"❌ Error starting server: {str(e)}")
+            
+            # Run server in a background thread
+            server_thread = threading.Thread(target=run_server, daemon=True)
+            server_thread.start()
+            
+            # Wait for a moment to allow the server to start and register endpoints
+            time.sleep(0.5)
+        else:
+            # If server is already running, wait a moment to make sure the endpoint is registered
+            time.sleep(0.1)
+            print(f"🔌 Available endpoints on port {port}: {', '.join(list(_agents_registered_endpoints[port].keys()))}")
+        
+        # Get the stack frame to check if this is the last launch() call in the script
+        import inspect
+        stack = inspect.stack()
+        
+        # If this is called from a Python script (not interactive), try to detect if it's the last launch call
+        if len(stack) > 1 and stack[1].filename.endswith('.py'):
+            caller_frame = stack[1]
+            caller_line = caller_frame.lineno
+            
+            try:
+                # Read the file to check if there are more launch calls after this one
+                with open(caller_frame.filename, 'r') as f:
+                    lines = f.readlines()
+                
+                # Check if there are more launch() calls after the current line
+                has_more_launches = False
+                for line in lines[caller_line:]:
+                    if '.launch(' in line and not line.strip().startswith('#'):
+                        has_more_launches = True
+                        break
+                
+                # If this is the last launch call, block the main thread
+                if not has_more_launches:
+                    try:
+                        print("\nAll agents registered. Press Ctrl+C to stop the servers.")
+                        while True:
+                            time.sleep(1)
+                    except KeyboardInterrupt:
+                        print("\nServers stopped")
+            except Exception as e:
+                # If something goes wrong with detection, block anyway to be safe
+                logging.error(f"Error in launch detection: {e}")
+                try:
+                    print("\nKeeping servers alive. Press Ctrl+C to stop.")
+                    while True:
+                        time.sleep(1)
+                except KeyboardInterrupt:
+                    print("\nServers stopped")
path: src/praisonai-agents/praisonaiagents/agents/agents.py
user: login: gemini-code-assist[bot]
body: ![medium](https://img.shields.io/badge/priority-medium-yellow)

The logic for detecting the last `launch()` call is complex and might not be reliable in all scenarios. Consider providing a more explicit way for the user to signal that all agents have been registered, such as a dedicated function call or a configuration option. This would make the code more robust and easier to understand.[^1]

```python
# Provide a dedicated function or configuration option
print("\nAll agents registered. Press Ctrl+C to stop the servers.")
while True:
    time.sleep(1)
```

#### Style Guide References
[^1]: Provide explicit mechanisms for signaling the completion of agent registration.
created_at: 2025-05-14T11:50:46Z
author_association: CONTRIBUTOR

PR comments:
user: login: netlify[bot]
created_at: 2025-05-14T11:50:14Z
body: ### <span aria-hidden="true">✅</span> Deploy Preview for *praisonai* canceled.


|  Name | Link |
|:-:|------------------------|
|<span aria-hidden="true">🔨</span> Latest commit | e50db48c7cffcbc924a81d8177031cdf090eb0ac |
|<span aria-hidden="true">🔍</span> Latest deploy log | https://app.netlify.com/sites/praisonai/deploys/6824837352b246000814866a |
author_association: NONE
user: login: coderabbitai[bot]
created_at: 2025-05-14T11:50:15Z
body: <!-- This is an auto-generated comment: summarize by coderabbit.ai -->
<!-- This is an auto-generated comment: failure by coderabbit.ai -->

> [!CAUTION]
> ## Review failed
> 
> The pull request is closed.

<!-- end of auto-generated comment: failure by coderabbit.ai -->
<!-- walkthrough_start -->

## Walkthrough

This update introduces new scripts for launching multi-agent systems via the `praisonaiagents` framework, including group and single-agent endpoints. It adds a `launch` method to the core agent class to expose agents as FastAPI endpoints, manages server state for multiple endpoints, and increments the project version.

## Changes

| File(s)                                                                 | Change Summary                                                                                                                                                                                                                                                                                                                                                       |
|-------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| src/praisonai-agents/multi-agents-api.py, src/praisonai-agents/multi-agents-group-api.py | New scripts defining and launching multi-agent systems with different agent groupings and web API endpoints using the `praisonaiagents` framework. Each script configures agents for research and summarization tasks and launches them as FastAPI services on specified paths and ports.                                      |
| src/praisonai-agents/praisonaiagents/agents/agents.py                   | Adds a `launch` method to the `PraisonAIAgents` class, enabling agents to be served as FastAPI endpoints. Handles sequential query processing, endpoint registration, server state management, and background server startup. Introduces global variables for shared app instances and endpoint tracking.                        |
| src/praisonai-agents/pyproject.toml                                     | Updates the project version from `0.0.79` to `0.0.80`. No other configuration changes.                                                                                                                                                                                                                                         |

## Sequence Diagram(s)

```mermaid
sequenceDiagram
    participant User
    participant FastAPI Server
    participant Agents Collection
    participant Research Agent
    participant Summarise Agent

    User->>FastAPI Server: POST /agents with query
    FastAPI Server->>Agents Collection: Receive query
    Agents Collection->>Research Agent: Process query (e.g., search AI 2024)
    Research Agent-->>Agents Collection: Research result
    Agents Collection->>Summarise Agent: Summarize research result
    Summarise Agent-->>Agents Collection: Summary points
    Agents Collection-->>FastAPI Server: Chain of agent results
    FastAPI Server-->>User: JSON response with results
```

## Suggested labels

`Review effort 2/5`

## Poem

> 🐇  
> A warren of agents, now ready to serve,  
> With research and summaries, they never lose nerve!  
> Endpoints are hopping, on port thirty-thirty,  
> FastAPI magic—responses come flirty.  
> With a version hop, and new code to show,  
> This rabbit says: onward, let the queries flow!  
>

<!-- walkthrough_end -->
<!-- internal state start -->


<!-- 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 -->

<!-- internal state end -->
<!-- announcements_start -->

> [!TIP]
> <details>
> <summary>⚡️ Faster reviews with caching</summary>
> 
> - CodeRabbit now supports caching for code and dependencies, helping speed up reviews. This means quicker feedback, reduced wait times, and a smoother review experience overall. Cached data is encrypted and stored securely. This feature will be automatically enabled for all accounts on May 16th. To opt out, configure `Review - Disable Cache` at either the organization or repository level. If you prefer to disable all data retention across your organization, simply turn off the `Data Retention` setting under your Organization Settings.
> 
> Enjoy the performance boost—your workflow just got faster.
> 
> </details>

<!-- announcements_end -->

---

<details>
<summary>📜 Recent review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**

<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between fed7ac528848d2356794418d5c122c757c0402cb and e50db48c7cffcbc924a81d8177031cdf090eb0ac.

</details>

<details>
<summary>⛔ Files ignored due to path filters (1)</summary>

* `src/praisonai-agents/uv.lock` is excluded by `!**/*.lock`

</details>

<details>
<summary>📒 Files selected for processing (4)</summary>

* `src/praisonai-agents/multi-agents-api.py` (1 hunks)
* `src/praisonai-agents/multi-agents-group-api.py` (1 hunks)
* `src/praisonai-agents/praisonaiagents/agents/agents.py` (2 hunks)
* `src/praisonai-agents/pyproject.toml` (1 hunks)

</details>

</details>
<!-- finishing_touch_checkbox_start -->

<details open="true">
<summary>✨ Finishing Touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate Docstrings

</details>

<!-- finishing_touch_checkbox_end -->
<!-- tips_start -->

---

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<details>
<summary>🪧 Tips</summary>

### Chat

There are 3 ways to chat with [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=MervinPraison/PraisonAI&utm_content=476):

- Review comments: Directly reply to a review comment made by CodeRabbit. Example:
  - `I pushed a fix in commit <commit_id>, please review it.`
  - `Explain this complex logic.`
  - `Open a follow-up GitHub issue for this discussion.`
- Files and specific lines of code (under the "Files changed" tab): Tag `@coderabbitai` in a new review comment at the desired location with your query. Examples:
  - `@coderabbitai explain this code block.`
  -	`@coderabbitai modularize this function.`
- PR comments: Tag `@coderabbitai` in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
  - `@coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.`
  - `@coderabbitai read src/utils.ts and explain its main purpose.`
  - `@coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.`
  - `@coderabbitai help me debug CodeRabbit configuration file.`

### Support

Need help? Create a ticket on our [support page](https://www.coderabbit.ai/contact-us/support) for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

### CodeRabbit Commands (Invoked using PR comments)

- `@coderabbitai pause` to pause the reviews on a PR.
- `@coderabbitai resume` to resume the paused reviews.
- `@coderabbitai review` to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
- `@coderabbitai full review` to do a full review from scratch and review all the files again.
- `@coderabbitai summary` to regenerate the summary of the PR.
- `@coderabbitai generate docstrings` to [generate docstrings](https://docs.coderabbit.ai/finishing-touches/docstrings) for this PR.
- `@coderabbitai generate sequence diagram` to generate a sequence diagram of the changes in this PR.
- `@coderabbitai resolve` resolve all the CodeRabbit review comments.
- `@coderabbitai configuration` to show the current CodeRabbit configuration for the repository.
- `@coderabbitai help` to get help.

### Other keywords and placeholders

- Add `@coderabbitai ignore` anywhere in the PR description to prevent this PR from being reviewed.
- Add `@coderabbitai summary` to generate the high-level summary at a specific location in the PR description.
- Add `@coderabbitai` anywhere in the PR title to generate the title automatically.

### CodeRabbit Configuration File (`.coderabbit.yaml`)

- You can programmatically configure CodeRabbit by adding a `.coderabbit.yaml` file to the root of your repository.
- Please see the [configuration documentation](https://docs.coderabbit.ai/guides/configure-coderabbit) for more information.
- If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: `# yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json`

### Documentation and Community

- Visit our [Documentation](https://docs.coderabbit.ai) for detailed information on how to use CodeRabbit.
- Join our [Discord Community](http://discord.gg/coderabbit) to get help, request features, and share feedback.
- Follow us on [X/Twitter](https://twitter.com/coderabbitai) for updates and announcements.

</details>

<!-- tips_end -->
author_association: CONTRIBUTOR