// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates // SPDX-License-Identifier: MIT import { BookOpen, FileText, Image, Link2, Loader2, Sparkles, ThumbsDown, ThumbsUp, } from "lucide-react"; import { useTranslations } from "next-intl"; import { useCallback, useEffect, useRef, useState } from "react"; import { Button } from "~/components/ui/button"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, } from "~/components/ui/dialog"; import { Progress } from "~/components/ui/progress"; import { evaluateReport, type EvaluationResult } from "~/core/api"; import { cn } from "~/lib/utils"; interface EvaluationDialogProps { open: boolean; onOpenChange: (open: boolean) => void; reportContent: string; query: string; reportStyle?: string; } function GradeBadge({ grade }: { grade: string }) { const gradeColors: Record = { "A+": "bg-emerald-500", A: "bg-emerald-500", "A-": "bg-emerald-400", "B+": "bg-blue-500", B: "bg-blue-500", "B-": "bg-blue-400", "C+": "bg-yellow-500", C: "bg-yellow-500", "C-": "bg-yellow-400", D: "bg-orange-500", F: "bg-red-500", }; return (
{grade}
); } function MetricItem({ icon: Icon, label, value, suffix, }: { icon: React.ComponentType<{ className?: string }>; label: string; value: number | string; suffix?: string; }) { return (
{label} {value} {suffix}
); } export function EvaluationDialog({ open, onOpenChange, reportContent, query, reportStyle, }: EvaluationDialogProps) { const t = useTranslations("chat.evaluation"); const [loading, setLoading] = useState(false); const [deepLoading, setDeepLoading] = useState(false); const [result, setResult] = useState(null); const [error, setError] = useState(null); const hasRunInitialEvaluation = useRef(false); const runEvaluation = useCallback( async (useLlm: boolean) => { if (useLlm) { setDeepLoading(true); } else { setLoading(true); } setError(null); try { const evalResult = await evaluateReport( reportContent, query, reportStyle, useLlm, ); setResult(evalResult); } catch (err) { setError(err instanceof Error ? err.message : "Evaluation failed"); } finally { setLoading(false); setDeepLoading(false); } }, [reportContent, query, reportStyle], ); useEffect(() => { if (open && !hasRunInitialEvaluation.current) { hasRunInitialEvaluation.current = true; void runEvaluation(false); } }, [open, runEvaluation]); useEffect(() => { if (!open) { setResult(null); setError(null); hasRunInitialEvaluation.current = false; } }, [open]); return ( {t("title")} {t("description")} {loading && !result ? (

{t("evaluating")}

) : error ? (
{error}
) : result ? (
{/* Grade and Score */}
{result.score}/10
{t("overallScore")}
{/* Metrics */}

{t("metrics")}

{t("sectionCoverage")} {Math.round(result.metrics.section_coverage_score * 100)}%
{/* LLM Evaluation Results */} {result.llm_evaluation && (

{t("detailedAnalysis")}

{/* LLM Scores */}
{Object.entries(result.llm_evaluation.scores).map( ([key, value]) => (
{t(`scores.${key}`)} {value}/10
), )}
{/* Strengths */} {result.llm_evaluation.strengths.length > 0 && (
{t("strengths")}
    {result.llm_evaluation.strengths .slice(0, 3) .map((s, i) => (
  • • {s}
  • ))}
)} {/* Weaknesses */} {result.llm_evaluation.weaknesses.length > 0 && (
{t("weaknesses")}
    {result.llm_evaluation.weaknesses .slice(0, 3) .map((w, i) => (
  • • {w}
  • ))}
)}
)} {/* Deep Evaluation Button */} {!result.llm_evaluation && ( )}
) : null}
); }