import * as PopoverPrimitive from "@radix-ui/react-popover"; import { IconThumbUp, IconThumbDown } from "@tabler/icons-react"; import { useState, useCallback } from "react"; import { agentNativePath } from "../api-path.js"; import { cn } from "../utils.js"; const THUMBS_DOWN_CATEGORIES = [ "Inaccurate", "Not helpful", "Wrong tool", "Too slow", ] as const; type ThumbsDownCategory = (typeof THUMBS_DOWN_CATEGORIES)[number]; export interface ThumbsFeedbackProps { threadId: string; runId: string; messageSeq: number; className?: string; } type Selection = "up" | "down" | null; export function ThumbsFeedback({ threadId, runId, messageSeq, className, }: ThumbsFeedbackProps) { const [selection, setSelection] = useState(null); const [categoryOpen, setCategoryOpen] = useState(false); const [submittedCategory, setSubmittedCategory] = useState( null, ); const sendFeedback = useCallback( async ( feedbackType: "thumbs_up" | "thumbs_down" | "category", value?: string, ) => { try { await fetch(agentNativePath("/_agent-native/observability/feedback"), { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ threadId, runId, messageSeq, feedbackType, value: value ?? "", }), }); } catch { // Fire-and-forget; don't block the UI on feedback submission failures } }, [threadId, runId, messageSeq], ); const handleThumbsUp = useCallback(() => { if (selection === "up") return; setSelection("up"); setCategoryOpen(false); setSubmittedCategory(null); sendFeedback("thumbs_up"); }, [selection, sendFeedback]); const handleThumbsDown = useCallback(() => { if (selection === "down") { setCategoryOpen((prev) => !prev); return; } setSelection("down"); setCategoryOpen(true); sendFeedback("thumbs_down"); }, [selection, sendFeedback]); const handleCategory = useCallback( (category: ThumbsDownCategory) => { setSubmittedCategory(category); setCategoryOpen(false); sendFeedback("category", category); }, [sendFeedback], ); return (
{THUMBS_DOWN_CATEGORIES.map((category) => ( ))}
); }