Multi-Step-AI-Agent / server.py
Mahmoud Maher
Initial commit: Multi-Step AI Agent with RAG knowledge base, FastAPI server, and Gradio UI
6d149e7
Raw
History Blame Contribute Delete
4.01 kB
"""
Multi-Step AI Agent β€” FastAPI REST API
=======================================
Run with: uvicorn server:app --host 0.0.0.0 --port 7860
"""
from contextlib import asynccontextmanager
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
import os
import tempfile
import shutil
from app.agent.agent import create_agent, get_memory_manager
# ── Lifespan β€” warm up agent on startup ─────────────────────────
_agent = None
_memory = None
@asynccontextmanager
async def lifespan(app: FastAPI):
"""Initialize the agent once at startup."""
global _agent, _memory
_agent = create_agent()
_memory = get_memory_manager()
yield
# ── FastAPI app ─────────────────────────────────────────────────
app = FastAPI(
title="Multi-Step AI Agent",
description="Plan-and-Execute AI Agent with RAG Knowledge Base",
version="1.0.0",
lifespan=lifespan,
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# ── Request / response models ───────────────────────────────────
class ChatRequest(BaseModel):
message: str
class ChatResponse(BaseModel):
reply: str
steps: list[dict] = []
class DocumentResponse(BaseModel):
status: str
detail: str
# ── Endpoints ───────────────────────────────────────────────────
@app.get("/health")
async def health():
"""Health check for deployment platforms."""
return {"status": "ok", "agent": _agent is not None}
@app.post("/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
"""Send a message to the agent and get a response."""
if not _agent:
raise HTTPException(status_code=503, detail="Agent not initialized")
steps_log = []
def on_step(step_num, tool_name, status):
steps_log.append({
"step": step_num,
"tool": tool_name,
"status": status,
})
try:
reply = _agent.chat(request.message, on_step=on_step)
except Exception as e:
raise HTTPException(status_code=500, detail=str(e))
return ChatResponse(reply=reply, steps=steps_log)
@app.post("/upload", response_model=DocumentResponse)
async def upload_document(file: UploadFile = File(...)):
"""Upload a PDF to the knowledge base."""
if not file.filename.lower().endswith(".pdf"):
raise HTTPException(status_code=400, detail="Only PDF files are supported")
# Save to a temp location, then ingest
upload_dir = os.path.join("data", "uploads")
os.makedirs(upload_dir, exist_ok=True)
file_path = os.path.join(upload_dir, file.filename)
with open(file_path, "wb") as f:
shutil.copyfileobj(file.file, f)
# Use the store_document tool directly
from app.tools.knowledge_base import store_document
result = store_document.invoke({"file_path": file_path})
return DocumentResponse(status="ok", detail=result)
@app.get("/documents")
async def list_docs():
"""List documents in the knowledge base."""
from app.tools.knowledge_base import list_documents
result = list_documents.invoke({})
return {"documents": result}
@app.post("/clear")
async def clear_memory():
"""Clear the agent's chat memory."""
if _memory:
_memory.clear()
return {"status": "ok", "detail": "Memory cleared"}
# ── Run directly ────────────────────────────────────────────────
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=7860)