diff --git a/agenta-web/src/components/pages/evaluations/cellRenderers/cellRenderers.tsx b/agenta-web/src/components/pages/evaluations/cellRenderers/cellRenderers.tsx index 94a468f599..61a5195dd5 100644 --- a/agenta-web/src/components/pages/evaluations/cellRenderers/cellRenderers.tsx +++ b/agenta-web/src/components/pages/evaluations/cellRenderers/cellRenderers.tsx @@ -186,7 +186,7 @@ export const StatusRenderer = React.memo( params.data?.duration || 0, runningStatuses.includes(params.value), ) - const {label, color} = statusMapper(token)[params.value.value as EvaluationStatus] + const {label, color} = statusMapper(token)[params.data?.status.value as EvaluationStatus] const errorMsg = params.data?.status.error?.message return ( diff --git a/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx b/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx index be9a2d5ad3..428c6dea0a 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationCompare/EvaluationCompare.tsx @@ -28,6 +28,8 @@ import CompareOutputDiff from "@/components/CompareOutputDiff/CompareOutputDiff" import {formatCurrency, formatLatency} from "@/lib/helpers/formatters" import FilterColumns, {generateFilterItems} from "../FilterColumns/FilterColumns" import _ from "lodash" +import {variantNameWithRev} from "@/lib/helpers/variantHelper" +import {escapeNewlines} from "@/lib/helpers/fileManipulations" const useStyles = createUseStyles((theme: JSSTheme) => ({ table: { @@ -78,6 +80,7 @@ const EvaluationCompareMode: React.FC = () => { const [evalIds, setEvalIds] = useState(evaluationIdsArray) const [hiddenVariants, setHiddenVariants] = useState([]) const [fetching, setFetching] = useState(false) + const [scenarios, setScenarios] = useState<_Evaluation[]>([]) const [rows, setRows] = useState([]) const [testset, setTestset] = useState() const [evaluators] = useAtom(evaluatorsAtom) @@ -238,7 +241,7 @@ const EvaluationCompareMode: React.FC = () => { }) Object.entries(confgisMap).forEach(([_, configs]) => { - configs.forEach(({config, variant, color}) => { + configs.forEach(({config, variant, color}, idx) => { colDefs.push({ flex: 1, minWidth: 200, @@ -259,6 +262,7 @@ const EvaluationCompareMode: React.FC = () => { ) }, headerName: config.name, + type: `evaluator_${idx}`, field: "variants.0.evaluatorConfigs.0.result" as any, ...getFilterParams("text"), hide: hiddenVariants.includes(config.name), @@ -288,6 +292,7 @@ const EvaluationCompareMode: React.FC = () => { hide: hiddenVariants.includes("Latency"), minWidth: 120, headerName: "Latency", + field: `latency.${vi}` as any, flex: 1, valueGetter: (params) => { const latency = params.data?.variants.find( @@ -309,6 +314,7 @@ const EvaluationCompareMode: React.FC = () => { ), + field: `cost.${vi}` as any, headerName: "Cost", minWidth: 120, hide: !evalIds.includes(variant.evaluationId) || hiddenVariants.includes("Cost"), @@ -329,7 +335,8 @@ const EvaluationCompareMode: React.FC = () => { const fetcher = () => { setFetching(true) fetchAllComparisonResults(evaluationIds) - .then(({rows, testset}) => { + .then(({rows, testset, evaluations}) => { + setScenarios(evaluations) setRows(rows) setTestset(testset) setTimeout(() => { @@ -371,13 +378,41 @@ const EvaluationCompareMode: React.FC = () => { [colDefs], ) - const onExport = () => { - if (!gridRef.current) return + const getDynamicHeaderName = (params: ColDef): string => { + const {headerName, field, type}: any = params + + const getVariantNameWithRev = (index: number): string => { + const scenario = scenarios[index] + const variantName = scenario?.variants[0]?.variantName ?? "" + const revision = scenario?.revisions[0] ?? "" + return variantNameWithRev({variant_name: variantName, revision}) + } + + if (headerName === "Output" || headerName === "Latency" || headerName === "Cost") { + const index = Number(field.split(".")[1]) + return `${headerName} ${getVariantNameWithRev(index)}` + } + + if (type && type.startsWith("evaluator")) { + const index = Number(type.split("_")[1]) + return `${headerName} ${getVariantNameWithRev(index)}` + } + + return headerName + } + + const onExport = (): void => { + const gridApi = gridRef.current?.api + if (!gridApi) return + const {currentApp} = getAppValues() - gridRef.current.api.exportDataAsCsv({ - fileName: `${currentApp?.app_name}_${variants - .map(({variantName}) => variantName) - .join("_")}.csv`, + const fileName = `${currentApp?.app_name ?? "export"}_${variants.map(({variantName}) => variantName).join("_")}.csv` + + gridApi.exportDataAsCsv({ + fileName, + processHeaderCallback: (params) => getDynamicHeaderName(params.column.getColDef()), + processCellCallback: (params) => + typeof params.value === "string" ? escapeNewlines(params.value) : params.value, }) } @@ -396,35 +431,29 @@ const EvaluationCompareMode: React.FC = () => { Variants:
- {variants?.map((v, vi) => ( + {scenarios?.map((v, vi) => ( - handleToggleVariantVisibility( - v.evaluationId, - ) + handleToggleVariantVisibility(v.id) } style={{cursor: "pointer"}} /> ) : ( - handleToggleVariantVisibility( - v.evaluationId, - ) + handleToggleVariantVisibility(v.id) } style={{cursor: "pointer"}} /> @@ -432,9 +461,12 @@ const EvaluationCompareMode: React.FC = () => { } > - {v.variantName} + {variantNameWithRev({ + variant_name: v.variants[0].variantName ?? "", + revision: v.revisions[0], + })} ))} diff --git a/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx b/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx index 1d8591af0b..a3245c19cd 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx @@ -4,8 +4,14 @@ import {useAppTheme} from "@/components/Layout/ThemeContextProvider" import {ColDef} from "ag-grid-community" import {createUseStyles} from "react-jss" import {Button, DropdownProps, Space, Spin, Tag, Tooltip, theme} from "antd" -import {DeleteOutlined, PlusCircleOutlined, SlidersOutlined, SwapOutlined} from "@ant-design/icons" -import {EvaluationStatus, JSSTheme, _Evaluation} from "@/lib/Types" +import { + DeleteOutlined, + DownloadOutlined, + PlusCircleOutlined, + SlidersOutlined, + SwapOutlined, +} from "@ant-design/icons" +import {EvaluationStatus, GenericObject, JSSTheme, _Evaluation} from "@/lib/Types" import {uniqBy} from "lodash" import dayjs from "dayjs" import relativeTime from "dayjs/plugin/relativeTime" @@ -32,6 +38,9 @@ import {calcEvalDuration, getFilterParams, getTypedValue} from "@/lib/helpers/ev import Link from "next/link" import FilterColumns, {generateFilterItems} from "../FilterColumns/FilterColumns" import {variantNameWithRev} from "@/lib/helpers/variantHelper" +import {getAppValues} from "@/contexts/app.context" +import {convertToCsv, downloadCsv} from "@/lib/helpers/fileManipulations" +import {formatDate24} from "@/lib/helpers/dateTimeHelper" dayjs.extend(relativeTime) dayjs.extend(duration) @@ -284,6 +293,8 @@ const EvaluationResults: React.FC = () => { filterValueGetter: (params) => statusMapper(token)[params.data?.status.value as EvaluationStatus].label, cellRenderer: StatusRenderer, + valueGetter: (params) => + statusMapper(token)[params.data?.status.value as EvaluationStatus].label, }, { flex: 1, @@ -312,6 +323,7 @@ const EvaluationResults: React.FC = () => { ...getFilterParams("date"), cellRenderer: DateFromNowRenderer, sort: "desc", + valueFormatter: (params) => formatDate24(params.value), }, ] return colDefs @@ -356,6 +368,38 @@ const EvaluationResults: React.FC = () => { } } + const onExport = () => { + if (!gridRef.current) return + const {currentApp} = getAppValues() + const filename = `${currentApp?.app_name}_evaluation_scenarios.csv` + if (!!selected.length) { + const csvData = convertToCsv( + selected.map((item) => ({ + Variant: variantNameWithRev({ + variant_name: item.variants[0].variantName ?? "", + revision: item.revisions[0], + }), + Testset: item.testset.name, + ...item.aggregated_results.reduce((acc, curr) => { + if (!acc[curr.evaluator_config.name]) { + acc[curr.evaluator_config.name] = getTypedValue(curr.result) + } + return acc + }, {} as GenericObject), + "Avg. Latency": getTypedValue(item.average_latency), + "Total Cost": getTypedValue(item.average_cost), + Created: formatDate24(item.created_at), + Status: statusMapper(token)[item.status.value as EvaluationStatus].label, + })), + colDefs.map((col) => col.headerName!), + ) + downloadCsv(csvData, filename) + } else { + gridRef.current.api.exportDataAsCsv({ + fileName: filename, + }) + } + } return ( <> {!fetching && !evaluations.length ? ( @@ -410,6 +454,9 @@ const EvaluationResults: React.FC = () => { setIsFilterColsDropdownOpen(true) }} /> + diff --git a/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx b/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx index 908ada0e0c..0d774a5ee7 100644 --- a/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx +++ b/agenta-web/src/components/pages/evaluations/evaluationScenarios/EvaluationScenarios.tsx @@ -25,6 +25,8 @@ import CompareOutputDiff from "@/components/CompareOutputDiff/CompareOutputDiff" import {formatCurrency, formatLatency} from "@/lib/helpers/formatters" import _ from "lodash" import FilterColumns, {generateFilterItems} from "../FilterColumns/FilterColumns" +import {variantNameWithRev} from "@/lib/helpers/variantHelper" +import {escapeNewlines} from "@/lib/helpers/fileManipulations" const useStyles = createUseStyles((theme: JSSTheme) => ({ infoRow: { @@ -270,6 +272,17 @@ const EvaluationScenarios: React.FC = () => { const {currentApp} = getAppValues() gridRef.current.api.exportDataAsCsv({ fileName: `${currentApp?.app_name}_${evalaution.variants[0].variantName}.csv`, + processHeaderCallback: (params) => { + if (params.column.getColDef().headerName === "Output") { + return `Output ${variantNameWithRev({ + variant_name: evalaution?.variants[0].variantName ?? "", + revision: evalaution.revisions[0], + })}` + } + return params.column.getColDef().headerName as string + }, + processCellCallback: (params) => + typeof params.value === "string" ? escapeNewlines(params.value) : params.value, }) } @@ -303,7 +316,10 @@ const EvaluationScenarios: React.FC = () => { - {evalaution?.variants[0].variantName || ""} + {variantNameWithRev({ + variant_name: evalaution?.variants[0].variantName ?? "", + revision: evalaution?.revisions[0], + })}