first commit
This commit is contained in:
@@ -0,0 +1,174 @@
|
||||
"""
|
||||
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()
|
||||
Reference in New Issue
Block a user