From 63ff9bed0e10042ac6eac8a909d63ba195ffdbe1 Mon Sep 17 00:00:00 2001 From: aakrem Date: Mon, 5 Jun 2023 20:39:25 +0200 Subject: [PATCH] list app evaluations / continue from last evaluation / update app evaluation / display app evaluation --- .../models/api/app_evaluation_model.py | 11 +- .../routers/app_evaluation_router.py | 69 ++++++-- .../EvaluationTable/EmptyEvaluationTable.tsx | 35 ----- .../EvaluationTable/EvaluationTable.tsx | 148 ++++++++---------- .../components/Evaluations/Evaluations.tsx | 84 +++++----- .../Evaluations/EvaluationsList.tsx | 141 +++++++++++++++++ agenta-web/src/components/Results/Results.tsx | 15 +- agenta-web/src/lib/Types.ts | 30 ++++ agenta-web/src/lib/enums.ts | 5 + agenta-web/src/lib/services/api.ts | 67 +++++++- agenta-web/src/lib/transformers.ts | 22 +++ .../evaluations/[evaluation_id]/index.tsx | 46 ++++++ agenta-web/src/styles/globals.css | 9 ++ 13 files changed, 487 insertions(+), 195 deletions(-) delete mode 100644 agenta-web/src/components/EvaluationTable/EmptyEvaluationTable.tsx create mode 100644 agenta-web/src/components/Evaluations/EvaluationsList.tsx create mode 100644 agenta-web/src/lib/enums.ts create mode 100644 agenta-web/src/lib/transformers.ts create mode 100644 agenta-web/src/pages/apps/[app_name]/evaluations/[evaluation_id]/index.tsx diff --git a/agenta-backend/agenta_backend/models/api/app_evaluation_model.py b/agenta-backend/agenta_backend/models/api/app_evaluation_model.py index b7a594c607..7bde852eb8 100644 --- a/agenta-backend/agenta_backend/models/api/app_evaluation_model.py +++ b/agenta-backend/agenta_backend/models/api/app_evaluation_model.py @@ -1,11 +1,13 @@ -from pydantic import BaseModel -from typing import Optional, List +from pydantic import BaseModel, Field +from typing import Optional, List, Dict from datetime import datetime class ComparisonTable(BaseModel): id: str variants: Optional[List[str]] + app_name: str + dataset: Dict[str, str] = Field(...) created_at: datetime updated_at: datetime @@ -30,7 +32,12 @@ class EvaluationRow(BaseModel): class EvaluationRowUpdate(BaseModel): vote: str + outputs: List[EvaluationRowOutput] class NewComparisonTable(BaseModel): + app_name: str variants: List[str] + inputs: List[str] + dataset: Dict[str, str] = Field(...) + status: str = Field(...) \ No newline at end of file diff --git a/agenta-backend/agenta_backend/routers/app_evaluation_router.py b/agenta-backend/agenta_backend/routers/app_evaluation_router.py index b7281b00a5..0a421214ea 100644 --- a/agenta-backend/agenta_backend/routers/app_evaluation_router.py +++ b/agenta-backend/agenta_backend/routers/app_evaluation_router.py @@ -1,6 +1,6 @@ from fastapi import HTTPException, APIRouter, Body from agenta_backend.models.api.app_evaluation_model import ComparisonTable, EvaluationRow, EvaluationRowUpdate, NewComparisonTable -from agenta_backend.services.db_mongo import comparison_tables, evaluation_rows +from agenta_backend.services.db_mongo import comparison_tables, evaluation_rows, datasets from datetime import datetime from bson import ObjectId from typing import List @@ -18,16 +18,49 @@ async def create_comparison_table(newComparisonTableData: NewComparisonTable = B Returns: _description_ """ - comparison_table = dict() + comparison_table = newComparisonTableData.dict() comparison_table["created_at"] = comparison_table["updated_at"] = datetime.utcnow() - comparison_table["variants"] = newComparisonTableData.variants - result = await comparison_tables.insert_one(comparison_table) - if result.acknowledged: - comparison_table["id"] = str(result.inserted_id) + + newComparisonTable = await comparison_tables.insert_one(comparison_table) + + if newComparisonTable.acknowledged: + datasetId = comparison_table["dataset"]["_id"] + dataset = await datasets.find_one({"_id": ObjectId(datasetId)}) + csvdata = dataset["csvdata"] + for datum in csvdata: + evaluation_row = { + "comparison_table_id": str(newComparisonTable.inserted_id), + "inputs": [{'input_name': name, 'input_value': datum[name]} for name in comparison_table["inputs"]], + "outputs": [], + "vote": "", + "created_at": datetime.utcnow(), + "updated_at": datetime.utcnow() + } + await evaluation_rows.insert_one(evaluation_row) + + comparison_table["id"] = str(newComparisonTable.inserted_id) return comparison_table else: raise HTTPException(status_code=500, detail="Failed to create evaluation_row") +@router.get("/{comparison_table_id}/evaluation_rows", response_model=List[EvaluationRow]) +async def fetch_evaluation_rows(comparison_table_id: str): + """Creates an empty evaluation row + + Arguments: + evaluation_row -- _description_ + + Raises: + HTTPException: _description_ + + Returns: + _description_ + """ + cursor = evaluation_rows.find({"comparison_table_id": comparison_table_id}) + items = await cursor.to_list(length=100) # limit length to 100 for the example + for item in items: + item['id'] = str(item['_id']) + return items @router.post("/{comparison_table_id}/evaluation_row", response_model=EvaluationRow) async def create_evaluation_row(evaluation_row: EvaluationRow): @@ -72,7 +105,10 @@ async def update_evaluation_row(evaluation_row_id: str, evaluation_row: Evaluati evaluation_row_dict["updated_at"] = datetime.utcnow() result = await evaluation_rows.update_one( {'_id': ObjectId(evaluation_row_id)}, - {'$set': {'vote': evaluation_row_dict["vote"]}} + {'$set': { + 'vote': evaluation_row_dict["vote"], + 'outputs': evaluation_row_dict["outputs"] + }} ) if result.acknowledged: return evaluation_row_dict @@ -81,8 +117,8 @@ async def update_evaluation_row(evaluation_row_id: str, evaluation_row: Evaluati @router.get("/", response_model=List[ComparisonTable]) -async def get_comparison_table_id(): - """lists the ids of all comparison tables +async def fetch_list_comparison_tables(): + """lists of all comparison tables Returns: _description_ @@ -94,6 +130,21 @@ async def get_comparison_table_id(): return items +@router.get("/{comparison_table_id}", response_model=ComparisonTable) +async def fetch_comparison_table(comparison_table_id: str): + """Fetch one comparison table + + Returns: + _description_ + """ + comparison_table = await comparison_tables.find_one({"_id" : ObjectId(comparison_table_id)}) + if comparison_table: + comparison_table["id"] = str(comparison_table["_id"]) + return comparison_table + else: + raise HTTPException(status_code=404, detail=f"dataset with id {comparison_table_id} not found") + + @router.get("/{comparison_table_id}/votes_data") async def fetch_results(comparison_table_id: str): """Fetch all the results for one the comparison table diff --git a/agenta-web/src/components/EvaluationTable/EmptyEvaluationTable.tsx b/agenta-web/src/components/EvaluationTable/EmptyEvaluationTable.tsx deleted file mode 100644 index 515e968df5..0000000000 --- a/agenta-web/src/components/EvaluationTable/EmptyEvaluationTable.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Table } from 'antd'; - -const EmptyEvaluationTable = () => { - const columns = [ - { - key: '1', - title: 'Inputs' - }, - { - key: '2', - title: 'App Variant 1' - }, - { - key: '3', - title: 'App Variant 2' - }, - { - key: '4', - title: 'Evaluate' - } - ] - - const rows: any = []; - - return ( - 'editable-row'} - /> - ); -}; - -export default EmptyEvaluationTable; \ No newline at end of file diff --git a/agenta-web/src/components/EvaluationTable/EvaluationTable.tsx b/agenta-web/src/components/EvaluationTable/EvaluationTable.tsx index 6a326e55e0..299aed5f66 100644 --- a/agenta-web/src/components/EvaluationTable/EvaluationTable.tsx +++ b/agenta-web/src/components/EvaluationTable/EvaluationTable.tsx @@ -1,26 +1,30 @@ -import { useState, useEffect, useContext } from 'react'; +import { useState, useEffect } from 'react'; import type { ColumnType } from 'antd/es/table'; -import { DownOutlined, CaretRightOutlined } from '@ant-design/icons'; -import { Button, Dropdown, Input, Menu, Row, Space, Spin, Table, message } from 'antd'; +import { CaretRightOutlined } from '@ant-design/icons'; +import { Button, Input, Space, Spin, Table } from 'antd'; import { Variant, Parameter } from '@/lib/Types'; -import { updateEvaluationRow, postEvaluationRow, getVariantParameters, callVariant } from '@/lib/services/api'; +import { updateEvaluationRow, callVariant } from '@/lib/services/api'; import { useVariant } from '@/lib/hooks/useVariant'; import { useRouter } from 'next/router'; +import { EvaluationFlow } from '@/lib/enums'; interface EvaluationTableProps { + appEvaluation: any; columnsCount: number; - variants: Variant[]; - dataset: any; - comparisonTableId: string; + evaluationRows: EvaluationTableRow[]; } interface EvaluationTableRow { id?: string; - inputFields: { + inputs: { input_name: string; input_value: string; }[]; + outputs: { + variant_name: string; + variant_output: string; + }[]; columnData0: string; columnData1: string; vote: string; @@ -28,27 +32,23 @@ interface EvaluationTableRow { } /** * + * @param appEvaluation - Evaluation object + * @param evaluationRows - Evaluation rows * @param columnsCount - Number of variants to compare face to face (per default 2) - * @param appVariants - List of all app variants available for comparison - * @param dataset - The dataset selected for comparison - * @param comparisonTableId - The id of the comparison table, used to save in the eval * @returns */ -enum EvaluationFlow { - EVALUATION_STARTED, - VOTE_STARTED, - COMPARISON_RUN_STARTED -} - -const EvaluationTable: React.FC = ({ columnsCount, variants, dataset, comparisonTableId }) => { +const EvaluationTable: React.FC = ({ appEvaluation, evaluationRows, columnsCount }) => { const router = useRouter(); - const { app_name } = router.query; - const [variantInputs, setVariantInputs] = useState([]); - const [isError, setIsError] = useState(false); - const datasetRowsData = dataset.csvdata + let app_name = ''; + if (Array.isArray(router.query.app_name)) { + app_name = router.query.app_name[0]; + } else if (typeof router.query.app_name === 'string') { + app_name = router.query.app_name; + } + const variants = appEvaluation.variants; - const variantData = variants.map((variant, index) => { + const variantData = variants.map((variant: Variant) => { const { optParams, URIPath, isLoading, isError, error } = useVariant(app_name, variant); return { @@ -59,38 +59,14 @@ const EvaluationTable: React.FC = ({ columnsCount, variant error }; }); - useEffect(() => { - // TODO: move this to the evaluation component. so that we fetch evertything for this component - const fetchAndSetSchema = async () => { - try { - if (variants.length > 0) { - const { inputParams } = await getVariantParameters(app_name, variants[0]); - setVariantInputs(inputParams.map((inputParam: Parameter) => inputParam.name)); - } - } catch (e) { - setIsError(true); - } - }; - fetchAndSetSchema(); - }, [app_name, variants]); const [rows, setRows] = useState([]); useEffect(() => { - if (variantInputs.length > 0) { - const initialRows = datasetRowsData && datasetRowsData.length > 0 ? datasetRowsData.map((item: any) => { - return { - inputFields: variantInputs.map((input: string) => ({ input_name: input, input_value: item[input] })), - columnData0: '', - columnData1: '', - vote: '', - evaluationFlow: EvaluationFlow.EVALUATION_STARTED - } - }) : []; - setRows([...initialRows, ...rows]); + if (evaluationRows) { + setRows(evaluationRows); } - - }, [variantInputs, dataset]); + }, [evaluationRows]); const handleInputChange = ( e: React.ChangeEvent, @@ -98,7 +74,7 @@ const EvaluationTable: React.FC = ({ columnsCount, variant inputFieldKey: number ) => { const newRows = [...rows]; - newRows[rowIndex].inputFields[inputFieldKey].input_value = e.target.value; + newRows[rowIndex].inputs[inputFieldKey].input_value = e.target.value; setRows(newRows); }; @@ -107,35 +83,22 @@ const EvaluationTable: React.FC = ({ columnsCount, variant if (evaluation_row_id) { setRowValue(rowIndex, 'vote', 'loading'); - const data = { vote: vote }; - - updateEvaluationRow(comparisonTableId, evaluation_row_id, data) - .then(data => { - setRowValue(rowIndex, 'vote', vote); - }).catch(err => { - console.error(err); - }); - } else { + // TODO: improve this to make it dynamic const appVariantNameX = variants[0].variantName; const appVariantNameY = variants[1].variantName; const outputVariantX = rows[rowIndex].columnData0; const outputVariantY = rows[rowIndex].columnData1; const data = { - "comparison_table_id": comparisonTableId, - "inputs": rows[rowIndex].inputFields, - "outputs": [ + vote: vote, + outputs: [ { "variant_name": appVariantNameX, "variant_output": outputVariantX }, { "variant_name": appVariantNameY, "variant_output": outputVariantY } ], - "vote": vote }; - setRowValue(rowIndex, 'vote', 'loading'); - - postEvaluationRow(comparisonTableId, data) + updateEvaluationRow(appEvaluation.id, evaluation_row_id, data) .then(data => { - setRowValue(rowIndex, 'vote', data.vote); - setRowValue(rowIndex, 'id', data.id); + setRowValue(rowIndex, 'vote', vote); }).catch(err => { console.error(err); }); @@ -155,7 +118,7 @@ const EvaluationTable: React.FC = ({ columnsCount, variant }; const runEvaluation = async (rowIndex: number) => { - const inputParamsDict = rows[rowIndex].inputFields.reduce((acc, item) => { acc[item.input_name] = item.input_value; return acc; }, {}); + const inputParamsDict = rows[rowIndex].inputs.reduce((acc: { [key: string]: any }, item) => { acc[item.input_name] = item.input_value; return acc; }, {}); const columnsDataNames = ['columnData0', 'columnData1'] columnsDataNames.forEach(async (columnName: any, idx: number) => { @@ -185,12 +148,24 @@ const EvaluationTable: React.FC = ({ columnsCount, variant return ({ title: (
- App Variant: {variants[i].variantName} + App Variant: + + {variants ? variants[i].variantName : ""} +
), dataIndex: columnKey, key: columnKey, - width: '20%' + width: '20%', + render: (text: any, record: EvaluationTableRow, rowIndex: number) => { + if (record.outputs && record.outputs.length > 0) { + const outputValue = record.outputs.find((output: any) => output.variant_name === variants[i].variantName)?.variant_output; + return ( +
{outputValue}
+ ) + } + return text; + } }); }); @@ -201,21 +176,23 @@ const EvaluationTable: React.FC = ({ columnsCount, variant
Inputs (Dataset: - {dataset.name} + + {appEvaluation.dataset.name} + )
), - dataIndex: 'inputFields', + dataIndex: 'inputs', render: (text: any, record: EvaluationTableRow, rowIndex: number) => (
- {variantInputs.length == record.inputFields.length && // initial value of inputFields is array with 1 element and variantInputs could contain more than 1 element - variantInputs.map((variantInputName: string, index: number) => + {record && record.inputs && record.inputs.length && // initial value of inputs is array with 1 element and variantInputs could contain more than 1 element + record.inputs.map((input: any, index: number) =>
handleInputChange(e, rowIndex, index)} />
@@ -239,22 +216,22 @@ const EvaluationTable: React.FC = ({ columnsCount, variant
- - - {!chatModeActivated && - evaluationTable - } + {/* {chatModeActivated && ([]); + + const app_name = router.query.app_name?.toString() || ""; + + useEffect(() => { + if (!app_name) { + return; + } + const fetchAppEvaluations = async () => { + try { + const result = await loadAppEvaluations(app_name); + setEvaluationsList(result); + // setLoading(false); + } catch (error) { + console.log(error) + // setError(error); + } + }; + + fetchAppEvaluations(); + }, [app_name]); + + const onCompleteEvaluation = (appEvaluation: any ) => { // TODO: improve type + router.push(`/apps/${app_name}/evaluations/${appEvaluation.id}/`); + } + + const columns: ColumnsType = [ + { + title: 'Evaluation', + render: (value: any, record: DataType, index: number) => { + return ( + {index+1} + ) + } + }, + { + title: 'Dataset', + dataIndex: 'datasetName', + key: 'datasetName', + render: (value: any, record: DataType, index: number) => { + return ( + {record.dataset.name} + ) + } + }, + // { + // title: 'Variants votes results', + // dataIndex: 'votesData', + // key: 'votesData', + // width: '70%', + // render: (value: any, record: DataType, index: number) => { + // const variants = data[index].variants; + + // if (!variants || !record.votesData) return null; + + // return renderVotesPlot(record.votesData, variants, index, record); + // }, + // }, + { + title: 'Variants', + dataIndex: 'variants', + key: 'variants', + render: (value: any, record: DataType, index: number) => { + // const variants = evaluationsList[index].variants; + return ( +
+ {value.map((variant: Variant, index: number) => { + return + {variant.variantName} + { + index < value.length - 1 && + | + } + + })} +
+ ) + + } + }, + { + title: 'Created at', + dataIndex: 'createdAt', + key: 'createdAt', + width: '300', + }, + { + title: 'Action', + dataIndex: 'action', + key: 'action', + render: (value: any, record: DataType, index: number) => { + return ( +
+ +
+ ) + } + }, + ]; + + return ( +
+
+ + ); +} diff --git a/agenta-web/src/components/Results/Results.tsx b/agenta-web/src/components/Results/Results.tsx index 00fbe0d810..179d318313 100644 --- a/agenta-web/src/components/Results/Results.tsx +++ b/agenta-web/src/components/Results/Results.tsx @@ -3,6 +3,7 @@ import { useState, useEffect } from 'react'; import { Table, Spin, Tag, Progress } from 'antd'; import { ColumnsType } from 'antd/es/table'; import { formatDate } from '@/lib/helpers/dateTimeHelper'; +import { AppEvaluationResponseType } from '@/lib/Types'; interface DataType { id: string; @@ -17,18 +18,6 @@ interface DataType { createdAt?: string; } -interface ResponseType { - id: string; - variants: string[]; - votes_data: { - variants_votes_data: { - number_of_votes: number, - percentage: number - }, - flag_votes: { number_of_votes: number, percentage: number }, - } - created_at: string; -} interface Vote { [key: string]: number; @@ -92,7 +81,7 @@ const Results: React.FC = () => { useEffect(() => { fetchData('http://localhost/api/app_evaluations') .then(responseData => { - const initialData: DataType[] = responseData.map((item: ResponseType) => { + const initialData: DataType[] = responseData.map((item: AppEvaluationResponseType) => { return { id: item.id, createdAt: formatDate(item.created_at), diff --git a/agenta-web/src/lib/Types.ts b/agenta-web/src/lib/Types.ts index f214ac636a..e30b4c3c13 100644 --- a/agenta-web/src/lib/Types.ts +++ b/agenta-web/src/lib/Types.ts @@ -17,6 +17,17 @@ export interface Variant { parameters: Record | null; // parameters of the variant. Only set in the case of forked variants } +export interface AppEvaluation { + id: string; + createdAt: string; + variants: Variant[]; + dataset: { + _id: string; + name: string; + }; + appName: string; +} + export interface Parameter { name: string; type: string; @@ -24,3 +35,22 @@ export interface Parameter { required: boolean; default?: any; } + + +export interface AppEvaluationResponseType { + id: string; + variants: string[]; + votes_data: { + variants_votes_data: { + number_of_votes: number, + percentage: number + }, + flag_votes: { number_of_votes: number, percentage: number }, + } + app_name: string; + dataset: { + _id: string; + name: string; + } + created_at: string; +} \ No newline at end of file diff --git a/agenta-web/src/lib/enums.ts b/agenta-web/src/lib/enums.ts new file mode 100644 index 0000000000..52bed7119c --- /dev/null +++ b/agenta-web/src/lib/enums.ts @@ -0,0 +1,5 @@ +export enum EvaluationFlow { + EVALUATION_STARTED = "EVALUATION_STARTED", + COMPARISON_RUN_STARTED = "COMPARISON_RUN_STARTED", + EVALUATION_FINISHED = "EVALUATION_FINISHED", +} \ No newline at end of file diff --git a/agenta-web/src/lib/services/api.ts b/agenta-web/src/lib/services/api.ts index 5bf01a86df..6c1f60725b 100644 --- a/agenta-web/src/lib/services/api.ts +++ b/agenta-web/src/lib/services/api.ts @@ -1,7 +1,8 @@ import useSWR from 'swr'; import axios from 'axios'; import { parseOpenApiSchema } from '@/lib/helpers/openapi_parser'; -import { Variant, Parameter } from '@/lib/Types'; +import { Variant, Parameter, AppEvaluationResponseType } from '@/lib/Types'; +import { fromAppEvaluationResponseToAppEvaluation } from '../transformers'; /** * Raw interface for the parameters parsed from the openapi.json */ @@ -64,7 +65,6 @@ export const getVariantParameters = async (app: string, variant: Variant) => { * Saves a new variant to the database based on previous */ export async function saveNewVariant(appName: string, variant: Variant, parameters: Parameter[]) { - console.log(variant); const appVariant = { app_name: appName, variant_name: variant.templateVariantName, @@ -133,6 +133,21 @@ export const loadDatasetsList = (app_name: string) => { } }; +export const loadDataset = async (datasetId: string) => { + return fetch(`${API_BASE_URL}/api/datasets/${datasetId}`, { + headers: { + "Content-Type": "application/json", + } + }) + .then((res) => res.json()) + .then((data) => { + return data + }) + .catch((err) => { + console.error(err); + }); +}; + export const deleteDatasets = async (ids: string[]) => { try { const response = await axios({ @@ -153,17 +168,57 @@ const eval_endpoint = axios.create({ baseURL: `${API_BASE_URL}/api/app_evaluations`, }); +export const loadAppEvaluations = async (app_name: string) => { + try { + return await eval_endpoint.get(`?app_name=${app_name}`) + .then(responseData => { + const appEvaluations = responseData.data.map((item: AppEvaluationResponseType) => { + return fromAppEvaluationResponseToAppEvaluation(item); + }); + + return appEvaluations; + }) + } catch(error){ + console.error(error); + throw error; + } +}; + +export const loadAppEvaluation = async (appEvaluationId: string) => { + try { + return await eval_endpoint.get(appEvaluationId) + .then(responseData => { + return fromAppEvaluationResponseToAppEvaluation(responseData.data); + }) + } catch(error){ + console.error(error); + throw error; + } +}; + +export const loadEvaluationsRows = async (evaluationTableId: string) => { + try { + return await eval_endpoint.get(`${evaluationTableId}/evaluation_rows`) + .then(responseData => { + return responseData.data; + }) + } catch(error){ + console.error(error); + throw error; + } +}; + export const updateAppEvaluations = async (evaluationTableId: string, data) => { - const response = await eval_endpoint.put(`${API_BASE_URL}/${evaluationTableId}`, data); + const response = await eval_endpoint.put(`${evaluationTableId}`, data); return response.data; }; export const updateEvaluationRow = async (evaluationTableId: string, evaluationRowId: string, data) => { - const response = await eval_endpoint.put(`${API_BASE_URL}/api/app_evaluations/${evaluationTableId}/evaluation_row/${evaluationRowId}`, data); + const response = await eval_endpoint.put(`${evaluationTableId}/evaluation_row/${evaluationRowId}`, data); return response.data; }; export const postEvaluationRow = async (evaluationTableId: string, data) => { - const response = await eval_endpoint.post(`${API_BASE_URL}/api/app_evaluations/${evaluationTableId}/evaluation_row`, data); + const response = await eval_endpoint.post(`${evaluationTableId}/evaluation_row`, data); return response.data; -}; \ No newline at end of file +}; diff --git a/agenta-web/src/lib/transformers.ts b/agenta-web/src/lib/transformers.ts new file mode 100644 index 0000000000..b007370102 --- /dev/null +++ b/agenta-web/src/lib/transformers.ts @@ -0,0 +1,22 @@ +import { AppEvaluationResponseType, Variant } from "./Types"; +import { formatDate } from "./helpers/dateTimeHelper"; + +export const fromAppEvaluationResponseToAppEvaluation = (item: AppEvaluationResponseType) => { + const variants:Variant[] = item.variants.map((variantName: string) => { + const variant :Variant = { + variantName: variantName, + templateVariantName: null, + persistent: true, + parameters: null + } + return variant; + }); + + return { + id: item.id, + createdAt: formatDate(item.created_at), + variants: variants, + dataset: item.dataset, + appName: item.app_name, + } +}; \ No newline at end of file diff --git a/agenta-web/src/pages/apps/[app_name]/evaluations/[evaluation_id]/index.tsx b/agenta-web/src/pages/apps/[app_name]/evaluations/[evaluation_id]/index.tsx new file mode 100644 index 0000000000..b108a1f53d --- /dev/null +++ b/agenta-web/src/pages/apps/[app_name]/evaluations/[evaluation_id]/index.tsx @@ -0,0 +1,46 @@ +import EvaluationTable from '@/components/EvaluationTable/EvaluationTable'; +import { AppEvaluation } from '@/lib/Types'; +import { loadAppEvaluation, loadEvaluationsRows } from '@/lib/services/api'; +import { useRouter } from 'next/router'; +import { useEffect, useState } from 'react'; + +export default function Evaluation() { + const router = useRouter(); + const evaluationTableId = router.query.evaluation_id ? router.query.evaluation_id.toString() : ''; + const [evaluationRows, setEvaluationRows] = useState([]); + const [appEvaluation, setAppEvaluation] = useState(); + + const columnsCount = 2; + + useEffect(() => { + const init = async () => { + const data = await loadEvaluationsRows(evaluationTableId); + setEvaluationRows(data); + } + init(); + }, []); + + useEffect(() => { + if (!evaluationTableId) { + return; + } + const init = async () => { + const appEvaluation:AppEvaluation = await loadAppEvaluation(evaluationTableId); + setAppEvaluation(appEvaluation) + } + init(); + }, [evaluationTableId]); + + return ( +
+ {evaluationTableId && evaluationRows && appEvaluation && + + } +
+ + ); +} \ No newline at end of file diff --git a/agenta-web/src/styles/globals.css b/agenta-web/src/styles/globals.css index a16f9a26d7..388b489ccf 100644 --- a/agenta-web/src/styles/globals.css +++ b/agenta-web/src/styles/globals.css @@ -20,4 +20,13 @@ body { .button-animation { animation: pulse 3s infinite; +} + +.hover-button-wrapper { + opacity: 0; + transition: opacity 0.3s ease; +} + +.ant-table-row:hover .hover-button-wrapper { + opacity: 1; } \ No newline at end of file