import { Badge } from "@/src/components/ui/badge"; import { Button } from "@/src/components/ui/button"; import { MemoizedIOTableCell } from "@/src/components/ui/IOTableCell"; import { useActiveCell } from "@/src/features/datasets/contexts/ActiveCellContext"; import { useDatasetCompareFields } from "@/src/features/datasets/contexts/DatasetCompareFieldsContext"; import { api } from "@/src/utils/api"; import { formatIntervalSeconds } from "@/src/utils/dates"; import { cn } from "@/src/utils/tailwind"; import { ClockIcon, ListTree } from "lucide-react"; import { usdFormatter } from "@/src/utils/numbers"; import { type EnrichedDatasetRunItem } from "@langfuse/shared/src/server"; import { ScoreRow } from "@/src/features/scores/components/ScoreRow"; import { type ScoreColumn } from "@/src/features/scores/types"; import { useRouter } from "next/router"; import { useHasProjectAccess } from "@/src/features/rbac/utils/checkProjectAccess"; import { useMergedAggregates } from "@/src/features/scores/lib/useMergedAggregates"; import { useMergeScoreColumns } from "@/src/features/scores/lib/mergeScoreColumns"; import { useTrpcError } from "@/src/hooks/useTrpcError"; import { type ScoreAggregate } from "@langfuse/shared"; import { computeScoreDiffs } from "@/src/features/datasets/lib/computeScoreDiffs"; import { useMemo } from "react"; import { type BaselineDiff } from "@/src/features/datasets/lib/calculateBaselineDiff"; import { DiffLabel } from "@/src/features/datasets/components/DiffLabel"; import { useResourceMetricsDiff } from "@/src/features/datasets/hooks/useResourceMetricsDiff"; import { NotFoundCard } from "@/src/features/datasets/components/NotFoundCard"; const DatasetAggregateCellContent = ({ projectId, value, scores, serverScoreColumns, scoreDiffs, baselineRunValue, }: { projectId: string; value: EnrichedDatasetRunItem; scores: ScoreAggregate; serverScoreColumns: ScoreColumn[]; scoreDiffs?: Record; baselineRunValue?: EnrichedDatasetRunItem; }) => { const router = useRouter(); const silentHttpCodes = [404]; const { selectedFields } = useDatasetCompareFields(); const { activeCell, setActiveCell } = useActiveCell(); const hasAnnotationWriteAccess = useHasProjectAccess({ projectId, scope: "scores:CUD", }); // Merge server columns with cache-only columns const mergedScoreColumns = useMergeScoreColumns(serverScoreColumns); // Subtract 1 day from the run item creation timestamp as a buffer in case the trace happened before the run const fromTimestamp = new Date( value.createdAt.getTime() - 24 * 60 * 60 * 1000, ); // conditionally fetch the trace or observation depending on the presence of observationId const trace = api.traces.byId.useQuery( { traceId: value.trace.id, projectId, fromTimestamp }, { enabled: value.observation === undefined, refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, retry: false, staleTime: Infinity, meta: { silentHttpCodes }, }, ); const observation = api.observations.byId.useQuery( { observationId: value.observation?.id as string, // disabled when observationId is undefined projectId, traceId: value.trace.id, }, { enabled: value.observation !== undefined, refetchOnMount: false, refetchOnWindowFocus: false, refetchOnReconnect: false, retry: false, staleTime: Infinity, meta: { silentHttpCodes }, }, ); const data = value.observation === undefined ? trace.data : observation.data; const isLoading = value.observation === undefined ? trace.isLoading : observation.isLoading; const { isSilentError } = useTrpcError( value.observation === undefined ? trace.error : observation.error, silentHttpCodes, ); const { latency, totalCost, latencyDiff, totalCostDiff } = useResourceMetricsDiff(value, baselineRunValue); // Note that we implement custom handling for opening peek view from cell const handleOpenPeek = () => { const newQuery: Record = { ...router.query, peek: value.trace.id, }; // Always set observation - either to the ID or undefined to remove it if (value.observation?.id) { newQuery.observation = value.observation.id; } else { delete newQuery.observation; } router.push( { pathname: router.pathname, query: newQuery, }, undefined, { shallow: true }, ); }; const handleOpenReview = () => { setActiveCell({ traceId: value.trace.id, observationId: value.observation?.id, scoreAggregates: scores, environment: data?.environment, }); }; const isActiveCell = activeCell?.traceId === value.trace.id && activeCell?.observationId === value.observation?.id; return (
{/* Displays trace/observation output */}
{isSilentError ? ( ) : ( )}
{/* Displays scores */}
{mergedScoreColumns.length > 0 ? ( mergedScoreColumns.map((scoreColumn) => ( )) ) : ( No scores )}
{/* Displays resource metrics and action buttons */} {!isLoading && (
{!!latency && (latencyDiff ? ( formatIntervalSeconds(value)} className="ml-1" /> ) : ( {formatIntervalSeconds(latency)} ))} {totalCost && (totalCostDiff ? ( usdFormatter(value, 2, 4)} className="ml-1" /> ) : ( {usdFormatter(totalCost)} ))}
{!(isSilentError || isLoading) && (
{/* Triggers review/annotation */} {/* Triggers peek view */}
)}
)}
); }; const DatasetAggregateCellAgainstBaseline = ({ value, projectId, serverScoreColumns, baselineRunValue, }: { projectId: string; value: EnrichedDatasetRunItem; serverScoreColumns: ScoreColumn[]; baselineRunValue: EnrichedDatasetRunItem; }) => { // Merge cached score writes into aggregates for optimistic display const displayScores = useMergedAggregates( value.scores, value.trace.id, value.observation?.id, ); const baselineScores = useMergedAggregates( baselineRunValue.scores, baselineRunValue.trace.id, baselineRunValue.observation?.id, ); // Compute diffs between current and baseline scores const scoreDiffs = useMemo( () => computeScoreDiffs(displayScores, baselineScores), [displayScores, baselineScores], ); return ( ); }; const DatasetAggregateCell = ({ value, projectId, serverScoreColumns, }: { projectId: string; value: EnrichedDatasetRunItem; serverScoreColumns: ScoreColumn[]; }) => { // Merge cached score writes into aggregates for optimistic display const displayScores = useMergedAggregates( value.scores, value.trace.id, value.observation?.id, ); return ( ); }; type DatasetAggregateTableCellProps = { projectId: string; value: EnrichedDatasetRunItem; serverScoreColumns: ScoreColumn[]; isBaselineRun: boolean; baselineRunValue?: EnrichedDatasetRunItem; }; export const DatasetAggregateTableCell = ({ projectId, value, serverScoreColumns, isBaselineRun, baselineRunValue, }: DatasetAggregateTableCellProps) => { return baselineRunValue && !isBaselineRun ? ( ) : ( ); };