update
This commit is contained in:
295
Backend/src/ai/routes/ai_assistant_routes.py
Normal file
295
Backend/src/ai/routes/ai_assistant_routes.py
Normal file
@@ -0,0 +1,295 @@
|
||||
"""
|
||||
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))
|
||||
|
||||
Reference in New Issue
Block a user