Spaces:
Running
Running
| # backend/app/api/feedback.py | |
| # Feedback endpoint. Lets the widget surface thumbs-up / thumbs-down for any | |
| # completed interaction. Ratings are stored against the SQLite interaction row. | |
| # | |
| # Self-improvement loop: | |
| # rating=1 → positive signal written to DB; used by data_prep.py to build | |
| # high-quality reranker triplets (in-scope chunks that helped). | |
| # rating=-1 → negative signal; used by data_prep.py to build hard-negative | |
| # triplets (chunks the model surfaced that didn't help the user). | |
| # The retrain_reranker.yml GitHub Actions workflow aggregates these signals | |
| # and auto-triggers reranker fine-tuning once enough triplets accumulate. | |
| import sqlite3 | |
| from fastapi import APIRouter, Depends, HTTPException, Request, status | |
| from pydantic import BaseModel, Field | |
| from app.core.logging import get_logger | |
| from app.security.jwt_auth import verify_jwt | |
| router = APIRouter() | |
| logger = get_logger(__name__) | |
| class FeedbackRequest(BaseModel): | |
| interaction_id: int = Field(..., description="Row ID returned in the SSE done event.") | |
| rating: int = Field(..., description="1 for thumbs-up, -1 for thumbs-down.") | |
| model_config = {"json_schema_extra": {"example": {"interaction_id": 42, "rating": 1}}} | |
| async def submit_feedback( | |
| request: Request, | |
| body: FeedbackRequest, | |
| _token: dict = Depends(verify_jwt), | |
| ) -> None: | |
| """Record user feedback on a completed interaction. Idempotent — re-rating overwrites.""" | |
| if body.rating not in (1, -1): | |
| raise HTTPException( | |
| status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, | |
| detail="rating must be 1 (positive) or -1 (negative)", | |
| ) | |
| db_path = request.app.state.settings.DB_PATH | |
| try: | |
| with sqlite3.connect(db_path) as conn: | |
| cursor = conn.execute( | |
| "UPDATE interactions SET feedback = ? WHERE id = ?", | |
| (body.rating, body.interaction_id), | |
| ) | |
| if cursor.rowcount == 0: | |
| raise HTTPException( | |
| status_code=status.HTTP_404_NOT_FOUND, | |
| detail=f"Interaction {body.interaction_id} not found.", | |
| ) | |
| except HTTPException: | |
| raise | |
| except Exception as exc: | |
| logger.error("Feedback write failed for interaction %d: %s", body.interaction_id, exc) | |
| raise HTTPException( | |
| status_code=status.HTTP_503_SERVICE_UNAVAILABLE, | |
| detail="Feedback store unavailable.", | |
| ) | |