Download src/chatbot/feedback.py from SolarWine/api: direct link, hf CLI and curl.
- Browser
- Download file 3.09 kB
-
https://huggingface.co/spaces/SolarWine/api/resolve/main/src/chatbot/feedback.py
- Command line
-
hf download hf://spaces/SolarWine/api/src/chatbot/feedback.py
-
curl -L -o feedback.py https://huggingface.co/spaces/SolarWine/api/resolve/main/src/chatbot/feedback.py
3.09 kB
| """ | |
| Feedback storage for the Vineyard Advisor chatbot. | |
| Logs user feedback (thumbs up/down, flags) to a JSON-lines file. | |
| Each entry captures the query, response, tool results, rules applied, | |
| and the user's feedback action. | |
| """ | |
| from __future__ import annotations | |
| import json | |
| import logging | |
| from datetime import datetime, timezone | |
| from pathlib import Path | |
| from typing import Optional | |
| from config.settings import DATA_DIR | |
| logger = logging.getLogger(__name__) | |
| FEEDBACK_FILE = DATA_DIR / "advisor_feedback.jsonl" | |
| def log_feedback( | |
| query: str, | |
| response: str, | |
| feedback: str, | |
| confidence: str = "", | |
| sources: Optional[list[str]] = None, | |
| tool_calls: Optional[list[dict]] = None, | |
| rule_violations: Optional[list[dict]] = None, | |
| response_mode: str = "", | |
| comment: str = "", | |
| ) -> None: | |
| """Append a feedback entry to the JSONL file. | |
| Parameters | |
| ---------- | |
| query : str | |
| The user's original question. | |
| response : str | |
| The chatbot's response text. | |
| feedback : str | |
| One of: "thumbs_up", "thumbs_down", "flag_incorrect". | |
| confidence, sources, tool_calls, rule_violations, response_mode : | |
| Metadata from the ChatResponse. | |
| comment : str | |
| Optional free-text comment from the user. | |
| """ | |
| entry = { | |
| "timestamp": datetime.now(tz=timezone.utc).isoformat(), | |
| "query": query, | |
| "response": response[:500], # truncate for storage | |
| "feedback": feedback, | |
| "confidence": confidence, | |
| "sources": sources or [], | |
| "tool_calls": [ | |
| {"name": tc.get("name", ""), "args": tc.get("args", {})} | |
| for tc in (tool_calls or []) | |
| ], | |
| "rule_violations": rule_violations or [], | |
| "response_mode": response_mode, | |
| "comment": comment, | |
| } | |
| try: | |
| FEEDBACK_FILE.parent.mkdir(parents=True, exist_ok=True) | |
| with open(FEEDBACK_FILE, "a") as f: | |
| f.write(json.dumps(entry, default=str) + "\n") | |
| logger.info("Feedback logged: %s for query: %s", feedback, query[:50]) | |
| except Exception as exc: | |
| logger.warning("Failed to log feedback: %s", exc) | |
| def load_feedback(limit: int = 100) -> list[dict]: | |
| """Load recent feedback entries.""" | |
| if not FEEDBACK_FILE.exists(): | |
| return [] | |
| entries = [] | |
| try: | |
| with open(FEEDBACK_FILE) as f: | |
| for line in f: | |
| line = line.strip() | |
| if line: | |
| entries.append(json.loads(line)) | |
| except Exception as exc: | |
| logger.warning("Failed to load feedback: %s", exc) | |
| return entries[-limit:] | |
| def feedback_summary() -> dict: | |
| """Return a summary of feedback stats.""" | |
| entries = load_feedback(limit=10000) | |
| if not entries: | |
| return {"total": 0} | |
| return { | |
| "total": len(entries), | |
| "thumbs_up": sum(1 for e in entries if e.get("feedback") == "thumbs_up"), | |
| "thumbs_down": sum(1 for e in entries if e.get("feedback") == "thumbs_down"), | |
| "flagged": sum(1 for e in entries if e.get("feedback") == "flag_incorrect"), | |
| } | |