""" JARVIS — Chat API Route POST /api/chat/message — send a message, get AI response GET /api/chat/history — retrieve session history """ import uuid from typing import Optional, List from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel, Field from loguru import logger from app.db.database import ( AsyncSession, get_db, create_session, get_session, get_messages, add_message, ) from app.agents.react_agent import ReActAgent from app.core.config import settings router = APIRouter() # ───────────────────────────────────────────── # Request / Response schemas # ───────────────────────────────────────────── class ChatRequest(BaseModel): message: str = Field(..., min_length=1, max_length=4096) session_id: Optional[str] = None image_caption: Optional[str] = None # pre-analyzed image context stream: bool = False class Config: json_schema_extra = { "example": { "message": "What is the capital of France?", "session_id": "abc-123", } } class AgentStepOut(BaseModel): step_type: str content: str tool_name: Optional[str] = None class ChatResponse(BaseModel): response: str session_id: str message_id: int steps: List[AgentStepOut] = [] used_tools: List[str] = [] memory_hits: int = 0 iterations: int = 1 # ───────────────────────────────────────────── # Routes # ───────────────────────────────────────────── @router.post("/message", response_model=ChatResponse) async def chat_message( request: ChatRequest, db: AsyncSession = Depends(get_db), ): """ Send a message to JARVIS and get an AI response. Creates a new session if session_id not provided. """ # Ensure session exists session_id = request.session_id or str(uuid.uuid4()) session = await get_session(db, session_id) if not session: title = request.message[:50] + ("..." if len(request.message) > 50 else "") await create_session(db, session_id, title=title) # Load conversation history (last 20 messages) messages = await get_messages(db, session_id, limit=20) history = [ {"role": msg.role, "content": msg.content} for msg in messages ] # Save user message to DB user_msg = await add_message( db, session_id, role="user", content=request.message, modality="text", ) # Run ReAct agent try: agent = ReActAgent() result = agent.run( user_message=request.message, conversation_history=history, session_id=session_id, image_caption=request.image_caption, ) except Exception as e: logger.error(f"Agent error: {e}") raise HTTPException(status_code=500, detail=f"Agent error: {str(e)}") # Save assistant response to DB asst_msg = await add_message( db, session_id, role="assistant", content=result.final_response, modality="text", ) return ChatResponse( response=result.final_response, session_id=session_id, message_id=asst_msg.id, steps=[ AgentStepOut( step_type=s.step_type.value, content=s.content, tool_name=s.tool_name, ) for s in result.steps ], used_tools=result.used_tools, memory_hits=result.memory_hits, iterations=result.iterations, ) @router.get("/history/{session_id}") async def get_history( session_id: str, limit: int = 50, db: AsyncSession = Depends(get_db), ): """Retrieve conversation history for a session.""" session = await get_session(db, session_id) if not session: raise HTTPException(status_code=404, detail="Session not found") messages = await get_messages(db, session_id, limit=limit) return { "session_id": session_id, "title": session.title, "messages": [ { "id": msg.id, "role": msg.role, "content": msg.content, "modality": msg.modality, "created_at": msg.created_at.isoformat(), } for msg in messages ], }