import logging from fastapi import APIRouter, HTTPException, Request from app.config import settings from app.models.schemas import ChatRequest, ChatResponse from app.services import rag_service from app.services.limits import SlidingWindowLimiter logger = logging.getLogger(__name__) router = APIRouter() _chat_limiter = SlidingWindowLimiter(settings.rate_limit_chat_per_minute, 60) def _client_ip(request: Request) -> str: # Behind Vercel/HF proxies the real client is in X-Forwarded-For fwd = request.headers.get("x-forwarded-for") if fwd: return fwd.split(",")[0].strip() return request.client.host if request.client else "unknown" @router.post("/chat", response_model=ChatResponse) async def chat_endpoint(request: ChatRequest, http_request: Request): if not request.message.strip(): raise HTTPException(status_code=400, detail="Message cannot be empty") if not _chat_limiter.allow(_client_ip(http_request)): raise HTTPException( status_code=429, detail="Too many requests — please wait a minute and try again.", ) try: answer, sources, response_type, structured_data, token_usage, model_used, rate_limit = \ await rag_service.chat( request.session_id, request.message, request.model, request.email_action, request.email_payload, ) return ChatResponse( answer=answer, sources=sources, session_id=request.session_id, response_type=response_type, structured_data=structured_data, token_usage=token_usage, model_used=model_used, rate_limit=rate_limit, ) except Exception: # Full details go to the server log only — never to the client. logger.exception("Chat request failed (session=%s)", request.session_id) raise HTTPException( status_code=500, detail="Something went wrong while processing your request. Please try again.", ) @router.delete("/chat/{session_id}") async def clear_session(session_id: str): rag_service.clear_session(session_id) return {"cleared": session_id}