fsconvo / app /controllers /ask.py
hari7775's picture
refactor(llm): modularize LLM with BaseLLMEngine ABC, engines provider, use_rag bypass, and remove legacy parser
8433fda
Raw History Blame Contribute Delete
5.13 kB
import asyncio
import json
import logging
from typing import Optional
from fastapi import APIRouter, HTTPException
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from app.service.rag_pipeline import get_answer_from_rag, get_answer_stream_from_rag
from app.service.session_manager import session_manager
logger = logging.getLogger(__name__)
router = APIRouter(
prefix="/api",
tags=["Ask"]
)
class AskQuestionRequest(BaseModel):
question: str
context: Optional[str] = None
session_id: Optional[str] = None
model: Optional[str] = None
stream: Optional[bool] = False
use_rag: Optional[bool] = None
class StopAskRequest(BaseModel):
session_id: Optional[str] = None
@router.post("/ask/stream")
async def ask_question_stream(request: AskQuestionRequest):
"""
Stream tokens in real-time using Server-Sent Events (SSE).
"""
session_id = (request.session_id or "default").strip()
current_task = asyncio.current_task()
if current_task:
session_manager.register_task(session_id, current_task)
logger.info(
f"[ask-stream] session_id={session_id!r} "
f"model={request.model!r} "
f"use_rag={request.use_rag!r} "
f"question={request.question[:80]!r}"
)
def event_stream():
try:
for token in get_answer_stream_from_rag(
question=request.question,
context=request.context,
session_id=session_id,
model=request.model,
use_rag=request.use_rag,
):
payload = json.dumps({"token": token})
yield f"data: {payload}\n\n"
yield "data: [DONE]\n\n"
except asyncio.CancelledError:
logger.info(f"[ask-stream] Cancelled for session '{session_id}'.")
err_payload = json.dumps({"error": f"Generation stopped for session '{session_id}'."})
yield f"data: {err_payload}\n\n"
except Exception as e:
logger.error(f"[ask-stream] error: {e}")
err_payload = json.dumps({"error": str(e)})
yield f"data: {err_payload}\n\n"
finally:
if current_task:
session_manager.unregister_task(session_id, current_task)
return StreamingResponse(
event_stream(),
media_type="text/event-stream",
headers={
"Cache-Control": "no-cache",
"Connection": "keep-alive",
"X-Accel-Buffering": "no",
}
)
@router.post("/ask")
async def ask_question(request: AskQuestionRequest):
if request.stream:
return await ask_question_stream(request)
session_id = (request.session_id or "default").strip()
current_task = asyncio.current_task()
if current_task:
session_manager.register_task(session_id, current_task)
try:
logger.info(
f"[ask] session_id={session_id!r} "
f"model={request.model!r} "
f"use_rag={request.use_rag!r} "
f"question={request.question[:80]!r}"
)
answer = get_answer_from_rag(
question=request.question,
context=request.context,
session_id=session_id,
model=request.model,
use_rag=request.use_rag,
)
logger.info(
f"[ask] answer preview: {str(answer)[:120]!r}"
)
return {
"status": "success",
"answer": answer,
}
except asyncio.CancelledError:
logger.info(f"[ask] Generation stopped for session '{session_id}'.")
raise HTTPException(
status_code=499,
detail=f"Generation terminated for session '{session_id}'."
)
except Exception as e:
logger.error(f"[ask] error: {e}")
raise HTTPException(
status_code=500,
detail=str(e)
)
finally:
if current_task:
session_manager.unregister_task(session_id, current_task)
@router.post("/ask/stop")
async def stop_ask(
request: Optional[StopAskRequest] = None,
session_id: Optional[str] = None,
):
"""
Cancel active model generation task(s).
If session_id is provided, only halts that specific session.
"""
target_id = (request.session_id if request else None) or session_id
if target_id:
target_id = target_id.strip()
terminated = session_manager.terminate_session(target_id)
logger.info(f"[ask] Stop signal for session '{target_id}' (terminated={terminated}).")
return {
"status": "success",
"session_id": target_id,
"terminated": terminated,
"message": (
f"Generation for session '{target_id}' stopped."
if terminated
else f"No active generation running for session '{target_id}'."
),
}
count = session_manager.terminate_all()
return {
"status": "success",
"message": f"Stopped {count} active generation(s).",
"stopped_count": count,
}