Spaces:
Build error
Build error
| """ | |
| AlgoPharma β Chat API router. | |
| POST /api/chat | |
| - Receives {message, state} from frontend. | |
| - Calls Nemotron slot-filler to update state. | |
| - If state is READY (medicine + source filled): | |
| * Creates a Project row in SQLite DB. | |
| * Launches llm_agent as a FastAPI BackgroundTask. | |
| * Returns {ready: true, project_id, bot_message}. | |
| - Otherwise returns {ready: false, bot_message, state}. | |
| No session storage β the frontend owns and persists the state dict. | |
| """ | |
| import logging | |
| from fastapi import APIRouter, BackgroundTasks, HTTPException | |
| from pydantic import BaseModel | |
| logger = logging.getLogger(__name__) | |
| router = APIRouter(prefix="/api", tags=["chat"]) | |
| # ββ Request / Response models βββββββββββββββββββββββββββββ | |
| class ChatRequest(BaseModel): | |
| message: str | |
| state: dict = {"medicine": None, "source": None, "symptom": None} | |
| class ChatResponse(BaseModel): | |
| ready: bool | |
| bot_message: str | |
| state: dict | |
| project_id: int | None = None | |
| # ββ Helpers βββββββββββββββββββββββββββββββββββββββββββββββ | |
| def _build_query(state: dict) -> str: | |
| """Convert the gathered state dict into an llm_agent query string.""" | |
| med = state["medicine"] | |
| source = state["source"] | |
| symp = state.get("symptom") | |
| q = f"Find side effects of {med} on {source}" | |
| if symp: | |
| q += f" focusing on {symp}" | |
| return q | |
| def _create_project(name: str) -> int: | |
| """Insert a new Project row and return its integer ID.""" | |
| from database import SessionLocal | |
| from models import Project | |
| with SessionLocal() as session: | |
| # Avoid unique-constraint errors by appending a timestamp if needed | |
| from datetime import datetime | |
| ts = datetime.utcnow().strftime("%Y%m%d_%H%M%S") | |
| unique_name = f"{name[:120]}_{ts}" | |
| project = Project(name=unique_name, description=f"Auto-created by chat pipeline: {name}") | |
| session.add(project) | |
| session.commit() | |
| session.refresh(project) | |
| return project.id | |
| # ββ Endpoint ββββββββββββββββββββββββββββββββββββββββββββββ | |
| async def chat(request: ChatRequest, background_tasks: BackgroundTasks): | |
| """ | |
| Main chat endpoint. Frontend calls this every time the user sends a message. | |
| The frontend owns the `state` dict β it persists it in memory and sends it | |
| back each turn. | |
| """ | |
| from agentic.chat_manager import get_nemotron_response, READY_SIGNAL | |
| # 1. Update state via Nemotron | |
| try: | |
| new_state, bot_message = get_nemotron_response(request.message, request.state) | |
| except Exception as e: | |
| logger.error(f"[/api/chat] chat_manager failed: {e}") | |
| raise HTTPException(status_code=500, detail=f"LLM error: {e}") | |
| # 2. Not ready yet β return next question | |
| if bot_message != READY_SIGNAL: | |
| return ChatResponse( | |
| ready=False, | |
| bot_message=bot_message, | |
| state=new_state, | |
| ) | |
| # 3. READY β create project + fire background pipeline | |
| try: | |
| med = new_state.get("medicine", "unknown") | |
| source = new_state.get("source", "unknown") | |
| symp = new_state.get("symptom") | |
| project_name = f"{med}_{source}" + (f"_{symp}" if symp else "") | |
| project_id = _create_project(project_name) | |
| query = _build_query(new_state) | |
| logger.info(f"[/api/chat] Pipeline triggered | project_id={project_id} | query={query!r}") | |
| # Fire-and-forget: runs llm_agent in the background so we respond immediately | |
| from llm_module import llm_agent | |
| background_tasks.add_task(llm_agent, query, project_id) | |
| return ChatResponse( | |
| ready=True, | |
| bot_message="Analysis started! Redirecting to your dashboard...", | |
| state=new_state, | |
| project_id=project_id, | |
| ) | |
| except Exception as e: | |
| logger.error(f"[/api/chat] Pipeline launch failed: {e}") | |
| raise HTTPException(status_code=500, detail=f"Pipeline error: {e}") | |