"""Main entry point for X-Agents agent service.""" import logging import asyncio import sys from pathlib import Path # Add project root to path (parent of core directory) project_root = Path(__file__).parent.parent.parent core_dir = project_root / "core" sys.path.insert(0, str(project_root)) # for X-Agents root sys.path.insert(0, str(core_dir)) # for core sys.path.insert(0, str(core_dir / "nanobot")) # for nanobot from fastapi import FastAPI import uvicorn from agents.config import Config from agents.api.routes import router, set_agent, set_team_agent, add_cors from agents.agent.loop import AgentLoop from agents.agent.team_agent import TeamAgent from agents.llm import ProviderFactory from agents.tools import create_default_registry # Configure logging logging.basicConfig( level=logging.INFO, format="%(asctime)s - %(name)s - %(levelname)s - %(message)s", ) logger = logging.getLogger(__name__) class SimpleProvider: """Simple LLM provider placeholder for testing without API keys.""" def __init__(self, api_key: str = "", base_url: str | None = None): self.api_key = api_key self.base_url = base_url async def chat(self, messages: list[dict], model: str, **kwargs) -> dict: """Simulate LLM chat response. Args: messages: Message list model: Model name Returns: Simulated response """ from agents.llm import LLMResponse user_msg = "" for msg in reversed(messages): if msg.get("role") == "user": user_msg = msg.get("content", "") break return LLMResponse( content=f"I received your message: {user_msg[:50]}... (LLM integration pending)", tool_calls=[], finish_reason="stop", ) async def chat_with_retry(self, *args, **kwargs): return await self.chat(*args, **kwargs) def get_default_model(self) -> str: return "simple" def create_app(config: Config | None = None) -> FastAPI: """Create and configure the FastAPI application. Args: config: Configuration instance Returns: Configured FastAPI app """ config = config or Config() app = FastAPI( title="X-Agents API", description="Agent API for X-Agents platform", version="0.1.0", ) # Include routers with /api/v1 prefix (aligned with Go backend paths: /api/agent/chat, /api/agent/chat/stream) app.include_router(router, prefix="/api/v1") # Add CORS middleware to allow Go backend cross-origin requests add_cors(app) # Initialize LLM provider if config.LLM_API_KEY: try: provider = ProviderFactory.create( provider=config.LLM_PROVIDER, api_key=config.LLM_API_KEY, api_base=config.LLM_BASE_URL, ) logger.info(f"Using {config.LLM_PROVIDER} provider with model {config.LLM_MODEL}") except ImportError as e: logger.warning(f"Failed to import provider package: {e}, using placeholder") provider = SimpleProvider(api_key=config.LLM_API_KEY) else: logger.warning("No LLM_API_KEY provided, using placeholder provider") provider = SimpleProvider() # Create tool registry tools = create_default_registry() # Initialize agent agent = AgentLoop( provider=provider, model=config.LLM_MODEL, workspace=config.WORKSPACE, max_iterations=config.MAX_ITERATIONS, tools=tools, ) set_agent(agent) # Initialize team agent for multi-agent collaboration team_agent = TeamAgent( provider=provider, model=config.LLM_MODEL, workspace=config.WORKSPACE, ) set_team_agent(team_agent) @app.on_event("startup") async def startup_event(): logger.info("X-Agents starting up...") logger.info(f"Model: {config.LLM_MODEL}") logger.info(f"Provider: {config.LLM_PROVIDER}") logger.info(f"Workspace: {config.WORKSPACE}") logger.info(f"Tools: {tools.tool_names}") @app.on_event("shutdown") async def shutdown_event(): logger.info("X-Agents shutting down...") return app def main(): """Run the agent service.""" config = Config() # Ensure workspace exists config.WORKSPACE.mkdir(exist_ok=True) app = create_app(config) uvicorn.run( app, host=config.API_HOST, port=config.API_PORT, log_level="info", ) if __name__ == "__main__": main()