import { api } from "@/src/utils/api"; import { DashboardCard } from "@/src/features/dashboard/components/cards/DashboardCard"; import { type ScoreDataTypeType, type FilterState } from "@langfuse/shared"; import { type DashboardDateRangeAggregationOption } from "@/src/utils/date-range-utils"; import { MultiSelectKeyValues } from "@/src/features/scores/components/multi-select-key-values"; import React, { useMemo } from "react"; import { Separator } from "@/src/components/ui/separator"; import { isBooleanDataType, isCategoricalDataType, isNumericDataType, } from "@/src/features/scores/lib/helpers"; import { NumericScoreTimeSeriesChart } from "@/src/features/dashboard/components/score-analytics/NumericScoreTimeSeriesChart"; import { CategoricalScoreChart } from "@/src/features/dashboard/components/score-analytics/CategoricalScoreChart"; import { NumericScoreHistogram } from "@/src/features/dashboard/components/score-analytics/NumericScoreHistogram"; import DocPopup from "@/src/components/layouts/doc-popup"; import { NoDataOrLoading } from "@/src/components/NoDataOrLoading"; import useLocalStorage from "@/src/components/useLocalStorage"; import { convertScoreColumnsToAnalyticsData, getScoreDataTypeIcon, } from "@/src/features/scores/lib/scoreColumns"; export function ScoreAnalytics(props: { className?: string; agg: DashboardDateRangeAggregationOption; globalFilterState: FilterState; fromTimestamp: Date; toTimestamp: Date; projectId: string; isLoading?: boolean; }) { // Stale score selections in localStorage are ignored as we only show scores that exist in scoreAnalyticsOptions const [selectedDashboardScoreKeys, setSelectedDashboardScoreKeys] = useLocalStorage( `selectedDashboardScoreKeys-${props.projectId}`, [], ); const scoreKeysAndProps = api.scores.getScoreColumns.useQuery( { projectId: props.projectId, fromTimestamp: props.fromTimestamp, toTimestamp: props.toTimestamp, }, { enabled: !props.isLoading, }, ); const { scoreAnalyticsOptions, scoreKeyToData } = useMemo( () => convertScoreColumnsToAnalyticsData(scoreKeysAndProps.data?.scoreColumns), [scoreKeysAndProps.data], ); const scoreAnalyticsValues = scoreAnalyticsOptions?.filter((option) => selectedDashboardScoreKeys.includes(option.key), ); return ( { if (values.length === 0) setSelectedDashboardScoreKeys([]); if (changedValueId) { if (selectedValueKeys?.has(changedValueId)) { setSelectedDashboardScoreKeys([ ...selectedDashboardScoreKeys, changedValueId, ]); } else { setSelectedDashboardScoreKeys( selectedDashboardScoreKeys.filter( (key) => key !== changedValueId, ), ); } } }} values={scoreAnalyticsValues} options={scoreAnalyticsOptions} /> ) } > {Boolean(scoreKeysAndProps.data?.scoreColumns.length) && Boolean(scoreAnalyticsValues.length) ? (
{scoreAnalyticsValues.map(({ key: scoreKey }, index) => { const scoreData = scoreKeyToData.get(scoreKey); if (!scoreData) return null; const { name, dataType, source } = scoreData; return (
{`${getScoreDataTypeIcon(dataType)} ${name} (${source.toLowerCase()})`}
{/* aggregate */}
Total aggregate scores {isNumericDataType(dataType) && ( )}
{isCategoricalDataType(dataType) && ( )} {(isNumericDataType(dataType) || isBooleanDataType(dataType)) && ( } globalFilterState={props.globalFilterState} /> )}
{/* timeseries */}
{isNumericDataType(dataType) ? "Moving average over time" : "Scores over time"}
{isCategoricalDataType(dataType) && ( )} {(isNumericDataType(dataType) || isBooleanDataType(dataType)) && ( } projectId={props.projectId} globalFilterState={props.globalFilterState} fromTimestamp={props.fromTimestamp} toTimestamp={props.toTimestamp} /> )}
{scoreAnalyticsValues.length - 1 > index && ( )}
); })}
) : Boolean(scoreKeysAndProps.data?.scoreColumns.length) ? (

Select a score to view analytics

) : ( )}
); }