Skip to content

Commit

Permalink
Merge pull request #1763 from Agenta-AI/AGE-262/-export-evaluation-re…
Browse files Browse the repository at this point in the history
…sults-from-the-UI

[Feature]: Export results in the Evaluation Scenarios view
  • Loading branch information
mmabrouk authored Jun 6, 2024
2 parents 0a2cfb5 + 9b1af61 commit c6ab18c
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -78,6 +80,7 @@ const EvaluationCompareMode: React.FC<Props> = () => {
const [evalIds, setEvalIds] = useState(evaluationIdsArray)
const [hiddenVariants, setHiddenVariants] = useState<string[]>([])
const [fetching, setFetching] = useState(false)
const [scenarios, setScenarios] = useState<_Evaluation[]>([])
const [rows, setRows] = useState<ComparisonResultRow[]>([])
const [testset, setTestset] = useState<TestSet>()
const [evaluators] = useAtom(evaluatorsAtom)
Expand Down Expand Up @@ -238,7 +241,7 @@ const EvaluationCompareMode: React.FC<Props> = () => {
})

Object.entries(confgisMap).forEach(([_, configs]) => {
configs.forEach(({config, variant, color}) => {
configs.forEach(({config, variant, color}, idx) => {
colDefs.push({
flex: 1,
minWidth: 200,
Expand All @@ -259,6 +262,7 @@ const EvaluationCompareMode: React.FC<Props> = () => {
)
},
headerName: config.name,
type: `evaluator_${idx}`,
field: "variants.0.evaluatorConfigs.0.result" as any,
...getFilterParams("text"),
hide: hiddenVariants.includes(config.name),
Expand Down Expand Up @@ -288,6 +292,7 @@ const EvaluationCompareMode: React.FC<Props> = () => {
hide: hiddenVariants.includes("Latency"),
minWidth: 120,
headerName: "Latency",
field: `latency.${vi}` as any,
flex: 1,
valueGetter: (params) => {
const latency = params.data?.variants.find(
Expand All @@ -309,6 +314,7 @@ const EvaluationCompareMode: React.FC<Props> = () => {
</Space>
</AgCustomHeader>
),
field: `cost.${vi}` as any,
headerName: "Cost",
minWidth: 120,
hide: !evalIds.includes(variant.evaluationId) || hiddenVariants.includes("Cost"),
Expand All @@ -329,7 +335,8 @@ const EvaluationCompareMode: React.FC<Props> = () => {
const fetcher = () => {
setFetching(true)
fetchAllComparisonResults(evaluationIds)
.then(({rows, testset}) => {
.then(({rows, testset, evaluations}) => {
setScenarios(evaluations)
setRows(rows)
setTestset(testset)
setTimeout(() => {
Expand Down Expand Up @@ -371,13 +378,41 @@ const EvaluationCompareMode: React.FC<Props> = () => {
[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,
})
}

Expand All @@ -396,45 +431,42 @@ const EvaluationCompareMode: React.FC<Props> = () => {
<Space>
<Typography.Text strong>Variants:</Typography.Text>
<div>
{variants?.map((v, vi) => (
{scenarios?.map((v, vi) => (
<Tag
key={evaluationIds[vi]}
color={colors[vi]}
className={classes.tag}
style={{
opacity: hiddenVariants.includes(v.evaluationId)
? 0.4
: 1,
opacity: hiddenVariants.includes(v.id) ? 0.4 : 1,
}}
icon={
evalIds.length < 2 &&
evalIds.includes(
v.evaluationId,
) ? null : evalIds.includes(v.evaluationId) ? (
evalIds.includes(v.id) ? null : evalIds.includes(
v.id,
) ? (
<CloseCircleOutlined
onClick={() =>
handleToggleVariantVisibility(
v.evaluationId,
)
handleToggleVariantVisibility(v.id)
}
style={{cursor: "pointer"}}
/>
) : (
<UndoOutlined
onClick={() =>
handleToggleVariantVisibility(
v.evaluationId,
)
handleToggleVariantVisibility(v.id)
}
style={{cursor: "pointer"}}
/>
)
}
>
<Link
href={`/apps/${appId}/playground/?variant=${v.variantName}`}
href={`/apps/${appId}/playground/?variant=${v.variants[0].variantName}`}
>
{v.variantName}
{variantNameWithRev({
variant_name: v.variants[0].variantName ?? "",
revision: v.revisions[0],
})}
</Link>
</Tag>
))}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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)
Expand Down Expand Up @@ -284,6 +293,8 @@ const EvaluationResults: React.FC<Props> = () => {
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,
Expand Down Expand Up @@ -312,6 +323,7 @@ const EvaluationResults: React.FC<Props> = () => {
...getFilterParams("date"),
cellRenderer: DateFromNowRenderer,
sort: "desc",
valueFormatter: (params) => formatDate24(params.value),
},
]
return colDefs
Expand Down Expand Up @@ -356,6 +368,38 @@ const EvaluationResults: React.FC<Props> = () => {
}
}

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 ? (
Expand Down Expand Up @@ -410,6 +454,9 @@ const EvaluationResults: React.FC<Props> = () => {
setIsFilterColsDropdownOpen(true)
}}
/>
<Button onClick={onExport} icon={<DownloadOutlined />}>
{!!selected.length ? `Export (${selected.length})` : "Export All"}
</Button>
</Space>

<Spin spinning={fetching}>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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: {
Expand Down Expand Up @@ -270,6 +272,17 @@ const EvaluationScenarios: React.FC<Props> = () => {
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,
})
}

Expand Down Expand Up @@ -303,7 +316,10 @@ const EvaluationScenarios: React.FC<Props> = () => {
<Typography.Link
href={`/apps/${appId}/playground/?variant=${evalaution?.variants[0].variantName}`}
>
{evalaution?.variants[0].variantName || ""}
{variantNameWithRev({
variant_name: evalaution?.variants[0].variantName ?? "",
revision: evalaution?.revisions[0],
})}
</Typography.Link>
</Space>
</Space>
Expand Down

0 comments on commit c6ab18c

Please sign in to comment.