import sys import os import secrets import hmac import hashlib import time from fastapi import FastAPI, HTTPException, Request from fastapi.responses import HTMLResponse, Response from fastapi.staticfiles import StaticFiles from pydantic import BaseModel from typing import Optional, List sys.path.append(os.path.dirname(os.path.abspath(__file__))) from agent import AgentContext, AgentContextType, UserMessage import initialize from python.helpers import runtime, dotenv, files, git from python.helpers.print_style import PrintStyle app = FastAPI(title="Skilled-Agent API") # CSRF Logic from run_ui.py CSRF_SECRET = secrets.token_bytes(32) TOKEN_TTL = 3600 def generate_csrf_token(): nonce = secrets.token_hex(16) timestamp = str(int(time.time())) data = f"{nonce}:{timestamp}" sig = hmac.new(CSRF_SECRET, data.encode(), hashlib.sha256).hexdigest() return f"{data}.{sig}" class ChatRequest(BaseModel): message: str chat_id: Optional[str] = None attachments: Optional[List[str]] = None class ChatResponse(BaseModel): response: str chat_id: str @app.on_event("startup") async def startup_event(): PrintStyle().print("Initializing Skilled-Agent API...") runtime.initialize() dotenv.load_dotenv() # Run migrations if necessary if hasattr(initialize, "initialize_migration"): initialize.initialize_migration() # Initialize chats init_chats = initialize.initialize_chats() init_chats.result_sync() # Initialize MCP initialize.initialize_mcp() # Start job loop initialize.initialize_job_loop() # Preload initialize.initialize_preload() PrintStyle().print("Skilled-Agent API started.") @app.get("/", response_class=HTMLResponse) async def serve_index(): PrintStyle().print("Serving index.html") gitinfo = None try: gitinfo = git.get_git_info() except Exception as e: gitinfo = {"version": "unknown", "commit_time": "unknown"} index_content = files.read_file("webui/index.html") index_content = files.replace_placeholders_text( _content=index_content, version_no=gitinfo["version"], version_time=gitinfo["commit_time"] ) csrf_token = generate_csrf_token() runtime_id = runtime.get_runtime_id() meta_tags = f''' ''' index_content = index_content.replace("", f"{meta_tags}") return index_content @app.post("/chat", response_model=ChatResponse) async def chat(request: ChatRequest): context = None if request.chat_id: context = AgentContext.get(request.chat_id) if not context: raise HTTPException(status_code=404, detail=f"Chat session {request.chat_id} not found") else: config = initialize.initialize_agent() context = AgentContext(config=config, type=AgentContextType.BACKGROUND) if not request.message: raise HTTPException(status_code=400, detail="Message is required") try: PrintStyle().print(f"Processing message for chat {context.id}...") task = context.communicate( UserMessage( message=request.message, attachments=request.attachments or [] ) ) result = await task.result() return ChatResponse(response=result, chat_id=context.id) except Exception as e: PrintStyle().error(f"Error in chat: {e}") raise HTTPException(status_code=500, detail=str(e)) @app.get("/health") async def health(): return {"status": "healthy"} # Mount static files app.mount("/", StaticFiles(directory="webui"), name="static")