Spaces:
Running
Running
| """ | |
| Feedback API Routes | |
| Endpoints for collecting user feedback on search results. | |
| """ | |
| from fastapi import APIRouter, HTTPException, Depends | |
| from pydantic import BaseModel, Field | |
| from typing import Optional, Dict, Any | |
| import logging | |
| from src.infrastructure.adapters.feedback_tracker import feedback_tracker | |
| logger = logging.getLogger(__name__) | |
| router = APIRouter(prefix="/feedback", tags=["feedback"]) | |
| # ═══════════════════════════════════════════════════════════════════════════ | |
| # REQUEST MODELS | |
| # ═══════════════════════════════════════════════════════════════════════════ | |
| class ThumbsFeedback(BaseModel): | |
| """Thumbs up/down feedback""" | |
| session_id: str = Field(..., description="User session ID") | |
| query: str = Field(..., description="Original query") | |
| thumbs_up: bool = Field(..., description="True for thumbs up, False for thumbs down") | |
| comment: Optional[str] = Field(None, description="Optional comment") | |
| query_metadata: Dict[str, Any] = Field(..., description="Query metadata from response") | |
| class SourceRating(BaseModel): | |
| """Rating for a specific source""" | |
| session_id: str = Field(..., description="User session ID") | |
| query: str = Field(..., description="Original query") | |
| source_name: str = Field(..., description="Source name") | |
| rating: int = Field(..., ge=1, le=5, description="Rating from 1-5") | |
| comment: Optional[str] = Field(None, description="Optional comment") | |
| query_metadata: Dict[str, Any] = Field(..., description="Query metadata from response") | |
| class IntentCorrection(BaseModel): | |
| """Correction for intent classification""" | |
| session_id: str = Field(..., description="User session ID") | |
| query: str = Field(..., description="Original query") | |
| classified_intent: str = Field(..., description="Intent that was classified") | |
| correct_intent: str = Field(..., description="Correct intent") | |
| comment: Optional[str] = Field(None, description="Optional comment") | |
| query_metadata: Dict[str, Any] = Field(..., description="Query metadata from response") | |
| # ═══════════════════════════════════════════════════════════════════════════ | |
| # ENDPOINTS | |
| # ═══════════════════════════════════════════════════════════════════════════ | |
| async def submit_thumbs_feedback(feedback: ThumbsFeedback): | |
| """ | |
| Submit thumbs up/down feedback on search results. | |
| This helps us understand which results are helpful and which aren't. | |
| """ | |
| try: | |
| if not feedback_tracker: | |
| raise HTTPException(status_code=503, detail="Feedback system not available") | |
| feedback_tracker.record_feedback( | |
| session_id=feedback.session_id, | |
| query=feedback.query, | |
| feedback_type="thumbs_up" if feedback.thumbs_up else "thumbs_down", | |
| feedback_value=feedback.thumbs_up, | |
| query_metadata=feedback.query_metadata, | |
| feedback_comment=feedback.comment | |
| ) | |
| return { | |
| "status": "success", | |
| "message": "Thank you for your feedback!", | |
| "feedback_type": "thumbs_up" if feedback.thumbs_up else "thumbs_down" | |
| } | |
| except Exception as e: | |
| logger.error(f"Failed to submit thumbs feedback: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def submit_source_rating(rating: SourceRating): | |
| """ | |
| Submit rating for a specific source. | |
| This helps us understand which sources provide the best information. | |
| """ | |
| try: | |
| if not feedback_tracker: | |
| raise HTTPException(status_code=503, detail="Feedback system not available") | |
| feedback_tracker.record_feedback( | |
| session_id=rating.session_id, | |
| query=rating.query, | |
| feedback_type="source_rating", | |
| feedback_value={"source": rating.source_name, "rating": rating.rating}, | |
| query_metadata=rating.query_metadata, | |
| feedback_comment=rating.comment | |
| ) | |
| return { | |
| "status": "success", | |
| "message": f"Thank you for rating {rating.source_name}!", | |
| "source": rating.source_name, | |
| "rating": rating.rating | |
| } | |
| except Exception as e: | |
| logger.error(f"Failed to submit source rating: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def submit_intent_correction(correction: IntentCorrection): | |
| """ | |
| Submit correction for intent classification. | |
| This helps us improve our understanding of what users are looking for. | |
| """ | |
| try: | |
| if not feedback_tracker: | |
| raise HTTPException(status_code=503, detail="Feedback system not available") | |
| feedback_tracker.record_feedback( | |
| session_id=correction.session_id, | |
| query=correction.query, | |
| feedback_type="intent_correction", | |
| feedback_value={ | |
| "classified": correction.classified_intent, | |
| "correct": correction.correct_intent | |
| }, | |
| query_metadata=correction.query_metadata, | |
| feedback_comment=correction.comment | |
| ) | |
| return { | |
| "status": "success", | |
| "message": "Thank you for the correction!", | |
| "classified_intent": correction.classified_intent, | |
| "correct_intent": correction.correct_intent | |
| } | |
| except Exception as e: | |
| logger.error(f"Failed to submit intent correction: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def get_feedback_stats(days: int = 7): | |
| """ | |
| Get feedback statistics for the last N days. | |
| Args: | |
| days: Number of days to analyze (default: 7) | |
| Returns: | |
| Feedback statistics including counts, averages, and accuracy metrics | |
| """ | |
| try: | |
| if not feedback_tracker: | |
| raise HTTPException(status_code=503, detail="Feedback system not available") | |
| stats = feedback_tracker.get_feedback_stats(days=days) | |
| accuracy = feedback_tracker.get_intent_accuracy(days=days) | |
| return { | |
| "status": "success", | |
| "feedback_stats": stats, | |
| "intent_accuracy": accuracy | |
| } | |
| except Exception as e: | |
| logger.error(f"Failed to get feedback stats: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |
| async def get_low_confidence_queries(threshold: float = 0.7, limit: int = 100): | |
| """ | |
| Get queries with low intent classification confidence. | |
| Args: | |
| threshold: Confidence threshold (default: 0.7) | |
| limit: Maximum number of queries (default: 100) | |
| Returns: | |
| List of low-confidence queries for review | |
| """ | |
| try: | |
| if not feedback_tracker: | |
| raise HTTPException(status_code=503, detail="Feedback system not available") | |
| queries = feedback_tracker.get_low_confidence_queries( | |
| threshold=threshold, | |
| limit=limit | |
| ) | |
| return { | |
| "status": "success", | |
| "threshold": threshold, | |
| "count": len(queries), | |
| "queries": queries | |
| } | |
| except Exception as e: | |
| logger.error(f"Failed to get low confidence queries: {e}") | |
| raise HTTPException(status_code=500, detail=str(e)) | |