""" Manus AI Clone - AI-powered browser automation API """ import os from contextlib import asynccontextmanager from pathlib import Path from typing import Optional from dotenv import load_dotenv from fastapi import FastAPI, HTTPException, Request from fastapi.responses import HTMLResponse from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates from pydantic import BaseModel, ConfigDict from pydantic_settings import BaseSettings from browser_agent import BrowserAgent # Load environment variables load_dotenv() # Get base directory BASE_DIR = Path(__file__).resolve().parent # Setup templates and static files templates = Jinja2Templates(directory=str(BASE_DIR / "templates")) class Settings(BaseSettings): """Application settings""" model_config = ConfigDict(env_file=".env") openai_api_key: str = os.getenv("OPENAI_API_KEY", "") model: str = os.getenv("MODEL", "gpt-4o-mini") headless: bool = os.getenv("HEADLESS", "false").lower() == "true" # Global agent instance agent: Optional[BrowserAgent] = None settings = Settings() @asynccontextmanager async def lifespan(app: FastAPI): """Manage application lifecycle""" global agent # Startup if not settings.openai_api_key: print("Warning: OPENAI_API_KEY not set. Please set it in .env file") else: print("Initializing browser agent...") agent = BrowserAgent( openai_api_key=settings.openai_api_key, model=settings.model ) await agent.initialize(headless=settings.headless) print("Browser agent ready!") yield # Shutdown if agent: print("Cleaning up browser agent...") await agent.cleanup() print("Cleanup complete") # Create FastAPI app app = FastAPI( title="Manus AI Clone", description="AI-powered browser automation using LangChain and Playwright", version="0.1.0", lifespan=lifespan, ) # Mount static files app.mount("/static", StaticFiles(directory=str(BASE_DIR / "static")), name="static") # Pydantic models class TaskRequest(BaseModel): """Request model for browser automation tasks""" model_config = ConfigDict( json_schema_extra={ "example": { "prompt": "Go to google.com and search for 'LangChain tutorial'" } } ) prompt: str class TaskResponse(BaseModel): """Response model for browser automation tasks""" success: bool output: Optional[str] = None error: Optional[str] = None screenshot: Optional[str] = None action_history: list = [] # API Routes @app.get("/", response_class=HTMLResponse) async def home(request: Request): """Serve the frontend interface""" return templates.TemplateResponse("index.html", {"request": request}) @app.get("/health") async def health_check(): """Health check endpoint""" return { "status": "healthy", "agent_initialized": agent is not None, "model": settings.model, } @app.post("/execute", response_model=TaskResponse) async def execute_task(request: TaskRequest): """Execute a browser automation task""" if not agent: raise HTTPException( status_code=503, detail="Browser agent not initialized. Please check OPENAI_API_KEY.", ) if not request.prompt.strip(): raise HTTPException(status_code=400, detail="Prompt cannot be empty") try: result = await agent.execute_task(request.prompt) return TaskResponse(**result) except Exception as e: raise HTTPException(status_code=500, detail=f"Error executing task: {str(e)}") @app.get("/status") async def get_status(): """Get current browser status and action history""" if not agent: raise HTTPException(status_code=503, detail="Browser agent not initialized") return { "action_history": agent.browser.action_history, "current_url": agent.browser.page.url if agent.browser.page else None, } def main(): """Run the application""" import uvicorn port = int(os.getenv("PORT", "8000")) host = os.getenv("HOST", "0.0.0.0") print(f""" 🤖 Manus AI Clone Starting... 📝 Make sure to set OPENAI_API_KEY in your .env file 🌐 Server will be available at: http://localhost:{port} """) uvicorn.run(app, host=host, port=port) if __name__ == "__main__": main()