Files
Hotel-Booking/Backend/src/ai/routes/ai_assistant_routes.py
Iliyan Angelov 39fcfff811 update
2025-11-30 22:43:09 +02:00

296 lines
10 KiB
Python

"""
AI Assistant API Routes
"""
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from typing import Optional
from pydantic import BaseModel
from ...shared.config.database import get_db
from ...security.middleware.auth import get_current_user
from ...auth.models.user import User
from ...auth.models.role import Role
from ..services.ai_chat_service import AIChatService
from ..services.ai_assistant_service import AIAssistantService
from ..services.ai_learning_service import AILearningService
from ..services.ai_training_scheduler import get_training_scheduler
from ...shared.config.logging_config import get_logger
logger = get_logger(__name__)
router = APIRouter(prefix="/ai-assistant", tags=["AI Assistant"])
class ChatMessageRequest(BaseModel):
message: str
context: Optional[dict] = None
class ChatMessageResponse(BaseModel):
response: str
intent: str
data_used: dict
timestamp: str
@router.post("/chat")
async def chat_with_ai(
request: ChatMessageRequest,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
Chat with AI assistant
Accessible to all authenticated users (admin, staff, accountant, customer)
Responses are filtered based on user role
"""
try:
# Load user role
user_role = db.query(Role).filter(Role.id == current_user.role_id).first()
if not user_role:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="User role not found"
)
# Initialize AI chat service with current user for role-based responses
ai_service = AIChatService(db, current_user)
# Generate response with role awareness
result = ai_service.generate_response(
user_query=request.message,
context=request.context
)
return {
"status": "success",
"data": {
"response": result["response"],
"intent": result.get("intent", "unknown"),
"data_used": result.get("data_used", {}),
"timestamp": result.get("timestamp", ""),
"user_role": user_role.name
}
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error in AI chat: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=f"Error processing AI request: {str(e)}"
)
@router.get("/status")
async def get_ai_status(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""
Get comprehensive system status for AI assistant
Status is filtered based on user role
"""
try:
# Load user role
user_role = db.query(Role).filter(Role.id == current_user.role_id).first()
if not user_role:
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="User role not found"
)
# Initialize AI assistant with current user for role-based context
ai_assistant = AIAssistantService(db, current_user)
context = ai_assistant.generate_context_for_ai()
return {
"status": "success",
"data": context
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting AI status: {str(e)}", exc_info=True)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail=str(e)
)
@router.get("/rooms/occupied")
async def get_occupied_rooms(
room_number: Optional[str] = None,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Get list of occupied rooms - Admin and Staff only"""
try:
user_role = db.query(Role).filter(Role.id == current_user.role_id).first()
if not user_role or user_role.name not in ['staff', 'admin']:
raise HTTPException(status_code=403, detail="Only staff and admin can access this information")
ai_assistant = AIAssistantService(db, current_user)
rooms = ai_assistant.search_occupied_rooms(room_number)
return {"status": "success", "data": {"rooms": rooms}}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting occupied rooms: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
@router.get("/rooms/problems")
async def get_room_problems(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Get rooms with problems - Admin and Staff only"""
try:
user_role = db.query(Role).filter(Role.id == current_user.role_id).first()
if not user_role or user_role.name not in ['staff', 'admin']:
raise HTTPException(status_code=403, detail="Only staff and admin can access this information")
ai_assistant = AIAssistantService(db, current_user)
problems = ai_assistant.get_room_problems()
return {"status": "success", "data": {"problems": problems}}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting room problems: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
@router.get("/chats/unanswered")
async def get_unanswered_chats(
hours: int = 24,
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Get unanswered chat messages - Admin and Staff only"""
try:
user_role = db.query(Role).filter(Role.id == current_user.role_id).first()
if not user_role or user_role.name not in ['staff', 'admin']:
raise HTTPException(status_code=403, detail="Only staff and admin can access this information")
ai_assistant = AIAssistantService(db, current_user)
chats = ai_assistant.get_unanswered_chats(hours)
return {"status": "success", "data": {"chats": chats}}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting unanswered chats: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
@router.post("/training/trigger")
async def trigger_manual_training(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Manually trigger AI training - Admin only"""
try:
user_role = db.query(Role).filter(Role.id == current_user.role_id).first()
if not user_role or user_role.name != 'admin':
raise HTTPException(status_code=403, detail="Only admin can trigger manual training")
learning_service = AILearningService(db)
result = learning_service.auto_train_from_all_conversations()
return {
"status": "success",
"message": "Training completed",
"data": result
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error triggering training: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
@router.post("/training/analyze")
async def trigger_manual_analysis(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Manually trigger AI analysis and improvement - Admin only"""
try:
user_role = db.query(Role).filter(Role.id == current_user.role_id).first()
if not user_role or user_role.name != 'admin':
raise HTTPException(status_code=403, detail="Only admin can trigger manual analysis")
learning_service = AILearningService(db)
result = learning_service.auto_analyze_and_improve()
return {
"status": "success",
"message": "Analysis completed",
"data": result
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error triggering analysis: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))
@router.get("/training/status")
async def get_training_status(
current_user: User = Depends(get_current_user),
db: Session = Depends(get_db)
):
"""Get AI training status and metrics - Admin only"""
try:
user_role = db.query(Role).filter(Role.id == current_user.role_id).first()
if not user_role or user_role.name != 'admin':
raise HTTPException(status_code=403, detail="Only admin can view training status")
from ..models.ai_conversation import AIConversation, AILearnedPattern, AIKnowledgeEntry, AITrainingMetrics
from sqlalchemy import func
from datetime import datetime, timedelta
# Get statistics
total_conversations = db.query(AIConversation).count()
total_patterns = db.query(AILearnedPattern).filter(AILearnedPattern.is_active == True).count()
total_knowledge = db.query(AIKnowledgeEntry).count()
# Get recent metrics
recent_metrics = db.query(AITrainingMetrics).order_by(
AITrainingMetrics.metric_date.desc()
).limit(1).first()
# Get scheduler status
scheduler = get_training_scheduler()
return {
"status": "success",
"data": {
"scheduler_running": scheduler.running,
"last_training": scheduler.last_training_time.isoformat() if scheduler.last_training_time else None,
"last_analysis": scheduler.last_analysis_time.isoformat() if scheduler.last_analysis_time else None,
"statistics": {
"total_conversations": total_conversations,
"total_patterns": total_patterns,
"total_knowledge_entries": total_knowledge,
},
"recent_metrics": {
"average_rating": float(recent_metrics.average_rating) if recent_metrics and recent_metrics.average_rating else None,
"helpful_rate": float(recent_metrics.helpful_rate) if recent_metrics and recent_metrics.helpful_rate else None,
"average_response_time_ms": recent_metrics.average_response_time_ms if recent_metrics else None,
} if recent_metrics else None
}
}
except HTTPException:
raise
except Exception as e:
logger.error(f"Error getting training status: {str(e)}", exc_info=True)
raise HTTPException(status_code=500, detail=str(e))