2222import uuid
2323from dataclasses import dataclass
2424
25+ # Global variables for API server
26+ _server_started = False
27+ _registered_agents = {}
28+ _shared_app = None
29+
2530# Don't import FastAPI dependencies here - use lazy loading instead
2631
2732if TYPE_CHECKING :
2833 from ..task .task import Task
2934
30- # Shared variables for API server
31- _shared_app = None
32- _server_started = False
33- _registered_agents = {}
34-
3535@dataclass
3636class ChatCompletionMessage :
3737 content : str
@@ -1408,17 +1408,15 @@ async def execute_tool_async(self, function_name: str, arguments: Dict[str, Any]
14081408 logging .error (f"Error in execute_tool_async: { str (e )} " , exc_info = True )
14091409 return {"error" : f"Error in execute_tool_async: { str (e )} " }
14101410
1411- def launch (self , path : str = '/' , port : int = 8000 , host : str = '0.0.0.0' , autostart : bool = True , debug : bool = False , blocking : bool = True ):
1411+ def launch (self , path : str = '/' , port : int = 8000 , host : str = '0.0.0.0' , debug : bool = False ):
14121412 """
14131413 Launch the agent as an HTTP API endpoint.
14141414
14151415 Args:
14161416 path: API endpoint path (default: '/')
14171417 port: Server port (default: 8000)
14181418 host: Server host (default: '0.0.0.0')
1419- autostart: Whether to start the server automatically (default: True)
14201419 debug: Enable debug mode for uvicorn (default: False)
1421- blocking: If True, blocks the main thread to keep the server running (default: True)
14221420
14231421 Returns:
14241422 None
@@ -1431,6 +1429,8 @@ def launch(self, path: str = '/', port: int = 8000, host: str = '0.0.0.0', autos
14311429 from fastapi import FastAPI , HTTPException , Request
14321430 from fastapi .responses import JSONResponse
14331431 from pydantic import BaseModel
1432+ import threading
1433+ import time
14341434
14351435 # Define the request model here since we need pydantic
14361436 class AgentQuery (BaseModel ):
@@ -1458,6 +1458,11 @@ class AgentQuery(BaseModel):
14581458 @_shared_app .get ("/" )
14591459 async def root ():
14601460 return {"message" : "Welcome to PraisonAI Agents API. See /docs for usage." }
1461+
1462+ # Add healthcheck endpoint
1463+ @_shared_app .get ("/health" )
1464+ async def healthcheck ():
1465+ return {"status" : "ok" , "agents" : list (_registered_agents .keys ())}
14611466
14621467 # Normalize path to ensure it starts with /
14631468 if not path .startswith ('/' ):
@@ -1516,47 +1521,68 @@ async def handle_agent_query(request: Request, query_data: Optional[AgentQuery]
15161521
15171522 print (f"🚀 Agent '{ self .name } ' available at http://{ host } :{ port } { path } " )
15181523
1519- # Start the server if this is the first launch call and autostart is True
1520- if autostart and not _server_started :
1524+ # Start the server if it's not already running
1525+ if not _server_started :
15211526 _server_started = True
15221527
1523- # Add healthcheck endpoint
1524- @_shared_app .get ("/health" )
1525- async def healthcheck ():
1526- return {"status" : "ok" , "agents" : list (_registered_agents .keys ())}
1527-
1528- # Start the server in a separate thread to not block execution
1529- import threading
1528+ # Start the server in a separate thread
15301529 def run_server ():
15311530 try :
1531+ print (f"✅ FastAPI server started at http://{ host } :{ port } " )
1532+ print (f"📚 API documentation available at http://{ host } :{ port } /docs" )
1533+ print (f"🔌 Available endpoints: { ', ' .join (list (_registered_agents .keys ()))} " )
15321534 uvicorn .run (_shared_app , host = host , port = port , log_level = "debug" if debug else "info" )
15331535 except Exception as e :
15341536 logging .error (f"Error starting server: { str (e )} " , exc_info = True )
15351537 print (f"❌ Error starting server: { str (e )} " )
15361538
1539+ # Run server in a background thread
15371540 server_thread = threading .Thread (target = run_server , daemon = True )
15381541 server_thread .start ()
15391542
1540- # Give the server a moment to start up
1541- import time
1543+ # Wait for a moment to allow the server to start and register endpoints
15421544 time .sleep (0.5 )
1545+ else :
1546+ # If server is already running, wait a moment to make sure the endpoint is registered
1547+ time .sleep (0.1 )
1548+ print (f"🔌 Available endpoints: { ', ' .join (list (_registered_agents .keys ()))} " )
1549+
1550+ # Get the stack frame to check if this is the last launch() call in the script
1551+ import inspect
1552+ stack = inspect .stack ()
1553+
1554+ # If this is called from a Python script (not interactive), try to detect if it's the last launch call
1555+ if len (stack ) > 1 and stack [1 ].filename .endswith ('.py' ):
1556+ caller_frame = stack [1 ]
1557+ caller_line = caller_frame .lineno
15431558
1544- print (f"✅ FastAPI server started at http://{ host } :{ port } " )
1545- print (f"📚 API documentation available at http://{ host } :{ port } /docs" )
1546-
1547- # If blocking is True, keep the main thread alive
1548- if blocking :
1549- print ("\n Server is running in blocking mode. Press Ctrl+C to stop..." )
1559+ try :
1560+ # Read the file to check if there are more launch calls after this one
1561+ with open (caller_frame .filename , 'r' ) as f :
1562+ lines = f .readlines ()
1563+
1564+ # Check if there are more launch() calls after the current line
1565+ has_more_launches = False
1566+ for line in lines [caller_line :]:
1567+ if '.launch(' in line and not line .strip ().startswith ('#' ):
1568+ has_more_launches = True
1569+ break
1570+
1571+ # If this is the last launch call, block the main thread
1572+ if not has_more_launches :
1573+ try :
1574+ print ("\n All agents registered. Press Ctrl+C to stop the server." )
1575+ while True :
1576+ time .sleep (1 )
1577+ except KeyboardInterrupt :
1578+ print ("\n Server stopped" )
1579+ except Exception as e :
1580+ # If something goes wrong with detection, block anyway to be safe
1581+ logging .error (f"Error in launch detection: { e } " )
15501582 try :
15511583 while True :
15521584 time .sleep (1 )
15531585 except KeyboardInterrupt :
15541586 print ("\n Server stopped" )
1555- else :
1556- # Note for non-blocking mode
1557- print ("\n Note: Server is running in a background thread. To keep it alive, either:" )
1558- print ("1. Set blocking=True when calling launch()" )
1559- print ("2. Keep your main application running" )
1560- print ("3. Use a loop in your code to prevent the program from exiting" )
1561-
1587+
15621588 return None
0 commit comments