from uuid import uuid4 from fastapi import APIRouter, Depends, HTTPException, Request, Response from sqlalchemy.ext.asyncio import AsyncSession from app.core.security import get_current_user from app.db.session import get_db from app.models.user import User from app.schemas.ai import ( AIProviderStatusResponse, AlertBriefRequest, AlertBriefResponse, BGPBriefRequest, BGPBriefRecordResponse, BGPBriefRecordSummary, PlaygroundMessageActionResponse, PlaygroundMessageCreateRequest, PlaygroundMessageEditRequest, PlaygroundMessageResendRequest, PlaygroundMessageStopRequest, PlaygroundSessionResponse, PlaygroundSessionUpsertRequest, PlaygroundThreadResponse, SituationalAlertBriefRequest, SituationalAlertBriefResponse, SituationalAnalysisRequest, SituationalAnalysisResponse, ) from app.services.alert_ai_brief import build_alert_brief_request from app.services.ai_client import AIProviderClient, get_ai_provider_client from app.services.bgp_ai_brief import build_bgp_brief_request from app.services.bgp_ai_brief_store import ( get_bgp_brief_record, get_latest_bgp_brief_record, list_bgp_brief_records, save_bgp_brief_record, ) from app.services.playground_session_store import ( get_playground_session, upsert_playground_session, ) from app.services.playground_chat_service import ( create_turn, edit_user_message, get_thread, resend_turn, stop_message, ) from app.services.situational_alert_ai_brief import build_situational_alert_brief_request router = APIRouter() @router.get("/provider/status", response_model=AIProviderStatusResponse) async def get_ai_provider_status( request: Request, response: Response, current_user: User = Depends(get_current_user), provider_client: AIProviderClient = Depends(get_ai_provider_client), ): request_id = request.headers.get("X-Request-ID") or str(uuid4()) response.headers["X-Request-ID"] = request_id return await provider_client.get_status(request_id=request_id) @router.post("/situational-awareness/analyze", response_model=SituationalAnalysisResponse) async def analyze_situational_awareness( payload: SituationalAnalysisRequest, request: Request, response: Response, current_user: User = Depends(get_current_user), provider_client: AIProviderClient = Depends(get_ai_provider_client), ): request_id = request.headers.get("X-Request-ID") or str(uuid4()) response.headers["X-Request-ID"] = request_id return await provider_client.analyze(payload, request_id=request_id) @router.get("/playground/thread", response_model=PlaygroundThreadResponse | None) async def get_playground_thread( session_key: str = "default", current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return await get_thread( db, user_id=current_user.id, session_key=session_key, ) @router.get("/playground/session", response_model=PlaygroundSessionResponse | None) async def get_saved_playground_session( session_key: str = "default", current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return await get_playground_session( db, user_id=current_user.id, session_key=session_key, ) @router.put("/playground/session", response_model=PlaygroundSessionResponse) async def save_playground_session( payload: PlaygroundSessionUpsertRequest, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return await upsert_playground_session( db, user_id=current_user.id, payload=payload, ) @router.post("/playground/messages", response_model=PlaygroundMessageActionResponse) async def create_playground_message( payload: PlaygroundMessageCreateRequest, current_user: User = Depends(get_current_user), provider_client: AIProviderClient = Depends(get_ai_provider_client), db: AsyncSession = Depends(get_db), ): return await create_turn( db, user_id=current_user.id, payload=payload, provider_client=provider_client, ) @router.post("/playground/messages/stop", response_model=PlaygroundMessageActionResponse) async def stop_playground_message( payload: PlaygroundMessageStopRequest, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return await stop_message( db, user_id=current_user.id, payload=payload, ) @router.post("/playground/messages/resend", response_model=PlaygroundMessageActionResponse) async def resend_playground_message( payload: PlaygroundMessageResendRequest, current_user: User = Depends(get_current_user), provider_client: AIProviderClient = Depends(get_ai_provider_client), db: AsyncSession = Depends(get_db), ): return await resend_turn( db, user_id=current_user.id, payload=payload, provider_client=provider_client, ) @router.post("/playground/messages/edit", response_model=PlaygroundMessageActionResponse) async def edit_playground_message( payload: PlaygroundMessageEditRequest, current_user: User = Depends(get_current_user), db: AsyncSession = Depends(get_db), ): return await edit_user_message( db, user_id=current_user.id, payload=payload, ) @router.get("/bgp/briefs", response_model=list[BGPBriefRecordSummary]) async def list_saved_bgp_briefs( current_user: User = Depends(get_current_user), ): return list_bgp_brief_records() @router.get("/bgp/briefs/latest", response_model=BGPBriefRecordResponse | None) async def get_latest_saved_bgp_brief( current_user: User = Depends(get_current_user), ): return get_latest_bgp_brief_record() @router.get("/bgp/briefs/{brief_id}", response_model=BGPBriefRecordResponse) async def get_saved_bgp_brief( brief_id: str, current_user: User = Depends(get_current_user), ): record = get_bgp_brief_record(brief_id) if record is None: raise HTTPException(status_code=404, detail="BGP brief not found") return record @router.post("/bgp/brief", response_model=BGPBriefRecordResponse) async def analyze_bgp_brief( payload: BGPBriefRequest, request: Request, response: Response, current_user: User = Depends(get_current_user), provider_client: AIProviderClient = Depends(get_ai_provider_client), db: AsyncSession = Depends(get_db), ): request_id = request.headers.get("X-Request-ID") or str(uuid4()) response.headers["X-Request-ID"] = request_id brief_request, facts, context = await build_bgp_brief_request( db, incident_limit=payload.incident_limit, anomaly_limit=payload.anomaly_limit, collector_limit=payload.collector_limit, ) brief_request.preferred_model = payload.preferred_model brief_request.thinking = payload.thinking analysis = await provider_client.analyze(brief_request, request_id=request_id) return save_bgp_brief_record( analysis, request_id=request_id, facts=facts, context=context, ) @router.post("/alerts/brief", response_model=AlertBriefResponse) async def analyze_alert_brief( payload: AlertBriefRequest, request: Request, response: Response, current_user: User = Depends(get_current_user), provider_client: AIProviderClient = Depends(get_ai_provider_client), db: AsyncSession = Depends(get_db), ): request_id = request.headers.get("X-Request-ID") or str(uuid4()) response.headers["X-Request-ID"] = request_id brief_request, facts, context = await build_alert_brief_request( db, alert_limit=payload.alert_limit, ) brief_request.preferred_model = payload.preferred_model brief_request.thinking = payload.thinking analysis = await provider_client.analyze(brief_request, request_id=request_id) return AlertBriefResponse( **analysis.model_dump(), title=brief_request.title, objective=brief_request.objective, facts=facts, context=context, ) @router.post("/situational-alerts/brief", response_model=SituationalAlertBriefResponse) async def analyze_situational_alert_brief( payload: SituationalAlertBriefRequest, request: Request, response: Response, current_user: User = Depends(get_current_user), provider_client: AIProviderClient = Depends(get_ai_provider_client), db: AsyncSession = Depends(get_db), ): request_id = request.headers.get("X-Request-ID") or str(uuid4()) response.headers["X-Request-ID"] = request_id brief_request, facts, context = await build_situational_alert_brief_request(db) brief_request.preferred_model = payload.preferred_model brief_request.thinking = payload.thinking analysis = await provider_client.analyze(brief_request, request_id=request_id) return SituationalAlertBriefResponse( **analysis.model_dump(), title=brief_request.title, objective=brief_request.objective, facts=facts, context=context, )