| from __future__ import annotations | |
| import json | |
| import uuid | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| from typing import Any | |
| from config import FEEDBACK_LOG_PATH | |
| FEEDBACK_CATEGORIES = [ | |
| "Doğru ve yararlı", | |
| "Yanlış kaynak", | |
| "Eksik cevap", | |
| "Kendini tekrar ediyor", | |
| "Bağlamı kaçırdı", | |
| "Başka konuya geçti", | |
| "Çok uzun", | |
| "Çok kısa", | |
| "Anlaşılır değil", | |
| "Diğer", | |
| ] | |
| REGRESSION_REVIEW_CATEGORIES = { | |
| "Yanlış kaynak", | |
| "Eksik cevap", | |
| "Kendini tekrar ediyor", | |
| "Bağlamı kaçırdı", | |
| "Başka konuya geçti", | |
| "Anlaşılır değil", | |
| } | |
| def save_feedback(chat_history: Any, rating: int | float, category: str, comment: str) -> str: | |
| user_message, assistant_message = _last_turn(chat_history) | |
| if not user_message and not assistant_message: | |
| return "Kaydedilecek bir diyalog bulunamadı." | |
| try: | |
| numeric_rating = int(float(rating)) | |
| except Exception: | |
| numeric_rating = 0 | |
| numeric_rating = max(1, min(numeric_rating, 10)) | |
| normalized_category = category or "Diğer" | |
| record = { | |
| "feedback_id": str(uuid.uuid4()), | |
| "created_at": datetime.now(timezone.utc).isoformat(), | |
| "rating": numeric_rating, | |
| "category": normalized_category, | |
| "comment": (comment or "").strip(), | |
| "user_message": user_message, | |
| "assistant_message": assistant_message, | |
| "needs_regression_review": _needs_regression_review(numeric_rating, normalized_category), | |
| } | |
| _append_jsonl(FEEDBACK_LOG_PATH, record) | |
| return "Geri bildirim kaydedildi. Teşekkürler." | |
| def _last_turn(chat_history: Any) -> tuple[str, str]: | |
| if not chat_history: | |
| return "", "" | |
| for item in reversed(chat_history): | |
| if isinstance(item, dict): | |
| role = item.get("role") | |
| if role == "assistant": | |
| assistant = str(item.get("content", "") or "").strip() | |
| user = _previous_user_message(chat_history, item) | |
| return user, assistant | |
| if isinstance(item, (list, tuple)) and len(item) >= 2: | |
| return str(item[0] or "").strip(), str(item[1] or "").strip() | |
| return "", "" | |
| def _previous_user_message(chat_history: Any, assistant_item: dict) -> str: | |
| try: | |
| assistant_index = chat_history.index(assistant_item) | |
| except ValueError: | |
| assistant_index = len(chat_history) | |
| for item in reversed(chat_history[:assistant_index]): | |
| if isinstance(item, dict) and item.get("role") == "user": | |
| return str(item.get("content", "") or "").strip() | |
| return "" | |
| def _append_jsonl(path: Path, record: dict) -> None: | |
| path.parent.mkdir(parents=True, exist_ok=True) | |
| with path.open("a", encoding="utf-8") as file: | |
| file.write(json.dumps(record, ensure_ascii=False) + "\n") | |
| def _needs_regression_review(rating: int, category: str) -> bool: | |
| return rating <= 5 or category in REGRESSION_REVIEW_CATEGORIES | |