From 0a7e551fc5bc828a7b44de787883082c24e7ff9b Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Tue, 3 Sep 2024 20:41:06 +0600 Subject: [PATCH 01/14] ui(frontend): automatic eval funcational table --- .../autoEvaluation/AutoEvaluation.tsx | 279 +++++++++++++++++- .../autoEvaluation/EditColumns.tsx | 87 ++++++ .../autoEvaluation/NewEvaluationModel.tsx | 244 +++++++++++++++ .../autoEvaluation/SearchFilter.tsx | 74 +++++ .../pages/apps/[app_id]/evaluations/index.tsx | 1 + 5 files changed, 673 insertions(+), 12 deletions(-) create mode 100644 agenta-web/src/components/pages/evaluations/autoEvaluation/EditColumns.tsx create mode 100644 agenta-web/src/components/pages/evaluations/autoEvaluation/NewEvaluationModel.tsx create mode 100644 agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx index ac586cf58e..31516ee6fb 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx @@ -1,7 +1,6 @@ -import {_Evaluation, JSSTheme} from "@/lib/Types" +import {_Evaluation, EvaluationStatus, JSSTheme} from "@/lib/Types" import { ArrowsLeftRight, - Columns, Database, Gauge, GearSix, @@ -10,20 +9,40 @@ import { Rocket, Trash, } from "@phosphor-icons/react" -import {Button, Dropdown, Space, Table} from "antd" -import React, {useState} from "react" +import {Button, Dropdown, DropdownProps, message, Space, Table} from "antd" +import React, {useEffect, useMemo, useRef, useState} from "react" import {createUseStyles} from "react-jss" import {ColumnsType} from "antd/es/table" import {MoreOutlined} from "@ant-design/icons" import EvaluatorsModal from "./EvaluatorsModal/EvaluatorsModal" import {useQueryParam} from "@/hooks/useQuery" import {formatDay} from "@/lib/helpers/dateTimeHelper" -import {getTypedValue} from "@/lib/helpers/evaluate" +import {calcEvalDuration, getTypedValue} from "@/lib/helpers/evaluate" import {variantNameWithRev} from "@/lib/helpers/variantHelper" +import NewEvaluationModal from "./NewEvaluationModel" +import { + deleteEvaluations, + fetchAllEvaluations, + fetchAllEvaluatorConfigs, + fetchAllEvaluators, + fetchEvaluationStatus, +} from "@/services/evaluations/api" +import {useAppId} from "@/hooks/useAppId" +import {useAtom} from "jotai" +import {evaluatorConfigsAtom, evaluatorsAtom} from "@/lib/atoms/evaluation" +import DeleteEvaluationModal from "@/components/DeleteEvaluationModal/DeleteEvaluationModal" +import {useRouter} from "next/router" +import EditColumns, {generateEditItems} from "./EditColumns" +import StatusRenderer from "../../overview/automaticEvaluation/StatusRenderer" +import {runningStatuses} from "../../evaluations/cellRenderers/cellRenderers" +import {useUpdateEffect} from "usehooks-ts" +import {shortPoll} from "@/lib/helpers/utils" +import {getFilterParams} from "./SearchFilter" interface AutoEvaluationProps { evaluationList: _Evaluation[] fetchingEvaluations: boolean + setEvaluationList: React.Dispatch> } const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -33,13 +52,119 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ }, })) -const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationProps) => { +const AutoEvaluation = ({ + evaluationList, + fetchingEvaluations, + setEvaluationList, +}: AutoEvaluationProps) => { const classes = useStyles() + const appId = useAppId() + const router = useRouter() + const [selectedRowKeys, setSelectedRowKeys] = useState([]) const [isConfigEvaluatorModalOpen, setIsConfigEvaluatorModalOpen] = useQueryParam( "configureEvaluatorModal", "", ) + // create new evaluation + const [newEvalModalOpen, setNewEvalModalOpen] = useState(false) + const [isEvalLoading, setIsEvalLoading] = useState(false) + const [evaluators, setEvaluators] = useAtom(evaluatorsAtom) + const setEvaluatorConfigs = useAtom(evaluatorConfigsAtom)[1] + // delete evaluation + const [selectedEvalRecord, setSelectedEvalRecord] = useState<_Evaluation>() + const [isDeleteEvalModalOpen, setIsDeleteEvalModalOpen] = useState(false) + const [isDeleteEvalMultipleModalOpen, setIsDeleteEvalMultipleModalOpen] = useState(false) + //edit columns + const [editColumns, setEditColumns] = useState([]) + const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) + // + const stoppers = useRef() + + const fetchEvaluations = async () => { + try { + setIsEvalLoading(true) + const [allEvaluations, allEvaluators, allEvaluatorConfigs] = await Promise.all([ + fetchAllEvaluations(appId), + fetchAllEvaluators(), + fetchAllEvaluatorConfigs(appId), + ]) + const result = allEvaluations.sort( + (a, b) => + new Date(b.created_at || 0).getTime() - new Date(a.created_at || 0).getTime(), + ) + setEvaluationList(result) + setEvaluators(allEvaluators) + setEvaluatorConfigs(allEvaluatorConfigs) + } catch (error) { + console.error(error) + } finally { + setIsEvalLoading(false) + } + } + + const handleDeleteMultipleEvaluations = async () => { + const evaluationsIds = selectedRowKeys.map((key) => key.toString()) + try { + setIsEvalLoading(true) + await deleteEvaluations(evaluationsIds) + setEvaluationList((prevEvaluationsList) => + prevEvaluationsList.filter((evaluation) => !evaluationsIds.includes(evaluation.id)), + ) + setSelectedRowKeys([]) + message.success("Evaluations Deleted") + } catch (error) { + console.error(error) + } finally { + setIsEvalLoading(false) + } + } + + const handleDeleteEvaluation = async (record: _Evaluation) => { + try { + setIsEvalLoading(true) + await deleteEvaluations([record.id]) + setEvaluationList((prevEvaluationsList) => + prevEvaluationsList.filter((evaluation) => ![record.id].includes(evaluation.id)), + ) + message.success("Evaluation Deleted") + } catch (error) { + console.error(error) + } finally { + setIsEvalLoading(false) + } + } + + const compareDisabled = useMemo(() => { + const evalList = evaluationList.filter((e) => selectedRowKeys.includes(e.id)) + return ( + evalList.length < 2 || + evalList.some( + (item) => + item.status.value === EvaluationStatus.STARTED || + item.status.value === EvaluationStatus.INITIALIZED || + item.testset.id !== evalList[0].testset.id, + ) + ) + }, [selectedRowKeys]) + + const onToggleEvaluatorVisibility = (evalConfigId: string) => { + if (!editColumns.includes(evalConfigId)) { + setEditColumns([...editColumns, evalConfigId]) + } else { + setEditColumns(editColumns.filter((item) => item !== evalConfigId)) + } + } + + const handleOpenChangeEditCols: DropdownProps["onOpenChange"] = (nextOpen, info) => { + if (info.source === "trigger" || nextOpen) { + setIsFilterColsDropdownOpen(nextOpen) + } + } + + const handleNavigation = (variantName: string, revisionNum: string) => { + router.push(`/apps/${appId}/playground?variant=${variantName}&revision=${revisionNum}`) + } const columns: ColumnsType<_Evaluation> = [ { @@ -60,6 +185,7 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro ) }, + ...getFilterParams("variants", "text"), }, { title: "Test set", @@ -71,6 +197,7 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro render: (_, record) => { return {record.testset.name} }, + ...getFilterParams("testset", "text"), }, { title: "Status", @@ -79,9 +206,14 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro onHeaderCell: () => ({ style: {minWidth: 240}, }), + render: (_, record) => { + return + }, + ...getFilterParams("status", "text"), }, { title: "Results", + key: "results", children: [ { title: "Evaluator 1", @@ -119,6 +251,7 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro render: (_, record) => { return formatDay(record.created_at) }, + ...getFilterParams("created_at", "date"), }, { title: "Avg. Latency", @@ -130,6 +263,7 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro render: (_, record) => { return getTypedValue(record.average_latency) }, + ...getFilterParams("average_latency", "number"), }, { title: "Total Cost", @@ -141,6 +275,7 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro render: (_, record) => { return getTypedValue(record.average_cost) }, + ...getFilterParams("total_cost", "number"), }, { title: , @@ -161,6 +296,9 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro icon: , onClick: (e) => { e.domEvent.stopPropagation() + router.push( + `/apps/${appId}/evaluations/results/${record.id}`, + ) }, }, { @@ -169,6 +307,10 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro icon: , onClick: (e) => { e.domEvent.stopPropagation() + handleNavigation( + record.variants[0].variantName, + record.revisions[0], + ) }, }, { @@ -177,6 +319,7 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro icon: , onClick: (e) => { e.domEvent.stopPropagation() + router.push(`/apps/${appId}/testsets/${record.testset.id}`) }, }, {type: "divider"}, @@ -187,6 +330,8 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro danger: true, onClick: (e) => { e.domEvent.stopPropagation() + setSelectedEvalRecord(record) + setIsDeleteEvalModalOpen(true) }, }, ], @@ -204,11 +349,68 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro }, ] + const runningEvaluationIds = useMemo( + () => + evaluationList + .filter((item) => runningStatuses.includes(item.status.value)) + .map((item) => item.id), + [evaluationList], + ) + + useUpdateEffect(() => { + stoppers.current?.() + + if (runningEvaluationIds.length) { + stoppers.current = shortPoll( + () => + Promise.all(runningEvaluationIds.map((id) => fetchEvaluationStatus(id))) + .then((res) => { + setEvaluationList((prev) => { + const newEvals = [...prev] + runningEvaluationIds.forEach((id, ix) => { + const index = newEvals.findIndex((e) => e.id === id) + if (index !== -1) { + newEvals[index].status = res[ix].status + newEvals[index].duration = calcEvalDuration(newEvals[index]) + } + }) + if ( + res.some((item) => !runningStatuses.includes(item.status.value)) + ) + fetchEvaluations() + return newEvals + }) + }) + .catch(console.error), + {delayMs: 2000, timeoutMs: Infinity}, + ).stopper + } + + return () => { + stoppers.current?.() + } + }, [JSON.stringify(runningEvaluationIds)]) + + useEffect(() => { + const defaultColumnNames = columns.map((item) => item.key as string) + setEditColumns(defaultColumnNames) + }, []) + + const editedColumns = columns.map((item) => ({ + ...item, + hidden: !editColumns?.includes(item.key as string), + })) + return (
- @@ -232,17 +436,30 @@ const AutoEvaluation = ({evaluationList, fetchingEvaluations}: AutoEvaluationPro type="text" icon={} className={classes.button} + disabled={compareDisabled} + onClick={() => + router.push( + `/apps/${appId}/evaluations/results/compare?evaluations=${selectedRowKeys.join(",")}`, + ) + } > Compare - + { + onToggleEvaluatorVisibility(key) + setIsFilterColsDropdownOpen(true) + }} + />
+ { + setIsConfigEvaluatorModalOpen("open") + setNewEvalModalOpen(false) + }} + onCancel={() => { + setNewEvalModalOpen(false) + }} + onSuccess={() => { + setNewEvalModalOpen(false) + fetchEvaluations() + }} + /> + {isConfigEvaluatorModalOpen === "open" && ( setIsConfigEvaluatorModalOpen("")} /> )} + + {selectedEvalRecord && ( + setIsDeleteEvalModalOpen(false)} + onOk={async () => { + await handleDeleteEvaluation(selectedEvalRecord) + setIsDeleteEvalModalOpen(false) + }} + evaluationType={"automatic evaluation"} + /> + )} + {isDeleteEvalMultipleModalOpen && ( + setIsDeleteEvalMultipleModalOpen(false)} + onOk={async () => { + await handleDeleteMultipleEvaluations() + setIsDeleteEvalMultipleModalOpen(false) + }} + evaluationType={"single model evaluation"} + /> + )} ) } diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/EditColumns.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/EditColumns.tsx new file mode 100644 index 0000000000..979b519b02 --- /dev/null +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/EditColumns.tsx @@ -0,0 +1,87 @@ +import {_Evaluation, JSSTheme} from "@/lib/Types" +import {Button, Dropdown, Space, Checkbox} from "antd" +import React from "react" +import {createUseStyles} from "react-jss" +import {Columns} from "@phosphor-icons/react" +import {ColumnsType} from "antd/es/table" + +const useStyles = createUseStyles((theme: JSSTheme) => ({ + dropdownMenu: { + "&>.ant-dropdown-menu-item": { + "& .anticon-check": { + display: "none", + }, + }, + "&>.ant-dropdown-menu-item-selected": { + "&:not(:hover)": { + backgroundColor: "transparent !important", + }, + "& .anticon-check": { + display: "inline-flex !important", + }, + }, + }, + button: { + display: "flex", + alignItems: "center", + }, +})) + +export const generateEditItems = (columns: ColumnsType, editColumns: string[]) => { + return columns + .filter((col) => col.key !== "key") + .map((col) => ({ + key: col.key, + label: ( + + + <>{col.title} + + ), + })) +} + +interface EditColumnsProps { + isOpen: boolean + handleOpenChange: ( + open: boolean, + info: { + source: "trigger" | "menu" + }, + ) => void + shownCols: string[] + items: any + onClick: ({key}: {key: string}) => void + buttonText?: string +} + +const EditColumns = ({ + isOpen, + handleOpenChange, + shownCols, + items, + onClick, + buttonText, +}: EditColumnsProps) => { + const classes = useStyles() + + return ( + + + + ) +} + +export default EditColumns diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/NewEvaluationModel.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/NewEvaluationModel.tsx new file mode 100644 index 0000000000..8092744ae5 --- /dev/null +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/NewEvaluationModel.tsx @@ -0,0 +1,244 @@ +import React, {useEffect, useState} from "react" +import {useAppId} from "@/hooks/useAppId" +import {JSSTheme, Variant, testset} from "@/lib/Types" +import {evaluatorConfigsAtom, evaluatorsAtom} from "@/lib/atoms/evaluation" +import {apiKeyObject, redirectIfNoLLMKeys} from "@/lib/helpers/utils" +import {fetchVariants} from "@/services/api" +import {CreateEvaluationData, createEvalutaiton} from "@/services/evaluations/api" +import {fetchTestsets} from "@/services/testsets/api" +import {Button, Divider, Form, Modal, Select, Spin} from "antd" +import {useAtom} from "jotai" +import {createUseStyles} from "react-jss" +import {ChartDonut, Plus} from "@phosphor-icons/react" + +const useStyles = createUseStyles((theme: JSSTheme) => ({ + spinContainer: { + display: "grid", + placeItems: "center", + height: "100%", + }, + selector: { + width: 300, + }, + configRow: { + display: "flex", + alignItems: "center", + justifyContent: "space-between", + }, + divider: { + margin: "1rem -1.5rem", + width: "unset", + }, + container: { + "& .ant-modal-footer": { + display: "flex", + alignItems: "center", + justifyContent: "flex-end", + }, + }, + modalContainer: { + display: "flex", + alignItems: "center", + }, + selectItemLabels: { + fontSize: theme.fontSizeSM, + lineHeight: theme.lineHeightSM, + color: theme.colorTextDescription, + margin: "0px 5px", + }, +})) + +type Props = { + onSuccess?: () => void + onOpenEvaluatorModal: () => void +} & React.ComponentProps + +const NewEvaluationModal: React.FC = ({onSuccess, onOpenEvaluatorModal, ...props}) => { + const classes = useStyles() + const appId = useAppId() + const [fetching, setFetching] = useState(false) + const [testSets, setTestSets] = useState([]) + const [variants, setVariants] = useState([]) + const [evaluatorConfigs] = useAtom(evaluatorConfigsAtom) + const [evaluators] = useAtom(evaluatorsAtom) + const [submitLoading, setSubmitLoading] = useState(false) + const [form] = Form.useForm() + + useEffect(() => { + setFetching(true) + form.resetFields() + Promise.all([fetchTestsets(appId), fetchVariants(appId)]) + .then(([testSets, variants]) => { + setTestSets(testSets) + setVariants(variants) + }) + .catch(console.error) + .finally(() => setFetching(false)) + }, [props.open, appId]) + + const rateLimitValues = { + batch_size: 10, + max_retries: 3, + retry_delay: 3, + delay_between_batches: 5, + } + const correctAnswerColumn = "correct_answer" + + const onSubmit = (values: CreateEvaluationData) => { + // redirect if no llm keys and an AI Critique config is selected + if ( + values.evaluators_configs.some( + (id) => + evaluatorConfigs.find((config) => config.id === id)?.evaluator_key === + "auto_ai_critique", + ) && + redirectIfNoLLMKeys() + ) + return + setSubmitLoading(true) + createEvalutaiton(appId, { + testset_id: values.testset_id, + variant_ids: values.variant_ids, + evaluators_configs: values.evaluators_configs, + rate_limit: rateLimitValues, + lm_providers_keys: apiKeyObject(), + correct_answer_column: correctAnswerColumn, + }) + .then(onSuccess) + .catch(console.error) + .finally(() => setSubmitLoading(false)) + } + + return ( + , + loading: submitLoading, + className: classes.modalContainer, + }} + className={classes.container} + {...props} + > + + +
+ + + + + + + + + + + + +
+
+ ) +} + +export default NewEvaluationModal diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx new file mode 100644 index 0000000000..cfe24569c7 --- /dev/null +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx @@ -0,0 +1,74 @@ +import {_Evaluation} from "@/lib/Types" +import {Input, InputRef, TableColumnType, DatePicker} from "antd" +import {FilterDropdownProps} from "antd/es/table/interface" +import React, {useRef} from "react" +import dayjs from "dayjs" + +type DataIndex = keyof _Evaluation + +type CellDataType = "number" | "text" | "date" + +export function getFilterParams(dataIndex: DataIndex, type: CellDataType): TableColumnType<_Evaluation> { + const searchInput = useRef(null) + + const filterDropdown = ({setSelectedKeys, selectedKeys, confirm}: FilterDropdownProps) => { + return ( +
e.stopPropagation()}> + {type === "date" ? ( + { + setSelectedKeys(dateString ? [dateString] : []) + confirm() + }} + /> + ) : ( + { + setSelectedKeys(e.target.value ? [e.target.value] : []) + confirm({closeDropdown: false}) + }} + style={{display: "block"}} + /> + )} +
+ ) + } + + const onFilter = (value: any, record: any) => { + try { + const cellValue = record[dataIndex] + + if (type === "date") { + return dayjs(cellValue).isSame(dayjs(value), "day") + } + if (typeof cellValue === "object" && cellValue !== null) { + if (Array.isArray(cellValue)) { + return cellValue.some((item) => + item.variantName?.toLowerCase().includes(value.toLowerCase()), + ) + } else if (cellValue.hasOwnProperty("name")) { + return cellValue.name.toLowerCase().includes(value.toLowerCase()) + } else if (cellValue.hasOwnProperty("value")) { + return cellValue.value.toLowerCase().includes(value.toLowerCase()) + } + } + return cellValue?.toString().toLowerCase().includes(value.toLowerCase()) + } catch (error) { + console.error(error) + } + } + + return { + filterDropdown, + onFilter, + onFilterDropdownOpenChange: (visible) => { + if (visible) { + setTimeout(() => searchInput.current?.select(), 100) + } + }, + } +} diff --git a/agenta-web/src/pages/apps/[app_id]/evaluations/index.tsx b/agenta-web/src/pages/apps/[app_id]/evaluations/index.tsx index b4d3bea986..eb233705d7 100644 --- a/agenta-web/src/pages/apps/[app_id]/evaluations/index.tsx +++ b/agenta-web/src/pages/apps/[app_id]/evaluations/index.tsx @@ -83,6 +83,7 @@ const EvaluationsPage = () => { ), }, From 1fe2babd6bf75cc9e840154dd0c4c1a55fcb2600 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Tue, 3 Sep 2024 21:27:30 +0600 Subject: [PATCH 02/14] fix(frontend): search issue with numbers --- .../pages/evaluations/autoEvaluation/SearchFilter.tsx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx index cfe24569c7..45d9e51435 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx @@ -8,7 +8,10 @@ type DataIndex = keyof _Evaluation type CellDataType = "number" | "text" | "date" -export function getFilterParams(dataIndex: DataIndex, type: CellDataType): TableColumnType<_Evaluation> { +export function getFilterParams( + dataIndex: DataIndex, + type: CellDataType, +): TableColumnType<_Evaluation> { const searchInput = useRef(null) const filterDropdown = ({setSelectedKeys, selectedKeys, confirm}: FilterDropdownProps) => { @@ -32,6 +35,7 @@ export function getFilterParams(dataIndex: DataIndex, type: CellDataType): Table confirm({closeDropdown: false}) }} style={{display: "block"}} + type={type} /> )} @@ -51,9 +55,9 @@ export function getFilterParams(dataIndex: DataIndex, type: CellDataType): Table item.variantName?.toLowerCase().includes(value.toLowerCase()), ) } else if (cellValue.hasOwnProperty("name")) { - return cellValue.name.toLowerCase().includes(value.toLowerCase()) + return cellValue.name.toString().toLowerCase().includes(value.toLowerCase()) } else if (cellValue.hasOwnProperty("value")) { - return cellValue.value.toLowerCase().includes(value.toLowerCase()) + return cellValue.value.toString().toLowerCase().includes(value.toLowerCase()) } } return cellValue?.toString().toLowerCase().includes(value.toLowerCase()) From cacad5769a156c1e40e6968a4069d2d4e011ecd8 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Wed, 4 Sep 2024 17:11:47 +0600 Subject: [PATCH 03/14] ui(frontend): added table results column --- .../autoEvaluation/AutoEvaluation.tsx | 219 ++++++++++++++---- .../pages/apps/[app_id]/evaluations/index.tsx | 27 +-- 2 files changed, 173 insertions(+), 73 deletions(-) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx index 31516ee6fb..6982203078 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx @@ -1,4 +1,4 @@ -import {_Evaluation, EvaluationStatus, JSSTheme} from "@/lib/Types" +import {_Evaluation, EvaluationStatus, EvaluatorConfig, JSSTheme} from "@/lib/Types" import { ArrowsLeftRight, Database, @@ -9,11 +9,11 @@ import { Rocket, Trash, } from "@phosphor-icons/react" -import {Button, Dropdown, DropdownProps, message, Space, Table} from "antd" +import {Button, Dropdown, DropdownProps, message, Popover, Space, Table, Tag} from "antd" import React, {useEffect, useMemo, useRef, useState} from "react" import {createUseStyles} from "react-jss" import {ColumnsType} from "antd/es/table" -import {MoreOutlined} from "@ant-design/icons" +import {EditOutlined, InfoCircleOutlined, MoreOutlined} from "@ant-design/icons" import EvaluatorsModal from "./EvaluatorsModal/EvaluatorsModal" import {useQueryParam} from "@/hooks/useQuery" import {formatDay} from "@/lib/helpers/dateTimeHelper" @@ -38,47 +38,57 @@ import {runningStatuses} from "../../evaluations/cellRenderers/cellRenderers" import {useUpdateEffect} from "usehooks-ts" import {shortPoll} from "@/lib/helpers/utils" import {getFilterParams} from "./SearchFilter" - -interface AutoEvaluationProps { - evaluationList: _Evaluation[] - fetchingEvaluations: boolean - setEvaluationList: React.Dispatch> -} +import {uniqBy} from "lodash" +import NewEvaluatorModal from "../evaluators/NewEvaluatorModal" const useStyles = createUseStyles((theme: JSSTheme) => ({ + resultTag: { + minWidth: 150, + display: "flex", + cursor: "pointer", + alignItems: "stretch", + borderRadius: theme.borderRadiusSM, + border: `1px solid ${theme.colorBorder}`, + textAlign: "center", + "& > div:nth-child(1)": { + backgroundColor: "rgba(0, 0, 0, 0.02)", + lineHeight: theme.lineHeight, + flex: 1, + borderRight: `1px solid ${theme.colorBorder}`, + padding: "0 7px", + }, + "& > div:nth-child(2)": { + padding: "0 7px", + }, + }, button: { display: "flex", alignItems: "center", }, })) -const AutoEvaluation = ({ - evaluationList, - fetchingEvaluations, - setEvaluationList, -}: AutoEvaluationProps) => { +const AutoEvaluation = () => { const classes = useStyles() const appId = useAppId() const router = useRouter() const [selectedRowKeys, setSelectedRowKeys] = useState([]) - const [isConfigEvaluatorModalOpen, setIsConfigEvaluatorModalOpen] = useQueryParam( - "configureEvaluatorModal", - "", - ) - // create new evaluation + const [evaluationList, setEvaluationList] = useState<_Evaluation[]>([]) const [newEvalModalOpen, setNewEvalModalOpen] = useState(false) const [isEvalLoading, setIsEvalLoading] = useState(false) const [evaluators, setEvaluators] = useAtom(evaluatorsAtom) const setEvaluatorConfigs = useAtom(evaluatorConfigsAtom)[1] - // delete evaluation const [selectedEvalRecord, setSelectedEvalRecord] = useState<_Evaluation>() const [isDeleteEvalModalOpen, setIsDeleteEvalModalOpen] = useState(false) const [isDeleteEvalMultipleModalOpen, setIsDeleteEvalMultipleModalOpen] = useState(false) - //edit columns const [editColumns, setEditColumns] = useState([]) const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) - // + const [selectedConfigEdit, setSelectedConfigEdit] = useState() + const [isEditEvalConfigOpen, setIsEditEvalConfigOpen] = useState(false) + const [isConfigEvaluatorModalOpen, setIsConfigEvaluatorModalOpen] = useQueryParam( + "configureEvaluatorModal", + "", + ) const stoppers = useRef() const fetchEvaluations = async () => { @@ -166,6 +176,24 @@ const AutoEvaluation = ({ router.push(`/apps/${appId}/playground?variant=${variantName}&revision=${revisionNum}`) } + const evaluatorConfigs = useMemo( + () => + uniqBy( + evaluationList + .map((item) => + item.aggregated_results.map((item) => ({ + ...item.evaluator_config, + evaluator: evaluators.find( + (e) => e.key === item.evaluator_config.evaluator_key, + ), + })), + ) + .flat(), + "id", + ), + [evaluationList], + ) + const columns: ColumnsType<_Evaluation> = [ { title: "Variant", @@ -214,32 +242,110 @@ const AutoEvaluation = ({ { title: "Results", key: "results", - children: [ - { - title: "Evaluator 1", - // dataIndex: "aggregated_results", - key: "results", - onHeaderCell: () => ({ - style: {minWidth: 240}, - }), - }, - { - title: "Evaluator 2", - // dataIndex: "aggregated_results", - key: "results", - onHeaderCell: () => ({ - style: {minWidth: 240}, - }), + onHeaderCell: () => ({style: {minWidth: 240}}), + children: evaluatorConfigs.map((evaluator, idx) => ({ + title: evaluator.name, + key: `results-${idx}`, + onHeaderCell: () => ({style: {minWidth: 240}}), + showSorterTooltip: false, + sorter: { + compare: (a, b) => { + const getSortValue = (item: _Evaluation) => { + if (item.aggregated_results && item.aggregated_results.length > 0) { + const result = item.aggregated_results[0].result + if (result && typeof result.value === "number") { + return result.value + } + } + return 0 + } + return getSortValue(a) - getSortValue(b) + }, }, - { - title: "Evaluator 3", - // dataIndex: "aggregated_results", - key: "results", - onHeaderCell: () => ({ - style: {minWidth: 240}, - }), + render: (_, record) => { + if (!evaluators?.length) return + + const matchingResults = record.aggregated_results.filter( + (result) => result.evaluator_config.id === evaluator.id, + ) + + return ( + + {matchingResults.map((result, index) => + result.result.error ? ( + + {result.result.error?.stacktrace} + + } + title={result.result.error?.message} + > + + + ) : ( + e.stopPropagation()} + > +
+ {result.evaluator_config.name} +
+
{getTypedValue(result.result)}
+ + } + title={ +
e.stopPropagation()} + > + + {evaluator?.name} + +
+ } + > +
e.stopPropagation()} + className={classes.resultTag} + > +
{result.evaluator_config.name}
+
{getTypedValue(result.result)}
+
+
+ ), + )} +
+ ) }, - ], + })), }, { title: "Created on", @@ -357,6 +463,12 @@ const AutoEvaluation = ({ [evaluationList], ) + useEffect(() => { + if (!appId) return + + fetchEvaluations() + }, [appId]) + useUpdateEffect(() => { stoppers.current?.() @@ -459,7 +571,7 @@ const AutoEvaluation = ({
)} + { + setIsEditEvalConfigOpen(false) + fetchEvaluations() + }} + newEvalModalConfigOpen={isEditEvalConfigOpen} + setNewEvalModalConfigOpen={setIsEditEvalConfigOpen} + setNewEvalModalOpen={() => {}} + editMode={true} + initialValues={selectedConfigEdit} + /> + {selectedEvalRecord && ( ({ @@ -36,40 +33,18 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) const EvaluationsPage = () => { - const appId = useAppId() const classes = useStyles() - const [autoEvaluationList, setAutoEvaluationList] = useState<_Evaluation[]>([]) const [selectedEvaluation, setSelectedEvaluation] = useQueryParam( "selectedEvaluation", "auto_evaluation", ) - const [fetchingEvaluations, setFetchingEvaluations] = useState(false) - - useEffect(() => { - if (!appId) return - - setFetchingEvaluations(true) - - fetchAllEvaluations(appId) - .then((autoEvalResult) => { - setAutoEvaluationList(autoEvalResult) - }) - .catch(console.error) - .finally(() => setFetchingEvaluations(false)) - }, [appId]) const items: TabsProps["items"] = [ { key: "auto_evaluation", label: "Automatic Evaluation", icon: , - children: ( - - ), + children: , }, { key: "ab_testing_evaluation", From a1cbbd6b1260ac81122d609d280800e5c73ac147 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Wed, 4 Sep 2024 19:20:42 +0600 Subject: [PATCH 04/14] refactor(frontend): removed unsed code --- .../NewEvaluationModal.tsx | 0 .../autoEvaluation/AutoEvaluation.tsx | 6 +- .../autoEvaluation/NewEvaluationModel.tsx | 244 -------- .../evaluationResults/EmptyEvaluations.tsx | 89 --- .../evaluationResults/EvaluationResults.tsx | 540 ------------------ .../AutomaticEvalOverview.tsx | 2 +- 6 files changed, 2 insertions(+), 879 deletions(-) rename agenta-web/src/components/pages/evaluations/{evaluationResults => NewEvaluation}/NewEvaluationModal.tsx (100%) delete mode 100644 agenta-web/src/components/pages/evaluations/autoEvaluation/NewEvaluationModel.tsx delete mode 100644 agenta-web/src/components/pages/evaluations/evaluationResults/EmptyEvaluations.tsx delete mode 100644 agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx diff --git a/agenta-web/src/components/pages/evaluations/evaluationResults/NewEvaluationModal.tsx b/agenta-web/src/components/pages/evaluations/NewEvaluation/NewEvaluationModal.tsx similarity index 100% rename from agenta-web/src/components/pages/evaluations/evaluationResults/NewEvaluationModal.tsx rename to agenta-web/src/components/pages/evaluations/NewEvaluation/NewEvaluationModal.tsx diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx index 6982203078..58386d3faa 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx @@ -19,7 +19,7 @@ import {useQueryParam} from "@/hooks/useQuery" import {formatDay} from "@/lib/helpers/dateTimeHelper" import {calcEvalDuration, getTypedValue} from "@/lib/helpers/evaluate" import {variantNameWithRev} from "@/lib/helpers/variantHelper" -import NewEvaluationModal from "./NewEvaluationModel" +import NewEvaluationModal from "@/components/pages/evaluations/NewEvaluation/NewEvaluationModal" import { deleteEvaluations, fetchAllEvaluations, @@ -594,10 +594,6 @@ const AutoEvaluation = () => { { - setIsConfigEvaluatorModalOpen("open") - setNewEvalModalOpen(false) - }} onCancel={() => { setNewEvalModalOpen(false) }} diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/NewEvaluationModel.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/NewEvaluationModel.tsx deleted file mode 100644 index 8092744ae5..0000000000 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/NewEvaluationModel.tsx +++ /dev/null @@ -1,244 +0,0 @@ -import React, {useEffect, useState} from "react" -import {useAppId} from "@/hooks/useAppId" -import {JSSTheme, Variant, testset} from "@/lib/Types" -import {evaluatorConfigsAtom, evaluatorsAtom} from "@/lib/atoms/evaluation" -import {apiKeyObject, redirectIfNoLLMKeys} from "@/lib/helpers/utils" -import {fetchVariants} from "@/services/api" -import {CreateEvaluationData, createEvalutaiton} from "@/services/evaluations/api" -import {fetchTestsets} from "@/services/testsets/api" -import {Button, Divider, Form, Modal, Select, Spin} from "antd" -import {useAtom} from "jotai" -import {createUseStyles} from "react-jss" -import {ChartDonut, Plus} from "@phosphor-icons/react" - -const useStyles = createUseStyles((theme: JSSTheme) => ({ - spinContainer: { - display: "grid", - placeItems: "center", - height: "100%", - }, - selector: { - width: 300, - }, - configRow: { - display: "flex", - alignItems: "center", - justifyContent: "space-between", - }, - divider: { - margin: "1rem -1.5rem", - width: "unset", - }, - container: { - "& .ant-modal-footer": { - display: "flex", - alignItems: "center", - justifyContent: "flex-end", - }, - }, - modalContainer: { - display: "flex", - alignItems: "center", - }, - selectItemLabels: { - fontSize: theme.fontSizeSM, - lineHeight: theme.lineHeightSM, - color: theme.colorTextDescription, - margin: "0px 5px", - }, -})) - -type Props = { - onSuccess?: () => void - onOpenEvaluatorModal: () => void -} & React.ComponentProps - -const NewEvaluationModal: React.FC = ({onSuccess, onOpenEvaluatorModal, ...props}) => { - const classes = useStyles() - const appId = useAppId() - const [fetching, setFetching] = useState(false) - const [testSets, setTestSets] = useState([]) - const [variants, setVariants] = useState([]) - const [evaluatorConfigs] = useAtom(evaluatorConfigsAtom) - const [evaluators] = useAtom(evaluatorsAtom) - const [submitLoading, setSubmitLoading] = useState(false) - const [form] = Form.useForm() - - useEffect(() => { - setFetching(true) - form.resetFields() - Promise.all([fetchTestsets(appId), fetchVariants(appId)]) - .then(([testSets, variants]) => { - setTestSets(testSets) - setVariants(variants) - }) - .catch(console.error) - .finally(() => setFetching(false)) - }, [props.open, appId]) - - const rateLimitValues = { - batch_size: 10, - max_retries: 3, - retry_delay: 3, - delay_between_batches: 5, - } - const correctAnswerColumn = "correct_answer" - - const onSubmit = (values: CreateEvaluationData) => { - // redirect if no llm keys and an AI Critique config is selected - if ( - values.evaluators_configs.some( - (id) => - evaluatorConfigs.find((config) => config.id === id)?.evaluator_key === - "auto_ai_critique", - ) && - redirectIfNoLLMKeys() - ) - return - setSubmitLoading(true) - createEvalutaiton(appId, { - testset_id: values.testset_id, - variant_ids: values.variant_ids, - evaluators_configs: values.evaluators_configs, - rate_limit: rateLimitValues, - lm_providers_keys: apiKeyObject(), - correct_answer_column: correctAnswerColumn, - }) - .then(onSuccess) - .catch(console.error) - .finally(() => setSubmitLoading(false)) - } - - return ( - , - loading: submitLoading, - className: classes.modalContainer, - }} - className={classes.container} - {...props} - > - - -
- - - - - - - - - - - - -
-
- ) -} - -export default NewEvaluationModal diff --git a/agenta-web/src/components/pages/evaluations/evaluationResults/EmptyEvaluations.tsx b/agenta-web/src/components/pages/evaluations/evaluationResults/EmptyEvaluations.tsx deleted file mode 100644 index 64de7a3f51..0000000000 --- a/agenta-web/src/components/pages/evaluations/evaluationResults/EmptyEvaluations.tsx +++ /dev/null @@ -1,89 +0,0 @@ -import {JSSTheme} from "@/lib/Types" -import {PlusCircleOutlined, SlidersOutlined} from "@ant-design/icons" -import {Button, Empty, Space, Tooltip, Typography} from "antd" -import Image from "next/image" -import React from "react" -import {createUseStyles} from "react-jss" -import evaluationIllustration from "@/media/eval-illustration.png" - -const useStyles = createUseStyles((theme: JSSTheme) => ({ - emptyRoot: { - height: "calc(100vh - 260px)", - display: "grid", - placeItems: "center", - }, - empty: { - "& .ant-empty-description": { - fontSize: 18, - marginTop: "0.75rem", - marginBottom: "1.5rem", - }, - }, - emptyImg: { - width: 120, - height: 120, - objectFit: "contain", - filter: theme.isDark ? "invert(1)" : "none", - opacity: 0.85, - }, -})) - -interface Props { - onConfigureEvaluators?: () => void - onBeginEvaluation?: () => void -} - -const EmptyEvaluations: React.FC = ({onConfigureEvaluators, onBeginEvaluation}) => { - const classes = useStyles() - - return ( -
- - Get Started with Your First Evaluation -
- - } - image={ - - } - > - - - - - Or - - - - -
-
- ) -} - -export default EmptyEvaluations diff --git a/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx b/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx deleted file mode 100644 index 97cae7ee85..0000000000 --- a/agenta-web/src/components/pages/evaluations/evaluationResults/EvaluationResults.tsx +++ /dev/null @@ -1,540 +0,0 @@ -import React, {useEffect, useMemo, useRef, useState} from "react" -import {AgGridReact} from "ag-grid-react" -import {useAppTheme} from "@/components/Layout/ThemeContextProvider" -import {ColDef, ValueGetterParams} from "ag-grid-community" -import {createUseStyles} from "react-jss" -import {Button, DropdownProps, Space, Spin, Tag, Tooltip, Typography, theme} from "antd" -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" -import duration from "dayjs/plugin/duration" -import NewEvaluationModal from "./NewEvaluationModal" -import {useAppId} from "@/hooks/useAppId" -import { - deleteEvaluations, - fetchAllEvaluations, - fetchEvaluationStatus, -} from "@/services/evaluations/api" -import {useUpdateEffect} from "usehooks-ts" -import {shortPoll} from "@/lib/helpers/utils" -import AlertPopup from "@/components/AlertPopup/AlertPopup" -import { - DateFromNowRenderer, - LinkCellRenderer, - StatusRenderer, - runningStatuses, - statusMapper, -} from "../cellRenderers/cellRenderers" -import {useAtom} from "jotai" -import {evaluatorsAtom} from "@/lib/atoms/evaluation" -import AgCustomHeader from "@/components/AgCustomHeader/AgCustomHeader" -import {useRouter} from "next/router" -import EmptyEvaluations from "./EmptyEvaluations" -import {calcEvalDuration, getFilterParams, getTypedValue} from "@/lib/helpers/evaluate" -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" -import {useQueryParam} from "@/hooks/useQuery" - -dayjs.extend(relativeTime) -dayjs.extend(duration) - -const useStyles = createUseStyles((theme: JSSTheme) => ({ - root: { - display: "flex", - flexDirection: "column", - gap: "1rem", - }, - table: { - height: "calc(100vh - 260px)", - }, - buttonsGroup: { - marginTop: "1rem", - alignSelf: "flex-end", - }, - dropdownMenu: { - "&>.ant-dropdown-menu-item": { - "& .anticon-check": { - display: "none", - }, - }, - "&>.ant-dropdown-menu-item-selected": { - "&:not(:hover)": { - backgroundColor: "transparent !important", - }, - "& .anticon-check": { - display: "inline-flex !important", - }, - }, - }, -})) - -interface Props {} - -const EvaluationResults: React.FC = () => { - const {appTheme} = useAppTheme() - const classes = useStyles() - const appId = useAppId() - const [evaluations, setEvaluations] = useState<_Evaluation[]>([]) - const [evaluators] = useAtom(evaluatorsAtom) - const [newEvalModalOpen, setNewEvalModalOpen] = useState(false) - const [queryNewEvalModalOpen, setQueryNewEvalModalOpen] = - useQueryParam("openNewEvaluationModal") - const [fetching, setFetching] = useState(false) - const [selected, setSelected] = useState<_Evaluation[]>([]) - const stoppers = useRef() - const router = useRouter() - const {token} = theme.useToken() - const gridRef = useRef() - const [hiddenCols, setHiddenCols] = useState([]) - const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) - - const runningEvaluationIds = useMemo( - () => - evaluations - .filter((item) => runningStatuses.includes(item.status.value)) - .map((item) => item.id), - [evaluations], - ) - - const onDelete = () => { - AlertPopup({ - title: "Delete Evaluations", - message: `Are you sure you want to delete all ${selected.length} selected evaluations?`, - onOk: () => - deleteEvaluations(selected.map((item) => item.id)) - .catch(console.error) - .then(fetcher), - }) - } - - const fetcher = () => { - setFetching(true) - fetchAllEvaluations(appId) - .then(setEvaluations) - .catch(console.error) - .finally(() => setFetching(false)) - } - - useEffect(() => { - fetcher() - }, [appId]) - - //update status of running evaluations through short polling - useUpdateEffect(() => { - stoppers.current?.() - - if (runningEvaluationIds.length) { - stoppers.current = shortPoll( - () => - Promise.all(runningEvaluationIds.map((id) => fetchEvaluationStatus(id))) - .then((res) => { - setEvaluations((prev) => { - const newEvals = [...prev] - runningEvaluationIds.forEach((id, ix) => { - const index = newEvals.findIndex((e) => e.id === id) - if (index !== -1) { - newEvals[index].status = res[ix].status - newEvals[index].duration = calcEvalDuration(newEvals[index]) - } - }) - if ( - res.some((item) => !runningStatuses.includes(item.status.value)) - ) - fetcher() - return newEvals - }) - }) - .catch(console.error), - {delayMs: 2000, timeoutMs: Infinity}, - ).stopper - } - - return () => { - stoppers.current?.() - } - }, [JSON.stringify(runningEvaluationIds)]) - - const evaluatorConfigs = useMemo( - () => - uniqBy( - evaluations - .map((item) => - item.aggregated_results.map((item) => ({ - ...item.evaluator_config, - evaluator: evaluators.find( - (e) => e.key === item.evaluator_config.evaluator_key, - ), - })), - ) - .flat(), - "id", - ), - [evaluations], - ) - - const compareDisabled = useMemo( - () => - selected.length < 2 || - selected.some( - (item) => - item.status.value === EvaluationStatus.STARTED || - item.status.value === EvaluationStatus.INITIALIZED || - item.testset.id !== selected[0].testset.id, - ), - [selected], - ) - - const colDefs = useMemo(() => { - const colDefs: ColDef<_Evaluation>[] = [ - { - field: "variants", - flex: 1, - minWidth: 160, - pinned: "left", - headerCheckboxSelection: true, - hide: hiddenCols.includes("Variant"), - checkboxSelection: true, - showDisabledCheckboxes: true, - cellRenderer: (params: any) => { - const {revisions, variants} = params.data - return ( - - {params.value} - - ) - }, - onCellClicked(params: any) { - const {revisions, variants} = params.data - router.push( - `/apps/${appId}/playground?variant=${variants[0].variantName}&revision=${revisions[0]}`, - ) - }, - valueGetter: (params) => - variantNameWithRev({ - variant_name: params.data?.variants[0].variantName ?? "", - revision: params.data?.revisions[0], - }), - headerName: "Variant", - tooltipValueGetter: (params) => params.data?.variants[0].variantName, - ...getFilterParams("text"), - }, - { - field: "testset.name", - hide: hiddenCols.includes("Testset"), - headerName: "Testset", - cellRenderer: (params: any) => ( - - ), - flex: 1, - minWidth: 160, - tooltipValueGetter: (params) => params.value, - ...getFilterParams("text"), - onCellClicked(params) { - router.push(`/apps/${appId}/testsets/${params.data?.testset.id}`) - }, - }, - ...evaluatorConfigs.map( - (config) => - ({ - flex: 1, - minWidth: 190, - hide: hiddenCols.includes(config.name), - field: "aggregated_results", - headerName: config.name, - headerComponent: (props: any) => ( - - - - - {config.name} - - - {config.evaluator?.name} - - - - ), - autoHeaderHeight: true, - ...getFilterParams("number"), - cellRenderer: (params: ValueGetterParams<_Evaluation, any>) => { - const result = params.data?.aggregated_results.find( - (item) => item.evaluator_config.id === config.id, - )?.result - - return result?.error ? ( - - Error - - ) : ( - {getTypedValue(result)} - ) - }, - valueGetter: (params) => - getTypedValue( - params.data?.aggregated_results.find( - (item) => item.evaluator_config.id === config.id, - )?.result, - ), - tooltipValueGetter: (params) => - params.data?.aggregated_results - .find((item) => item.evaluator_config.id === config.id) - ?.result?.value?.toString() || "", - }) as ColDef<_Evaluation>, - ), - { - flex: 1, - headerName: "Status", - hide: hiddenCols.includes("Status"), - field: "status", - minWidth: 185, - pinned: "right", - ...getFilterParams("text"), - 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, - field: "average_latency", - headerName: "Avg. Latency", - hide: hiddenCols.includes("Latency"), - minWidth: 120, - ...getFilterParams("number"), - valueGetter: (params) => getTypedValue(params?.data?.average_latency), - }, - { - flex: 1, - field: "total_cost", - headerName: "Total Cost", - hide: hiddenCols.includes("Cost"), - minWidth: 120, - ...getFilterParams("number"), - valueGetter: (params) => getTypedValue(params?.data?.total_cost), - }, - { - flex: 1, - field: "created_at", - headerName: "Created", - hide: hiddenCols.includes("Created"), - minWidth: 160, - ...getFilterParams("date"), - cellRenderer: DateFromNowRenderer, - sort: "desc", - valueFormatter: (params) => formatDate24(params.value), - }, - ] - return colDefs - }, [evaluatorConfigs, hiddenCols]) - - const compareBtnNode = ( - - ) - const onToggleEvaluatorVisibility = (evalConfigId: string) => { - if (!hiddenCols.includes(evalConfigId)) { - setHiddenCols([...hiddenCols, evalConfigId]) - } else { - setHiddenCols(hiddenCols.filter((item) => item !== evalConfigId)) - } - } - - const shownCols = useMemo( - () => - colDefs - .map((item) => item.headerName) - .filter((item) => item !== undefined && !hiddenCols.includes(item)) as string[], - [colDefs], - ) - - const handleOpenChangeFilterCols: DropdownProps["onOpenChange"] = (nextOpen, info) => { - if (info.source === "trigger" || nextOpen) { - setIsFilterColsDropdownOpen(nextOpen) - } - } - - 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 ? ( - - router.push(`/apps/${appId}/evaluations/new-evaluator`) - } - onBeginEvaluation={() => { - setNewEvalModalOpen(true) - }} - /> - ) : ( -
- - - {compareDisabled ? ( - - {compareBtnNode} - - ) : ( - compareBtnNode - )} - - - - - { - onToggleEvaluatorVisibility(key) - setIsFilterColsDropdownOpen(true) - }} - /> - - - - -
- - ref={gridRef as any} - rowData={evaluations} - columnDefs={colDefs} - rowStyle={{ - cursor: "pointer", - }} - getRowId={(params) => params.data.id} - onRowClicked={(params) => { - // ignore clicks on the checkbox col - if ( - params.eventPath?.find( - (item: any) => item.ariaColIndex === "1", - ) - ) - return - ;(EvaluationStatus.FINISHED === params.data?.status.value || - EvaluationStatus.FINISHED_WITH_ERRORS === - params.data?.status.value || - EvaluationStatus.AGGREGATION_FAILED === - params.data?.status.value) && - router.push( - `/apps/${appId}/evaluations/results/${params.data?.id}`, - ) - }} - rowSelection="multiple" - suppressRowClickSelection - onSelectionChanged={(event) => - setSelected(event.api.getSelectedRows()) - } - tooltipShowDelay={0} - /> -
-
-
- )} - { - setNewEvalModalOpen(false) - setQueryNewEvalModalOpen("") - }} - onSuccess={() => { - setNewEvalModalOpen(false) - setQueryNewEvalModalOpen("") - fetcher() - }} - /> - - ) -} - -export default EvaluationResults diff --git a/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx b/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx index 0c52b085d5..acd74fc2fd 100644 --- a/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx +++ b/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx @@ -23,7 +23,7 @@ import {useRouter} from "next/router" import React, {useEffect, useMemo, useRef, useState} from "react" import {createUseStyles} from "react-jss" import StatusRenderer from "./StatusRenderer" -import NewEvaluationModal from "../../evaluations/evaluationResults/NewEvaluationModal" +import NewEvaluationModal from "../../evaluations/NewEvaluation/NewEvaluationModal" import {useAtom} from "jotai" import {evaluatorConfigsAtom, evaluatorsAtom} from "@/lib/atoms/evaluation" import {runningStatuses} from "../../evaluations/cellRenderers/cellRenderers" From c7f1afdd93c195d29512a2b5d887484ec7f1c14d Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 5 Sep 2024 12:09:15 +0600 Subject: [PATCH 05/14] fix(frontend): failing cypress test due to layout change --- agenta-web/cypress/e2e/eval.comparison.cy.ts | 22 ++++++-------- agenta-web/cypress/e2e/eval.evaluations.cy.ts | 18 ++++++----- agenta-web/cypress/e2e/eval.scenarios.cy.ts | 13 ++++---- .../cypress/support/commands/evaluations.ts | 6 +--- .../HumanEvaluations/AbTestingEvaluation.tsx | 5 +++- .../SingleModelEvaluation.tsx | 5 +++- .../autoEvaluation/AutoEvaluation.tsx | 30 ++++++++++++------- .../autoEvaluation/SearchFilter.tsx | 1 + .../AutomaticEvalOverview.tsx | 2 +- .../automaticEvaluation/StatusRenderer.tsx | 2 +- 10 files changed, 57 insertions(+), 47 deletions(-) diff --git a/agenta-web/cypress/e2e/eval.comparison.cy.ts b/agenta-web/cypress/e2e/eval.comparison.cy.ts index cf97725acb..a5e1e33fe4 100644 --- a/agenta-web/cypress/e2e/eval.comparison.cy.ts +++ b/agenta-web/cypress/e2e/eval.comparison.cy.ts @@ -39,8 +39,8 @@ describe("Evaluation Comparison Test", function () { context("Executing Evaluation Comparison Workflow", () => { beforeEach(() => { - cy.visit(`/apps/${app_id}/evaluations/results`) - cy.location("pathname").should("include", "/evaluations/results") + cy.visit(`/apps/${app_id}/evaluations`) + cy.location("pathname").should("include", "/evaluations") }) it("Should create 2 new Evaluations", () => { @@ -48,11 +48,7 @@ describe("Evaluation Comparison Test", function () { url: `${Cypress.env().baseApiURL}/evaluations/?app_id=${app_id}`, method: "GET", }).then((resp) => { - if (resp.body.length) { - cy.get('[data-cy="new-evaluation-button"]').click() - } else { - cy.get('[data-cy="new-evaluation-button__no_variants"]').click() - } + cy.get('[data-cy="new-evaluation-button"]').click() }) cy.get(".ant-modal-content").should("exist") @@ -73,19 +69,19 @@ describe("Evaluation Comparison Test", function () { }) it("Should verify that there are completed evaluations in the list", () => { - cy.get('.ag-row[row-index="0"]').should("exist") - cy.get('.ag-row[row-index="1"]').should("exist") - cy.get('.ag-cell[col-id="status"]', {timeout: 60000}) + cy.get(".ant-table-row").eq(0).should("exist") + cy.get(".ant-table-row").eq(1).should("exist") + cy.get('[data-cy="evaluation-status-cell"]', {timeout: 60000}) .eq(0) .should("contain.text", "Completed") - cy.get('.ag-cell[col-id="status"]', {timeout: 60000}) + cy.get('[data-cy="evaluation-status-cell"]', {timeout: 60000}) .eq(1) .should("contain.text", "Completed") }) it("Should select 2 evaluations, click on the compare button, and successfully navigate to the comparison page", () => { - cy.get("div.ag-selection-checkbox input").eq(0).check() - cy.get("div.ag-selection-checkbox input").eq(1).check() + cy.get(".ant-checkbox-input").eq(0).check() + cy.get('[data-cy="evaluation-results-compare-button"]').should("not.be.disabled") cy.get('[data-cy="evaluation-results-compare-button"]').click() cy.location("pathname").should("include", "/evaluations/results/compare") diff --git a/agenta-web/cypress/e2e/eval.evaluations.cy.ts b/agenta-web/cypress/e2e/eval.evaluations.cy.ts index 248a4e4778..52ad572f98 100644 --- a/agenta-web/cypress/e2e/eval.evaluations.cy.ts +++ b/agenta-web/cypress/e2e/eval.evaluations.cy.ts @@ -9,8 +9,8 @@ describe("Evaluations CRUD Operations Test", function () { context("Executing Evaluations CRUD operations", () => { beforeEach(() => { - cy.visit(`/apps/${app_id}/evaluations/results`) - cy.location("pathname").should("include", "/evaluations/results") + cy.visit(`/apps/${app_id}/evaluations`) + cy.location("pathname").should("include", "/evaluations") }) it("Should successfully create an Evaluation", () => { @@ -26,15 +26,17 @@ describe("Evaluations CRUD Operations Test", function () { }) it("Should verify the successful creation and completion of the evaluation", () => { - cy.get('.ag-row[row-index="0"]').should("exist") - cy.get('.ag-cell[col-id="status"]').should("contain.text", "Completed") + cy.get(".ant-table-row").eq(0).should("exist") + cy.get('[data-cy="evaluation-status-cell"]').should("contain.text", "Completed") }) it("Should select evaluation and successfully delete it", () => { - cy.get(".ag-root-wrapper").should("exist") - cy.get("div.ag-selection-checkbox input").eq(0).check() - cy.get(":nth-child(1) > .ant-btn > .ant-btn-icon > .anticon > svg").click() - cy.get(".ant-modal-confirm-btns > :nth-child(2) > span").click() + cy.get(".ant-checkbox-wrapper").should("exist") + cy.get(".ant-checkbox-input").eq(0).check() + cy.get('[data-cy="delete-evaluation-button"]').click() + + cy.get(".ant-modal-content").should("exist") + cy.get(".ant-modal-footer > .ant-btn-primary").click() }) }) diff --git a/agenta-web/cypress/e2e/eval.scenarios.cy.ts b/agenta-web/cypress/e2e/eval.scenarios.cy.ts index 51d9bf3714..5c545b13bd 100644 --- a/agenta-web/cypress/e2e/eval.scenarios.cy.ts +++ b/agenta-web/cypress/e2e/eval.scenarios.cy.ts @@ -9,8 +9,8 @@ describe("Evaluation Scenarios Test", function () { context("Executing Evaluation Scenarios Workflow", () => { beforeEach(() => { - cy.visit(`/apps/${app_id}/evaluations/results`) - cy.location("pathname").should("include", "/evaluations/results") + cy.visit(`/apps/${app_id}/evaluations`) + cy.location("pathname").should("include", "/evaluations") }) it("Should successfully create an Evaluation", () => { @@ -18,15 +18,14 @@ describe("Evaluation Scenarios Test", function () { }) it("Should verify that evalaution was created and completed successfully", () => { - cy.get('.ag-row[row-index="0"]').should("exist") - cy.get('.ag-cell[col-id="status"]').should("contain.text", "Completed") + cy.get(".ant-table-row").eq(0).should("exist") + cy.get('[data-cy="evaluation-status-cell"]').should("contain.text", "Completed") }) it("Should double click on the Evaluation and successfully navigate to the evalaution results page", () => { - cy.get(".ag-root-wrapper").should("exist") - cy.get('.ag-row-first > [col-id="aggregated_results"]').click() + cy.get(".ant-table-row").eq(0).should("exist") + cy.get(".ant-table-row").click() cy.wait(1000) - cy.get(".ag-cell-focus").dblclick() cy.contains(/Evaluation Results/i) cy.get('[data-cy="evalaution-scenarios-table"]').should("exist") }) diff --git a/agenta-web/cypress/support/commands/evaluations.ts b/agenta-web/cypress/support/commands/evaluations.ts index 78215ed34f..f47417bb0f 100644 --- a/agenta-web/cypress/support/commands/evaluations.ts +++ b/agenta-web/cypress/support/commands/evaluations.ts @@ -105,11 +105,7 @@ Cypress.Commands.add("createNewEvaluation", () => { url: `${Cypress.env().baseApiURL}/evaluations/?app_id=${app_id}`, method: "GET", }).then((resp) => { - if (resp.body.length) { - cy.get('[data-cy="new-evaluation-button"]').click() - } else { - cy.get('[data-cy="new-evaluation-button__no_variants"]').click() - } + cy.get('[data-cy="new-evaluation-button"]').click() }) cy.get(".ant-modal-content").should("exist") diff --git a/agenta-web/src/components/HumanEvaluations/AbTestingEvaluation.tsx b/agenta-web/src/components/HumanEvaluations/AbTestingEvaluation.tsx index 2c8b6b4d4f..a36a69ef14 100644 --- a/agenta-web/src/components/HumanEvaluations/AbTestingEvaluation.tsx +++ b/agenta-web/src/components/HumanEvaluations/AbTestingEvaluation.tsx @@ -414,7 +414,10 @@ const AbTestingEvaluation = ({viewType}: {viewType: "evaluation" | "overview"})
A/B Testing Evaluations - diff --git a/agenta-web/src/components/HumanEvaluations/SingleModelEvaluation.tsx b/agenta-web/src/components/HumanEvaluations/SingleModelEvaluation.tsx index b2a2ec26b5..590849affa 100644 --- a/agenta-web/src/components/HumanEvaluations/SingleModelEvaluation.tsx +++ b/agenta-web/src/components/HumanEvaluations/SingleModelEvaluation.tsx @@ -311,7 +311,10 @@ const SingleModelEvaluation = ({viewType}: {viewType: "evaluation" | "overview"} Single Model Evaluations - diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx index 58386d3faa..5a3feabd10 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx @@ -247,19 +247,22 @@ const AutoEvaluation = () => { title: evaluator.name, key: `results-${idx}`, onHeaderCell: () => ({style: {minWidth: 240}}), - showSorterTooltip: false, + sortDirections: ["descend", "ascend"], sorter: { compare: (a, b) => { - const getSortValue = (item: _Evaluation) => { - if (item.aggregated_results && item.aggregated_results.length > 0) { - const result = item.aggregated_results[0].result - if (result && typeof result.value === "number") { - return result.value - } + const getSortValue = (item: _Evaluation, evaluatorId: string) => { + const matchingResult = item.aggregated_results.find( + (result) => result.evaluator_config.id === evaluatorId, + ) + + if (matchingResult && typeof matchingResult.result.value === "number") { + return matchingResult.result.value } + return 0 } - return getSortValue(a) - getSortValue(b) + + return getSortValue(a, evaluator.id) - getSortValue(b, evaluator.id) }, }, render: (_, record) => { @@ -269,6 +272,10 @@ const AutoEvaluation = () => { (result) => result.evaluator_config.id === evaluator.id, ) + if (matchingResults.length === 0) { + return - + } + return ( {matchingResults.map((result, index) => @@ -276,7 +283,7 @@ const AutoEvaluation = () => { @@ -522,6 +529,7 @@ const AutoEvaluation = () => { icon={} className={classes.button} onClick={() => setNewEvalModalOpen(true)} + data-cy="new-evaluation-button" > Start new evaluation @@ -541,6 +549,7 @@ const AutoEvaluation = () => { className={classes.button} onClick={() => setIsDeleteEvalMultipleModalOpen(true)} disabled={selectedRowKeys.length == 0} + data-cy="delete-evaluation-button" > Delete @@ -549,6 +558,7 @@ const AutoEvaluation = () => { icon={} className={classes.button} disabled={compareDisabled} + data-cy="evaluation-results-compare-button" onClick={() => router.push( `/apps/${appId}/evaluations/results/compare?evaluations=${selectedRowKeys.join(",")}`, @@ -588,7 +598,7 @@ const AutoEvaluation = () => { pagination={false} onRow={(record) => ({ style: {cursor: "pointer"}, - onClick: () => {}, + onClick: () => router.push(`/apps/${appId}/evaluations/results/${record.id}`), })} /> diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx index 45d9e51435..b8f1434dd2 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx @@ -35,6 +35,7 @@ export function getFilterParams( confirm({closeDropdown: false}) }} style={{display: "block"}} + step={0.1} type={type} /> )} diff --git a/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx b/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx index acd74fc2fd..2eaa0dfe62 100644 --- a/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx +++ b/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx @@ -430,7 +430,7 @@ const AutomaticEvalOverview = () => {
Automatic Evaluations - diff --git a/agenta-web/src/components/pages/overview/automaticEvaluation/StatusRenderer.tsx b/agenta-web/src/components/pages/overview/automaticEvaluation/StatusRenderer.tsx index 107498527d..72b67f4f43 100644 --- a/agenta-web/src/components/pages/overview/automaticEvaluation/StatusRenderer.tsx +++ b/agenta-web/src/components/pages/overview/automaticEvaluation/StatusRenderer.tsx @@ -43,7 +43,7 @@ const StatusRenderer = (record: _Evaluation) => { const errorStacktrace = record.status.error?.stacktrace return ( - +
{label} {errorMsg && ( From 827680e0c7b5a1a6facab1c60d5ee2cde548046f Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 5 Sep 2024 12:20:19 +0600 Subject: [PATCH 06/14] enhance(frontend): improved structure --- .../pages/evaluations/autoEvaluation/AutoEvaluation.tsx | 6 +++--- .../autoEvaluation/{ => Filters}/EditColumns.tsx | 0 .../autoEvaluation/{ => Filters}/SearchFilter.tsx | 0 .../cellRenderers}/StatusRenderer.tsx | 2 +- .../overview/automaticEvaluation/AutomaticEvalOverview.tsx | 2 +- 5 files changed, 5 insertions(+), 5 deletions(-) rename agenta-web/src/components/pages/evaluations/autoEvaluation/{ => Filters}/EditColumns.tsx (100%) rename agenta-web/src/components/pages/evaluations/autoEvaluation/{ => Filters}/SearchFilter.tsx (100%) rename agenta-web/src/components/pages/{overview/automaticEvaluation => evaluations/cellRenderers}/StatusRenderer.tsx (95%) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx index 5a3feabd10..a60462ba5b 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx @@ -32,12 +32,12 @@ import {useAtom} from "jotai" import {evaluatorConfigsAtom, evaluatorsAtom} from "@/lib/atoms/evaluation" import DeleteEvaluationModal from "@/components/DeleteEvaluationModal/DeleteEvaluationModal" import {useRouter} from "next/router" -import EditColumns, {generateEditItems} from "./EditColumns" -import StatusRenderer from "../../overview/automaticEvaluation/StatusRenderer" +import EditColumns, {generateEditItems} from "./Filters/EditColumns" +import StatusRenderer from "../cellRenderers/StatusRenderer" import {runningStatuses} from "../../evaluations/cellRenderers/cellRenderers" import {useUpdateEffect} from "usehooks-ts" import {shortPoll} from "@/lib/helpers/utils" -import {getFilterParams} from "./SearchFilter" +import {getFilterParams} from "./Filters/SearchFilter" import {uniqBy} from "lodash" import NewEvaluatorModal from "../evaluators/NewEvaluatorModal" diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/EditColumns.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/EditColumns.tsx similarity index 100% rename from agenta-web/src/components/pages/evaluations/autoEvaluation/EditColumns.tsx rename to agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/EditColumns.tsx diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx similarity index 100% rename from agenta-web/src/components/pages/evaluations/autoEvaluation/SearchFilter.tsx rename to agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx diff --git a/agenta-web/src/components/pages/overview/automaticEvaluation/StatusRenderer.tsx b/agenta-web/src/components/pages/evaluations/cellRenderers/StatusRenderer.tsx similarity index 95% rename from agenta-web/src/components/pages/overview/automaticEvaluation/StatusRenderer.tsx rename to agenta-web/src/components/pages/evaluations/cellRenderers/StatusRenderer.tsx index 72b67f4f43..dc7fbbf272 100644 --- a/agenta-web/src/components/pages/overview/automaticEvaluation/StatusRenderer.tsx +++ b/agenta-web/src/components/pages/evaluations/cellRenderers/StatusRenderer.tsx @@ -4,7 +4,7 @@ import {InfoCircleOutlined} from "@ant-design/icons" import {theme, Tooltip, Typography} from "antd" import React from "react" import {createUseStyles} from "react-jss" -import {runningStatuses, statusMapper} from "../../evaluations/cellRenderers/cellRenderers" +import {runningStatuses, statusMapper} from "./cellRenderers" const useStyles = createUseStyles((theme: JSSTheme) => ({ statusCell: { diff --git a/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx b/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx index 2eaa0dfe62..c6ae12388d 100644 --- a/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx +++ b/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx @@ -22,7 +22,7 @@ import {ColumnsType} from "antd/es/table" import {useRouter} from "next/router" import React, {useEffect, useMemo, useRef, useState} from "react" import {createUseStyles} from "react-jss" -import StatusRenderer from "./StatusRenderer" +import StatusRenderer from "../../evaluations/cellRenderers/StatusRenderer" import NewEvaluationModal from "../../evaluations/NewEvaluation/NewEvaluationModal" import {useAtom} from "jotai" import {evaluatorConfigsAtom, evaluatorsAtom} from "@/lib/atoms/evaluation" From d8bca048ffe2145ef709e026e87248a27f8f69a8 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 5 Sep 2024 12:30:22 +0600 Subject: [PATCH 07/14] fix(frontend): prettier format --- agenta-web/cypress/e2e/eval.comparison.cy.ts | 2 +- agenta-web/cypress/e2e/eval.evaluations.cy.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/agenta-web/cypress/e2e/eval.comparison.cy.ts b/agenta-web/cypress/e2e/eval.comparison.cy.ts index a5e1e33fe4..a8203ae672 100644 --- a/agenta-web/cypress/e2e/eval.comparison.cy.ts +++ b/agenta-web/cypress/e2e/eval.comparison.cy.ts @@ -80,7 +80,7 @@ describe("Evaluation Comparison Test", function () { }) it("Should select 2 evaluations, click on the compare button, and successfully navigate to the comparison page", () => { - cy.get(".ant-checkbox-input").eq(0).check() + cy.get(".ant-checkbox-input").eq(0).check() cy.get('[data-cy="evaluation-results-compare-button"]').should("not.be.disabled") cy.get('[data-cy="evaluation-results-compare-button"]').click() diff --git a/agenta-web/cypress/e2e/eval.evaluations.cy.ts b/agenta-web/cypress/e2e/eval.evaluations.cy.ts index 52ad572f98..633dd8b84e 100644 --- a/agenta-web/cypress/e2e/eval.evaluations.cy.ts +++ b/agenta-web/cypress/e2e/eval.evaluations.cy.ts @@ -36,7 +36,7 @@ describe("Evaluations CRUD Operations Test", function () { cy.get('[data-cy="delete-evaluation-button"]').click() cy.get(".ant-modal-content").should("exist") - cy.get(".ant-modal-footer > .ant-btn-primary").click() + cy.get(".ant-modal-footer > .ant-btn-primary").click() }) }) From baf3e683897edc4d33cedd8baf3e7aafccbdc7da Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 5 Sep 2024 12:40:43 +0600 Subject: [PATCH 08/14] fix(frontend): lint error --- .../evaluations/autoEvaluation/Filters/SearchFilter.tsx | 9 --------- 1 file changed, 9 deletions(-) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx index b8f1434dd2..d4211bd887 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx @@ -1,7 +1,6 @@ import {_Evaluation} from "@/lib/Types" import {Input, InputRef, TableColumnType, DatePicker} from "antd" import {FilterDropdownProps} from "antd/es/table/interface" -import React, {useRef} from "react" import dayjs from "dayjs" type DataIndex = keyof _Evaluation @@ -12,8 +11,6 @@ export function getFilterParams( dataIndex: DataIndex, type: CellDataType, ): TableColumnType<_Evaluation> { - const searchInput = useRef(null) - const filterDropdown = ({setSelectedKeys, selectedKeys, confirm}: FilterDropdownProps) => { return (
e.stopPropagation()}> @@ -27,7 +24,6 @@ export function getFilterParams( /> ) : ( { @@ -70,10 +66,5 @@ export function getFilterParams( return { filterDropdown, onFilter, - onFilterDropdownOpenChange: (visible) => { - if (visible) { - setTimeout(() => searchInput.current?.select(), 100) - } - }, } } From c43e6c1c97e6842b0b24f2164ac32c2fc4aa3c60 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Thu, 5 Sep 2024 22:29:37 +0100 Subject: [PATCH 09/14] fix(frontend): added EvaluationErrorPopover component and improve evaluation result output display --- .../EvaluationErrorPopover.tsx | 43 +++++++++++ .../autoEvaluation/AutoEvaluation.tsx | 75 ++----------------- .../AutomaticEvalOverview.tsx | 30 +------- 3 files changed, 53 insertions(+), 95 deletions(-) create mode 100644 agenta-web/src/components/pages/evaluations/EvaluationErrorProps/EvaluationErrorPopover.tsx diff --git a/agenta-web/src/components/pages/evaluations/EvaluationErrorProps/EvaluationErrorPopover.tsx b/agenta-web/src/components/pages/evaluations/EvaluationErrorProps/EvaluationErrorPopover.tsx new file mode 100644 index 0000000000..5261232393 --- /dev/null +++ b/agenta-web/src/components/pages/evaluations/EvaluationErrorProps/EvaluationErrorPopover.tsx @@ -0,0 +1,43 @@ +import {EvaluationError, JSSTheme, TypedValue} from "@/lib/Types" +import {InfoCircleOutlined} from "@ant-design/icons" +import {Button, Popover, Typography} from "antd" +import React from "react" +import {createUseStyles} from "react-jss" + +const useStyles = createUseStyles((theme: JSSTheme) => ({ + errModalStackTrace: { + maxWidth: 300, + "& code": { + display: "block", + width: "100%", + }, + }, +})) + +const EvaluationErrorPopover = (result: { + result: TypedValue & { + error: null | EvaluationError + } +}) => { + const classes = useStyles() + + return ( + + {result.result.error?.stacktrace} + + } + title={result.result.error?.message} + > + + + ) +} + +export default EvaluationErrorPopover diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx index a60462ba5b..94ba4fc83c 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx @@ -9,11 +9,11 @@ import { Rocket, Trash, } from "@phosphor-icons/react" -import {Button, Dropdown, DropdownProps, message, Popover, Space, Table, Tag} from "antd" +import {Button, Dropdown, DropdownProps, message, Space, Table, Typography} from "antd" import React, {useEffect, useMemo, useRef, useState} from "react" import {createUseStyles} from "react-jss" import {ColumnsType} from "antd/es/table" -import {EditOutlined, InfoCircleOutlined, MoreOutlined} from "@ant-design/icons" +import {MoreOutlined} from "@ant-design/icons" import EvaluatorsModal from "./EvaluatorsModal/EvaluatorsModal" import {useQueryParam} from "@/hooks/useQuery" import {formatDay} from "@/lib/helpers/dateTimeHelper" @@ -40,6 +40,7 @@ import {shortPoll} from "@/lib/helpers/utils" import {getFilterParams} from "./Filters/SearchFilter" import {uniqBy} from "lodash" import NewEvaluatorModal from "../evaluators/NewEvaluatorModal" +import EvaluationErrorPopover from "../EvaluationErrorProps/EvaluationErrorPopover" const useStyles = createUseStyles((theme: JSSTheme) => ({ resultTag: { @@ -280,73 +281,11 @@ const AutoEvaluation = () => { {matchingResults.map((result, index) => result.result.error ? ( - - {result.result.error?.stacktrace} -
- } - title={result.result.error?.message} - > - - + ) : ( - e.stopPropagation()} - > -
- {result.evaluator_config.name} -
-
{getTypedValue(result.result)}
-
- } - title={ -
e.stopPropagation()} - > - - {evaluator?.name} - -
- } - > -
e.stopPropagation()} - className={classes.resultTag} - > -
{result.evaluator_config.name}
-
{getTypedValue(result.result)}
-
- + + {getTypedValue(result.result)} + ), )} diff --git a/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx b/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx index c6ae12388d..04be6b8fd0 100644 --- a/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx +++ b/agenta-web/src/components/pages/overview/automaticEvaluation/AutomaticEvalOverview.tsx @@ -9,13 +9,7 @@ import { fetchAllEvaluators, fetchEvaluationStatus, } from "@/services/evaluations/api" -import { - EditOutlined, - InfoCircleOutlined, - MoreOutlined, - PlusOutlined, - SwapOutlined, -} from "@ant-design/icons" +import {EditOutlined, MoreOutlined, PlusOutlined, SwapOutlined} from "@ant-design/icons" import {Database, GearSix, Note, Rocket, Trash} from "@phosphor-icons/react" import {Button, Dropdown, message, Popover, Space, Spin, Table, Tag, Typography} from "antd" import {ColumnsType} from "antd/es/table" @@ -31,6 +25,7 @@ import {useUpdateEffect} from "usehooks-ts" import {shortPoll} from "@/lib/helpers/utils" import NewEvaluatorModal from "../../evaluations/evaluators/NewEvaluatorModal" import DeleteEvaluationModal from "@/components/DeleteEvaluationModal/DeleteEvaluationModal" +import EvaluationErrorPopover from "../../evaluations/EvaluationErrorProps/EvaluationErrorPopover" const {Title} = Typography @@ -250,26 +245,7 @@ const AutomaticEvalOverview = () => { ) return result.result.error ? ( - - {result.result.error?.stacktrace} -
- } - title={result.result.error?.message} - > - -
+ ) : ( Date: Fri, 6 Sep 2024 17:02:27 +0600 Subject: [PATCH 10/14] enhance(frontend): edit columns and short columns --- .../autoEvaluation/AutoEvaluation.tsx | 38 +++++++++++++++---- .../autoEvaluation/Filters/EditColumns.tsx | 37 +++++++++++++----- .../autoEvaluation/Filters/SearchFilter.tsx | 11 +++++- 3 files changed, 68 insertions(+), 18 deletions(-) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx index 94ba4fc83c..d02da22610 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx @@ -9,7 +9,7 @@ import { Rocket, Trash, } from "@phosphor-icons/react" -import {Button, Dropdown, DropdownProps, message, Space, Table, Typography} from "antd" +import {Button, Dropdown, DropdownProps, message, Space, Table, Tag, Typography} from "antd" import React, {useEffect, useMemo, useRef, useState} from "react" import {createUseStyles} from "react-jss" import {ColumnsType} from "antd/es/table" @@ -244,9 +244,18 @@ const AutoEvaluation = () => { title: "Results", key: "results", onHeaderCell: () => ({style: {minWidth: 240}}), - children: evaluatorConfigs.map((evaluator, idx) => ({ - title: evaluator.name, - key: `results-${idx}`, + children: evaluatorConfigs.map((evaluator) => ({ + title: () => { + return ( +
+ {evaluator.name} + + {evaluator.evaluator?.name} + +
+ ) + }, + key: evaluator.name, onHeaderCell: () => ({style: {minWidth: 240}}), sortDirections: ["descend", "ascend"], sorter: { @@ -312,6 +321,10 @@ const AutoEvaluation = () => { onHeaderCell: () => ({ style: {minWidth: 160}, }), + sorter: { + compare: (a, b) => + Number(a.average_latency?.value) - Number(b.average_latency?.value), + }, render: (_, record) => { return getTypedValue(record.average_latency) }, @@ -324,6 +337,9 @@ const AutoEvaluation = () => { onHeaderCell: () => ({ style: {minWidth: 160}, }), + sorter: { + compare: (a, b) => Number(a.average_cost?.value) - Number(b.average_cost?.value), + }, render: (_, record) => { return getTypedValue(record.average_cost) }, @@ -450,13 +466,21 @@ const AutoEvaluation = () => { }, [JSON.stringify(runningEvaluationIds)]) useEffect(() => { - const defaultColumnNames = columns.map((item) => item.key as string) - setEditColumns(defaultColumnNames) - }, []) + const defaultColumnNames = columns.flatMap((col) => + "children" in col ? [col.key, ...col.children.map((child) => child.key)] : [col.key], + ) + setEditColumns(defaultColumnNames as string[]) + }, [isEvalLoading]) const editedColumns = columns.map((item) => ({ ...item, hidden: !editColumns?.includes(item.key as string), + ...("children" in item && { + children: item.children.map((child) => ({ + ...child, + hidden: !editColumns.includes(child.key as string), + })), + }), })) return ( diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/EditColumns.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/EditColumns.tsx index 979b519b02..2346402a7f 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/EditColumns.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/EditColumns.tsx @@ -30,15 +30,34 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ export const generateEditItems = (columns: ColumnsType, editColumns: string[]) => { return columns .filter((col) => col.key !== "key") - .map((col) => ({ - key: col.key, - label: ( - - - <>{col.title} - - ), - })) + .flatMap((col) => [ + { + key: col.key, + label: ( + + + {col.title as string} + + ), + }, + ...(("children" in col && + col.children?.map((child) => ({ + key: child.key, + label: ( + + + {child.key as string} + + ), + }))) || + []), + ]) } interface EditColumnsProps { diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx index d4211bd887..ddcc2db2fa 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/Filters/SearchFilter.tsx @@ -1,7 +1,8 @@ -import {_Evaluation} from "@/lib/Types" -import {Input, InputRef, TableColumnType, DatePicker} from "antd" +import {_Evaluation, EvaluationStatus} from "@/lib/Types" +import {Input, TableColumnType, DatePicker} from "antd" import {FilterDropdownProps} from "antd/es/table/interface" import dayjs from "dayjs" +import {statusMapper} from "@/components/pages/evaluations/cellRenderers/cellRenderers" type DataIndex = keyof _Evaluation @@ -46,6 +47,12 @@ export function getFilterParams( if (type === "date") { return dayjs(cellValue).isSame(dayjs(value), "day") } + if (dataIndex === "status") { + const statusLabel = statusMapper({} as any)(record.status.value as EvaluationStatus) + .label as EvaluationStatus + return statusLabel.toLowerCase().includes(value.toLowerCase()) + } + if (typeof cellValue === "object" && cellValue !== null) { if (Array.isArray(cellValue)) { return cellValue.some((item) => From 2ab3da370945d859b301179021ce01e8c2e0eee6 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 6 Sep 2024 21:26:30 +0600 Subject: [PATCH 11/14] fix(frontend): fixed status update issue --- .../autoEvaluation/AutoEvaluation.tsx | 110 +++++++++--------- 1 file changed, 55 insertions(+), 55 deletions(-) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx index d02da22610..aadcdf37f7 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx @@ -92,6 +92,61 @@ const AutoEvaluation = () => { ) const stoppers = useRef() + const runningEvaluationIds = useMemo( + () => + evaluationList + .filter((item) => runningStatuses.includes(item.status.value)) + .map((item) => item.id), + [evaluationList], + ) + + useUpdateEffect(() => { + stoppers.current?.() + + if (runningEvaluationIds.length) { + stoppers.current = shortPoll( + () => + Promise.all(runningEvaluationIds.map((id) => fetchEvaluationStatus(id))) + .then((res) => { + setEvaluationList((prev) => { + const newEvals = [...prev] + runningEvaluationIds.forEach((id, ix) => { + const index = newEvals.findIndex((e) => e.id === id) + if (index !== -1) { + newEvals[index].status = res[ix].status + newEvals[index].duration = calcEvalDuration(newEvals[index]) + } + }) + if ( + res.some((item) => !runningStatuses.includes(item.status.value)) + ) + fetchEvaluations() + return newEvals + }) + }) + .catch(console.error), + {delayMs: 2000, timeoutMs: Infinity}, + ).stopper + } + + return () => { + stoppers.current?.() + } + }, [JSON.stringify(runningEvaluationIds)]) + + useEffect(() => { + if (!appId) return + + fetchEvaluations() + }, [appId]) + + useEffect(() => { + const defaultColumnNames = columns.flatMap((col) => + "children" in col ? [col.key, ...col.children.map((child) => child.key)] : [col.key], + ) + setEditColumns(defaultColumnNames as string[]) + }, [isEvalLoading]) + const fetchEvaluations = async () => { try { setIsEvalLoading(true) @@ -417,61 +472,6 @@ const AutoEvaluation = () => { }, ] - const runningEvaluationIds = useMemo( - () => - evaluationList - .filter((item) => runningStatuses.includes(item.status.value)) - .map((item) => item.id), - [evaluationList], - ) - - useEffect(() => { - if (!appId) return - - fetchEvaluations() - }, [appId]) - - useUpdateEffect(() => { - stoppers.current?.() - - if (runningEvaluationIds.length) { - stoppers.current = shortPoll( - () => - Promise.all(runningEvaluationIds.map((id) => fetchEvaluationStatus(id))) - .then((res) => { - setEvaluationList((prev) => { - const newEvals = [...prev] - runningEvaluationIds.forEach((id, ix) => { - const index = newEvals.findIndex((e) => e.id === id) - if (index !== -1) { - newEvals[index].status = res[ix].status - newEvals[index].duration = calcEvalDuration(newEvals[index]) - } - }) - if ( - res.some((item) => !runningStatuses.includes(item.status.value)) - ) - fetchEvaluations() - return newEvals - }) - }) - .catch(console.error), - {delayMs: 2000, timeoutMs: Infinity}, - ).stopper - } - - return () => { - stoppers.current?.() - } - }, [JSON.stringify(runningEvaluationIds)]) - - useEffect(() => { - const defaultColumnNames = columns.flatMap((col) => - "children" in col ? [col.key, ...col.children.map((child) => child.key)] : [col.key], - ) - setEditColumns(defaultColumnNames as string[]) - }, [isEvalLoading]) - const editedColumns = columns.map((item) => ({ ...item, hidden: !editColumns?.includes(item.key as string), From 10098ab10a8311907c492a912e35805794fa24f3 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Sun, 8 Sep 2024 20:12:28 +0100 Subject: [PATCH 12/14] fix(frontend): removed bad code --- .../autoEvaluation/AutoEvaluation.tsx | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx index aadcdf37f7..7455c01d80 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/AutoEvaluation.tsx @@ -1,4 +1,4 @@ -import {_Evaluation, EvaluationStatus, EvaluatorConfig, JSSTheme} from "@/lib/Types" +import {_Evaluation, EvaluationStatus, JSSTheme} from "@/lib/Types" import { ArrowsLeftRight, Database, @@ -39,7 +39,6 @@ import {useUpdateEffect} from "usehooks-ts" import {shortPoll} from "@/lib/helpers/utils" import {getFilterParams} from "./Filters/SearchFilter" import {uniqBy} from "lodash" -import NewEvaluatorModal from "../evaluators/NewEvaluatorModal" import EvaluationErrorPopover from "../EvaluationErrorProps/EvaluationErrorPopover" const useStyles = createUseStyles((theme: JSSTheme) => ({ @@ -84,7 +83,6 @@ const AutoEvaluation = () => { const [isDeleteEvalMultipleModalOpen, setIsDeleteEvalMultipleModalOpen] = useState(false) const [editColumns, setEditColumns] = useState([]) const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) - const [selectedConfigEdit, setSelectedConfigEdit] = useState() const [isEditEvalConfigOpen, setIsEditEvalConfigOpen] = useState(false) const [isConfigEvaluatorModalOpen, setIsConfigEvaluatorModalOpen] = useQueryParam( "configureEvaluatorModal", @@ -583,19 +581,6 @@ const AutoEvaluation = () => { /> )} - { - setIsEditEvalConfigOpen(false) - fetchEvaluations() - }} - newEvalModalConfigOpen={isEditEvalConfigOpen} - setNewEvalModalConfigOpen={setIsEditEvalConfigOpen} - setNewEvalModalOpen={() => {}} - editMode={true} - initialValues={selectedConfigEdit} - /> - {selectedEvalRecord && ( Date: Mon, 9 Sep 2024 10:15:22 +0600 Subject: [PATCH 13/14] test(frontend): fixed evaluator tests --- agenta-web/cypress/e2e/eval.evaluators.cy.ts | 39 ++++++++++++------- .../ConfigureEvaluator/index.tsx | 9 ++++- .../Evaluators/EvaluatorList.tsx | 2 + .../EvaluatorsModal/Evaluators/index.tsx | 1 + .../NewEvaluator/NewEvaluatorList.tsx | 1 + 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/agenta-web/cypress/e2e/eval.evaluators.cy.ts b/agenta-web/cypress/e2e/eval.evaluators.cy.ts index 0708d157d5..f167ba55e7 100644 --- a/agenta-web/cypress/e2e/eval.evaluators.cy.ts +++ b/agenta-web/cypress/e2e/eval.evaluators.cy.ts @@ -2,6 +2,7 @@ import {randString} from "../../src/lib/helpers/utils" describe("Evaluators CRUD Operations Test", function () { let newEvalName = randString(5) + let editedEvalName = randString(5) let app_id before(() => { cy.createVariant() @@ -12,30 +13,38 @@ describe("Evaluators CRUD Operations Test", function () { context("Executing Evaluators CRUD operations", () => { beforeEach(() => { - cy.visit(`/apps/${app_id}/evaluations/new-evaluator`) - cy.location("pathname").should("include", "/evaluations/new-evaluator") + cy.visit(`/apps/${app_id}/evaluations?configureEvaluatorModal=open`) + cy.url().should("include", "/evaluations?configureEvaluatorModal=open") }) - it("Should successfully create an Evaluator", () => { - cy.get('[data-cy="evaluator-card"]').should("exist") - cy.get(".ant-space > :nth-child(2) > .ant-btn").click() - cy.get('[data-cy="new-evaluator-modal"]').should("exist") - cy.get('[data-cy^="select-new-evaluator"]').eq(0).click() - cy.get('[data-cy="configure-new-evaluator-modal"]').should("exist") + it("Should successfully create an evaluator", () => { + cy.get(".ant-modal-content").should("exist") + cy.get('[data-cy="create-new-evaluator-button"]').click() + cy.get('[data-cy="new-evaluator-list"]').eq(0).click() + cy.contains(/configure new evaluator/i) cy.get('[data-cy="configure-new-evaluator-modal-input"]').type(newEvalName) cy.get('[data-cy="configure-new-evaluator-modal-save-btn"]').click() - cy.get('[data-cy="evaluator-card"]').should("have.length", 2) + cy.get('[data-cy="evaluator-list"]').should("have.length.gt", 2) }) - it("Should click on the edit button and successfully edit an evaluator", () => { - cy.get('[data-cy^="evaluator-card-edit-button"]').eq(0).click() - cy.get('[data-cy="configure-new-evaluator-modal-input"]').type("edit") + it("Should successfully edit an evaluator", () => { + cy.get(".ant-modal-content").should("exist") + cy.get('[data-cy="evaluator-menu-button"]').eq(0).trigger("mouseover") + cy.get(".ant-dropdown-menu").should("be.visible") + cy.get(".ant-dropdown-menu-item").eq(0).click() + cy.get('[data-cy="configure-new-evaluator-modal-input"]').clear() + cy.get('[data-cy="configure-new-evaluator-modal-input"]').type(editedEvalName) cy.get('[data-cy="configure-new-evaluator-modal-save-btn"]').click() }) - it("Should click on the delete button and successfully delete an evaluator", () => { - cy.get('[data-cy^="evaluator-card-delete-button"]').eq(0).click() - cy.get(".ant-modal-confirm-btns > :nth-child(2) > span").click() + it("Should successfully delete an evaluator", () => { + cy.get(".ant-modal-content").should("exist") + cy.get('[data-cy="evaluator-menu-button"]').eq(0).trigger("mouseover") + cy.get(".ant-dropdown-menu").should("be.visible") + cy.get(".ant-dropdown-menu-item") + .contains(/delete/i) + .click() + cy.get(".ant-modal-footer > .ant-btn-primary").click() }) }) diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/index.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/index.tsx index 044bc9856d..a7ba1d0dc1 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/index.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/ConfigureEvaluator/index.tsx @@ -416,7 +416,7 @@ const ConfigureEvaluator = ({ ]} className="flex-1" > - + {/* form.resetFields()}> Reset - diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/Evaluators/EvaluatorList.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/Evaluators/EvaluatorList.tsx index a30194d929..e6891a8170 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/Evaluators/EvaluatorList.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/Evaluators/EvaluatorList.tsx @@ -140,6 +140,7 @@ const EvaluatorList = ({ onClick={(e) => e.stopPropagation()} icon={} size="small" + data-cy="evaluator-menu-button" /> ) @@ -166,6 +167,7 @@ const EvaluatorList = ({ bordered onRow={(record) => ({ style: {cursor: "pointer"}, + "data-cy": "evaluator-list", onClick: () => { const selectedEval = evaluators.find((e) => e.key === record.evaluator_key) if (selectedEval) { diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/Evaluators/index.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/Evaluators/index.tsx index bceae4cc14..d08cc51afb 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/Evaluators/index.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/Evaluators/index.tsx @@ -93,6 +93,7 @@ const Evaluators = ({ type="primary" icon={} onClick={() => setCurrent(1)} + data-cy="create-new-evaluator-button" > Create new evaluator diff --git a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/NewEvaluator/NewEvaluatorList.tsx b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/NewEvaluator/NewEvaluatorList.tsx index 64fbd4bdfb..2f50049d06 100644 --- a/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/NewEvaluator/NewEvaluatorList.tsx +++ b/agenta-web/src/components/pages/evaluations/autoEvaluation/EvaluatorsModal/NewEvaluator/NewEvaluatorList.tsx @@ -67,6 +67,7 @@ const CreateEvaluatorList = ({ scroll={{x: true, y: 550}} style={{cursor: "pointer"}} onRow={(record) => ({ + "data-cy": "new-evaluator-list", onClick: () => { setSelectedEvaluator(record) setCurrent(2) From 5ebeb6c2a6b3da94ed52776cf22f9d405bf854f8 Mon Sep 17 00:00:00 2001 From: Kaosiso Ezealigo Date: Mon, 9 Sep 2024 12:43:06 +0100 Subject: [PATCH 14/14] fix(frontend): improved StatusRenderer logic to update status count --- .../pages/evaluations/cellRenderers/StatusRenderer.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/agenta-web/src/components/pages/evaluations/cellRenderers/StatusRenderer.tsx b/agenta-web/src/components/pages/evaluations/cellRenderers/StatusRenderer.tsx index dc7fbbf272..b1f1f6e8d7 100644 --- a/agenta-web/src/components/pages/evaluations/cellRenderers/StatusRenderer.tsx +++ b/agenta-web/src/components/pages/evaluations/cellRenderers/StatusRenderer.tsx @@ -35,8 +35,7 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ const StatusRenderer = (record: _Evaluation) => { const classes = useStyles() const {token} = theme.useToken() - const value = statusMapper(token)(record.status.value as EvaluationStatus) - .label as EvaluationStatus + const value = record.status.value const duration = useDurationCounter(record.duration || 0, runningStatuses.includes(value)) const {label, color} = statusMapper(token)(record.status.value as EvaluationStatus) const errorMsg = record.status.error?.message