import uuid, time, sys
from contextlib import asynccontextmanager
from loguru import logger
from fastapi import FastAPI, HTTPException, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.staticfiles import StaticFiles
from prometheus_client import generate_latest, CONTENT_TYPE_LATEST
import os
from schemas import ChatRequest, ChatResponse
from agent import SupportAgent
import httpx
from fastapi import Request
from starlette.responses import StreamingResponse
from prometheus_client import make_asgi_app
os.environ["ANONYMIZED_TELEMETRY"] = "False"
# Loguru Setup
logger.remove()
logger.add(sys.stdout, format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", level="INFO")
@asynccontextmanager
async def lifespan(app: FastAPI):
logger.info("Initializing SmartCoffee Agent for Hugging Face...")
try:
# Initialize agent once and store in app state
app.state.agent = SupportAgent()
logger.success("SupportAgent successfully initialized.")
except Exception as e:
logger.error(f"Critical Startup Error: {e}")
yield
logger.info("Shutting down...")
app = FastAPI(title="SmartCoffee AI 2026", lifespan=lifespan)
"""
Adding the dashaboard for monitoring
"""
@app.api_route("/grafana/{path:path}", methods=["GET", "POST", "PUT", "DELETE", "OPTIONS"])
async def grafana_proxy(path: str, request: Request):
async with httpx.AsyncClient(follow_redirects=True, timeout=30.0) as client:
resp = await client.request(
request.method,
f"http://127.0.0.1:3000/{path}",
params=request.query_params,
headers={
k: v
for k, v in request.headers.items()
if k.lower() not in ("host", "content-length")
},
content=await request.body(),
)
return StreamingResponse(
resp.aiter_raw(),
status_code=resp.status_code,
headers={
k: v
for k, v in resp.headers.items()
if k.lower() not in ("content-encoding", "transfer-encoding")
},
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_methods=["*"],
allow_headers=["*"],
)
# 1. ADDED: Health Check Route (Fixes the 404 error)
@app.get("/health")
async def health():
return {"status": "healthy", "time": time.time(), "agent_loaded": hasattr(app.state, 'agent')}
# 2. Metrics Route
@app.get("/metrics")
def metrics():
return Response(generate_latest(), media_type=CONTENT_TYPE_LATEST)
# 3. Core Chat API (Check that frontend sends 'question' and 'session_id')
@app.post("/api/v1/chat", response_model=ChatResponse)
async def chat(request: ChatRequest):
if request.session_id == "default":
request.session_id = f"hf_{uuid.uuid4().hex[:12]}"
if not hasattr(app.state, 'agent'):
raise HTTPException(status_code=503, detail="Agent logic is still initializing or failed to load.")
try:
# Calls the .run() method you shared in your agent.py
result = app.state.agent.run(request.question, session_id=request.session_id)
return ChatResponse(
question=request.question,
answer=result["answer"],
session_id=request.session_id,
timestamp=result.get("timestamp", time.time())
)
except Exception as e:
logger.error(f"Chat Execution Error: {e}")
raise HTTPException(status_code=500, detail=str(e))
# 4. Sync Route (Add if your JS CONFIG.API_ENDPOINT still uses /chat/sync)
@app.post("/api/v1/chat/sync", response_model=ChatResponse)
async def chat_sync(request: ChatRequest):
return await chat(request)
# 5. Static Files (MOUNTED LAST to prevent route hijacking)
app.mount("/", StaticFiles(directory="frontend", html=True), name="static")
app.mount("/metrics", make_asgi_app())