import { Badge } from "@/src/components/ui/badge"; import { HoverCard, HoverCardContent, HoverCardTrigger, } from "@/src/components/ui/hover-card"; import { type LastUserScore, type ScoreDomain } from "@langfuse/shared"; import { BracesIcon, MessageCircleMoreIcon, ExternalLinkIcon, } from "lucide-react"; import { JSONView } from "@/src/components/ui/CodeJsonViewer"; import Link from "next/link"; import useProjectIdFromURL from "@/src/hooks/useProjectIdFromURL"; import { type WithStringifiedMetadata } from "@/src/utils/clientSideDomainTypes"; const partitionScores = < T extends WithStringifiedMetadata | LastUserScore, >( scores: Record, maxVisible?: number, ) => { const sortedScores = Object.entries(scores).sort(([a], [b]) => a < b ? -1 : 1, ); if (!maxVisible) return { visibleScores: sortedScores, hiddenScores: [] }; const visibleScores = sortedScores.slice(0, maxVisible); const hiddenScores = sortedScores.slice(maxVisible); return { visibleScores, hiddenScores }; }; const hasMetadata = ( score: WithStringifiedMetadata | LastUserScore, ) => { if (!score.metadata) return false; try { const metadata = typeof score.metadata === "string" ? JSON.parse(score.metadata) : score.metadata; return Object.keys(metadata).length > 0; } catch { return false; } }; const ScoreGroupBadge = < T extends WithStringifiedMetadata | LastUserScore, >({ name, scores, compact, badgeClassName, }: { name: string; scores: T[]; compact?: boolean; badgeClassName?: string; }) => { const projectId = useProjectIdFromURL(); return (
{name}:
{scores.map((s, i) => ( {s.stringValue ?? s.value?.toFixed(2) ?? ""} {s.comment && (

{s.comment}

{"executionTraceId" in s && s.executionTraceId && projectId && ( View execution trace )}
)} {hasMetadata(s) && ( )} ,
))}
); }; export const GroupedScoreBadges = < T extends WithStringifiedMetadata | LastUserScore, >({ scores, maxVisible, compact, badgeClassName, }: { scores: T[]; maxVisible?: number; compact?: boolean; badgeClassName?: string; }) => { const groupedScores = scores.reduce>((acc, score) => { if (!acc[score.name] || !Array.isArray(acc[score.name])) { acc[score.name] = [score]; } else { acc[score.name].push(score); } return acc; }, {}); const { visibleScores, hiddenScores } = partitionScores( groupedScores, maxVisible, ); return ( <> {visibleScores.map(([name, scores]) => ( ))} {Boolean(hiddenScores.length) && ( +{hiddenScores.length}
{hiddenScores.map(([name, scores]) => ( ))}
)} ); };