From d5bec048c9d2d05b006b994020af7c70eda3a676 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 29 Nov 2024 20:22:18 +0600 Subject: [PATCH 01/28] feat(frontend): create first draft of testset drawer ui --- .../src/components/GenericDrawer/index.tsx | 10 +- .../observability/ObservabilityDashboard.tsx | 13 +- .../observability/drawer/TestsetDrawer.tsx | 254 ++++++++++++++++++ 3 files changed, 272 insertions(+), 5 deletions(-) create mode 100644 agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx diff --git a/agenta-web/src/components/GenericDrawer/index.tsx b/agenta-web/src/components/GenericDrawer/index.tsx index 8209095527..d1e35f43e4 100644 --- a/agenta-web/src/components/GenericDrawer/index.tsx +++ b/agenta-web/src/components/GenericDrawer/index.tsx @@ -7,10 +7,12 @@ type GenericDrawerProps = { headerExtra?: ReactNode mainContent: ReactNode sideContent?: ReactNode + drawerWidth?: number } & React.ComponentProps const GenericDrawer = ({...props}: GenericDrawerProps) => { - const [drawerWidth, setDrawerWidth] = useState(1200) + const initialWidth = props.drawerWidth || 1200 + const [drawerWidth, setDrawerWidth] = useState(initialWidth) return ( { {props.expandable && ( + { /> + + {activeTrace && !!traces?.length && ( ({ + editor: { + border: `1px solid ${theme.colorBorder}`, + borderRadius: theme.borderRadius, + overflow: "hidden", + "& .monaco-editor": { + width: "0 !important", + }, + }, + drawerHeading: { + fontSize: theme.fontSizeLG, + lineHeight: theme.lineHeightLG, + fontWeight: theme.fontWeightMedium, + }, + container: { + display: "flex", + flexDirection: "column", + gap: 4, + }, + label: { + fontWeight: theme.fontWeightMedium, + }, +})) + +const TestsetDrawer = ({open, setOpen}: any) => { + const {appTheme} = useAppTheme() + const classes = useStyles() + const [isNewTestset, setIsNewTestset] = useState(false) + const [testset, setTestset] = useState("") + const [testsetName, setTestsetName] = useState("") + const [formatType, setFormatType] = useState("json") + + const onClose = () => { + setOpen(false) + } + + // predifind options + const customSelectOptions = [ + {value: "create", label: "Create New Test set"}, + { + value: "divider", + label: , + className: "!p-0 !m-0 !min-h-0.5 !cursor-default", + disabled: true, + }, + ] + + const handleChange = (value: string) => {} + + const onTestsetOptionChange = (value: string) => { + if (value === "create") { + setIsNewTestset(true) + } else { + setIsNewTestset(false) + } + setTestset(value) + } + + const testsetOptions = [ + ...customSelectOptions, + {value: "jack", label: "Jack"}, + {value: "lucy", label: "Lucy"}, + ] + + const Content = () => { + return ( +
+ + Spans selected 65 + + +
+ Test set +
+ setTestsetName(e.target.value)} + placeholder="Test set name" + /> + +
+ )} +
+ + +
+ Data preview + +
+ + + + Show last 5 test set entries +
+ +
+ ({ + style: {minWidth: 160}, + }), + }, + { + title: "ground_truth", + dataIndex: "ground_truth", + key: "ground_truth", + onHeaderCell: () => ({ + style: {minWidth: 160}, + }), + }, + + { + title: "flag", + dataIndex: "flag", + key: "flag", + onHeaderCell: () => ({ + style: {minWidth: 160}, + }), + }, + ]} + // dataSource={evaluationsList} + scroll={{x: true}} + bordered + pagination={false} + /> + + + + ) + } + return ( + <> + } + /> + + ) +} + +export default TestsetDrawer From d37bb8d3efb31ab6247ad1ca48d184119a7653b5 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Fri, 29 Nov 2024 22:04:55 +0600 Subject: [PATCH 02/28] fetching testsets and proccessing the columns and rows --- .../observability/drawer/TestsetDrawer.tsx | 156 ++++++++++-------- 1 file changed, 84 insertions(+), 72 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx index 469dc03148..404bb36cf7 100644 --- a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx @@ -1,11 +1,12 @@ import GenericDrawer from "@/components/GenericDrawer" import {ArrowRight, Copy, PencilSimple, Plus, Trash} from "@phosphor-icons/react" -import {Button, Checkbox, Divider, DrawerProps, Input, Radio, Select, Table, Typography} from "antd" +import {Button, Checkbox, Divider, Input, Radio, Select, Table, Typography} from "antd" import React, {useState} from "react" import {useAppTheme} from "@/components/Layout/ThemeContextProvider" import {Editor} from "@monaco-editor/react" import {createUseStyles} from "react-jss" -import {JSSTheme} from "@/lib/Types" +import {JSSTheme, KeyValuePair, testset} from "@/lib/Types" +import {fetchTestset, useLoadTestsetsList} from "@/services/testsets/api" const useStyles = createUseStyles((theme: JSSTheme) => ({ editor: { @@ -34,15 +35,18 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ const TestsetDrawer = ({open, setOpen}: any) => { const {appTheme} = useAppTheme() const classes = useStyles() + // testset + const {testsets, isTestsetsLoading} = useLoadTestsetsList() const [isNewTestset, setIsNewTestset] = useState(false) - const [testset, setTestset] = useState("") + const [testset, setTestset] = useState({name: "", id: ""}) const [testsetName, setTestsetName] = useState("") + // table + const [tableColumns, setTableColumns] = useState([]) + const [tableRows, setTableRows] = useState([]) + const [isShowlastFiveRows, setIsShowlastFiveRows] = useState(false) + // others const [formatType, setFormatType] = useState("json") - const onClose = () => { - setOpen(false) - } - // predifind options const customSelectOptions = [ {value: "create", label: "Create New Test set"}, @@ -56,20 +60,25 @@ const TestsetDrawer = ({open, setOpen}: any) => { const handleChange = (value: string) => {} - const onTestsetOptionChange = (value: string) => { - if (value === "create") { + const onTestsetOptionChange = async (option: any) => { + if (option.value === "create") { setIsNewTestset(true) } else { setIsNewTestset(false) } - setTestset(value) - } - const testsetOptions = [ - ...customSelectOptions, - {value: "jack", label: "Jack"}, - {value: "lucy", label: "Lucy"}, - ] + setTestset({name: option.lable, id: option.value}) + + if (option.value && option.value !== "create") { + // fetch testset detailes and assign the columns and rows + const data = await fetchTestset(option.value) + + if (data) { + setTableColumns(Object.keys(data.csvdata[0]) as any) + setTableRows(data.csvdata.slice(-5)) + } + } + } const Content = () => { return ( @@ -82,11 +91,20 @@ const TestsetDrawer = ({open, setOpen}: any) => { Test set
{ - Show last 5 test set entries -
+ {tableColumns ? ( + <> +
+
({ - style: {minWidth: 160}, - }), - }, - { - title: "ground_truth", - dataIndex: "ground_truth", - key: "ground_truth", - onHeaderCell: () => ({ - style: {minWidth: 160}, - }), - }, - - { - title: "flag", - dataIndex: "flag", - key: "flag", - onHeaderCell: () => ({ - style: {minWidth: 160}, - }), - }, - ]} - // dataSource={evaluationsList} - scroll={{x: true}} - bordered - pagination={false} - /> - +
+
({ + title: column, + dataIndex: column, + key: idx, + onHeaderCell: () => ({ + style: {minWidth: 160}, + }), + }))} + dataSource={isShowlastFiveRows ? tableRows : []} + scroll={{x: true}} + bordered + pagination={false} + /> + + + ) : ( + + Please select test set to view test set preview. + + )} ) @@ -241,7 +253,7 @@ const TestsetDrawer = ({open, setOpen}: any) => { <> setOpen(false)} expandable drawerWidth={640} headerExtra="Add to test set" From 753d6d438eecb268f11e9729700631fe6c070ab6 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Sun, 1 Dec 2024 12:15:26 +0600 Subject: [PATCH 03/28] select multiple traces from table --- .../observability/ObservabilityDashboard.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx index ba3e6b94bd..859129d88a 100644 --- a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx @@ -81,6 +81,7 @@ const ObservabilityDashboard = () => { const [editColumns, setEditColumns] = useState(["span_type", "key", "usage"]) const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) const [isTestsetDrawerOpen, setIsTestsetDrawerOpen] = useState(false) + const [selectedRowKeys, setSelectedRowKeys] = useState([]) const [columns, setColumns] = useState>([ { title: "ID", @@ -265,6 +266,12 @@ const ObservabilityDashboard = () => { return () => clearInterval(interval) }, []) + const rowSelection = { + onChange: (selectedRowKeys: React.Key[]) => { + setSelectedRowKeys(selectedRowKeys) + }, + } + const selectedItem = useMemo( () => (traces?.length ? getNodeById(traces, selected) : null), [selected, traces], @@ -541,7 +548,7 @@ const ObservabilityDashboard = () => { @@ -558,6 +565,12 @@ const ObservabilityDashboard = () => {
[]} dataSource={traces} From 15f1eb07c72c3b34a8b8f68119eb907aa97cfb84 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Sun, 1 Dec 2024 12:50:30 +0600 Subject: [PATCH 04/28] creating and updating testsets --- .../observability/drawer/TestsetDrawer.tsx | 66 +++++++++++++++++-- 1 file changed, 61 insertions(+), 5 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx index 404bb36cf7..c3994bb6ed 100644 --- a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx @@ -1,12 +1,18 @@ import GenericDrawer from "@/components/GenericDrawer" import {ArrowRight, Copy, PencilSimple, Plus, Trash} from "@phosphor-icons/react" -import {Button, Checkbox, Divider, Input, Radio, Select, Table, Typography} from "antd" +import {Button, Checkbox, Divider, Input, message, Radio, Select, Table, Typography} from "antd" import React, {useState} from "react" import {useAppTheme} from "@/components/Layout/ThemeContextProvider" import {Editor} from "@monaco-editor/react" import {createUseStyles} from "react-jss" import {JSSTheme, KeyValuePair, testset} from "@/lib/Types" -import {fetchTestset, useLoadTestsetsList} from "@/services/testsets/api" +import { + createNewTestset, + fetchTestset, + updateTestset, + useLoadTestsetsList, +} from "@/services/testsets/api" +import {getStringOrJson} from "@/lib/helpers/utils" const useStyles = createUseStyles((theme: JSSTheme) => ({ editor: { @@ -46,6 +52,7 @@ const TestsetDrawer = ({open, setOpen}: any) => { const [isShowlastFiveRows, setIsShowlastFiveRows] = useState(false) // others const [formatType, setFormatType] = useState("json") + const [isLoading, setIsLoading] = useState(false) // predifind options const customSelectOptions = [ @@ -63,11 +70,18 @@ const TestsetDrawer = ({open, setOpen}: any) => { const onTestsetOptionChange = async (option: any) => { if (option.value === "create") { setIsNewTestset(true) + + // reset previous test set data if bing selected + if (tableColumns) { + setTableColumns([]) + setTableRows([]) + setIsShowlastFiveRows(false) + } } else { setIsNewTestset(false) } - setTestset({name: option.lable, id: option.value}) + setTestset({name: option.label, id: option.value}) if (option.value && option.value !== "create") { // fetch testset detailes and assign the columns and rows @@ -75,8 +89,35 @@ const TestsetDrawer = ({open, setOpen}: any) => { if (data) { setTableColumns(Object.keys(data.csvdata[0]) as any) - setTableRows(data.csvdata.slice(-5)) + setTableRows(data.csvdata) + } + } + } + + const onSaveTestset = async () => { + try { + setIsLoading(true) + + if (testset.id === "create") { + if (!testsetName) { + message.error("Please add a Test set name before saving it") + return + } + + await createNewTestset(testsetName, [{input: null, correct_answer: null}]) + message.success("Test set created successfully") + } else { + await updateTestset(testset.id as string, testset.name, tableRows) + + message.success("Test set updated successfully") } + + setOpen(false) + } catch (error) { + console.log(error) + message.error("Something went wrong. Please try again later") + } finally { + setIsLoading(false) } } @@ -203,7 +244,7 @@ const TestsetDrawer = ({open, setOpen}: any) => {
Preview - {tableColumns ? ( + {tableColumns.length > 0 ? ( <>
{
+ {/******* data-preview completed ✅ *******/}
Data preview
+ onMappingOptionChange({pathName: "data", value, idx}) + } + options={mappingOptions} /> - + onMappingOptionChange({ + pathName: "column", + value, + idx, + }) + } + options={[ + ...(testset.id === "create" ? customSelectOptions : []), + ...tableColumns?.map((column) => ({ + value: column, + lable: column, + })), + ]} + /> + {data.column === "create" && ( +
+ + onMappingOptionChange({ + pathName: "newColumn", + value: e.target.value, + idx, + }) + } + placeholder="Test set name" + /> + +
+ )} +
+ +
))} -
Preview - {tableColumns.length > 0 ? ( + {isMapColumnExist ? ( <>
({ - title: column, - dataIndex: column, + columns={mappingData.map((data, idx) => ({ + title: data.column, + dataIndex: data.column, key: idx, onHeaderCell: () => ({ style: {minWidth: 160}, }), }))} - dataSource={isShowlastFiveRows ? tableRows : []} + dataSource={[ + ...preview.data, + ...(showLastFiveRows ? tableRows.slice(-5) : []), + ]} scroll={{x: true}} bordered pagination={false} @@ -294,21 +493,21 @@ const TestsetDrawer = ({open, setOpen}: any) => { return ( <> setOpen(false)} + onClose={onClose} expandable drawerWidth={640} headerExtra="Add to test set" mainContent={} footer={
- + diff --git a/agenta-web/src/lib/helpers/utils.ts b/agenta-web/src/lib/helpers/utils.ts index 01fac1df35..06d1f52738 100644 --- a/agenta-web/src/lib/helpers/utils.ts +++ b/agenta-web/src/lib/helpers/utils.ts @@ -359,3 +359,20 @@ export const filterVariantParameters = ({ export const formatVariantIdWithHash = (variantId: string) => { return `# ${variantId.split("-")[0]}` } + +export const collectKeyPathsFromObject = (obj: any, prefix = ""): string[] => { + const paths: string[] = [] + + for (const [key, value] of Object.entries(obj)) { + const fullPath = prefix ? `${prefix}.${key}` : key + + if (value && typeof value === "object" && !Array.isArray(value)) { + const nestedPaths = collectKeyPathsFromObject(value, fullPath) + paths.push(...nestedPaths) + } else { + paths.push(fullPath) + } + } + + return paths +} From d23febe43b3625508a6b802e289884aa092109db Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Mon, 2 Dec 2024 20:42:47 +0600 Subject: [PATCH 06/28] improved the code structure --- .../observability/drawer/TestsetDrawer.tsx | 538 ++++++++++-------- 1 file changed, 291 insertions(+), 247 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx index cbcc74ffcc..43e23c6058 100644 --- a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx @@ -27,6 +27,7 @@ import { import {collectKeyPathsFromObject, getStringOrJson} from "@/lib/helpers/utils" import yaml from "js-yaml" import {useUpdateEffect} from "usehooks-ts" +import {ResizableTitle} from "@/components/ServerTable/components" const useStyles = createUseStyles((theme: JSSTheme) => ({ editor: { @@ -79,7 +80,10 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { data: [], }) - const isMapColumnExist = mappingData.some((mapping) => Boolean(mapping.column)) + const isMapColumnExist = mappingData.some((mapping) => + mapping.column === "create" ? !!mapping?.newColumn : !!mapping.column, + ) + const isNewTestset = testset.id === "create" // predefind options const customSelectOptions = useMemo( @@ -165,6 +169,13 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { } }, [mappingOptions]) + const filteredColumnOptions = useMemo(() => { + const selectedColumns = mappingData + .map((item) => item.column) + .filter((col) => col !== "create") + return tableColumns.filter((column) => !selectedColumns.includes(column)) + }, [mappingData, tableColumns]) + const onMappingOptionChange = ({ pathName, value, @@ -220,7 +231,7 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { const newTestsetData = mapAndConvertDataInCsvFormat(traceData) - if (testset.id === "create") { + if (isNewTestset) { if (!testsetName) { message.error("Please add a Test set name before saving it") return @@ -245,273 +256,306 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { } } - const Content = () => { - return ( -
- - Spans selected {traceData.length} - - - {/******* testset completed ✅ *******/} -
- Test set -
- + + + +
+ } + mainContent={ +
+ + Spans selected {traceData.length} + + + {/******* testset completed ✅ *******/} +
+ Test set +
+ setTestsetName(e.target.value)} + placeholder="Test set name" + /> + +
+ )}
- )} -
-
- - {/******* data-preview completed ✅ *******/} -
- Data preview - -
- - onMappingOptionChange({pathName: "data", value, idx}) - } - options={mappingOptions} + value={dataPreview} + onChange={(value) => setDataPreview(value)} + options={traceData.map((trace, idx) => ({ + value: trace?.key, + label: `Span ${idx + 1}`, + }))} /> - -
- +
+
+ + +
+ + {/******* mapping almost completed ✔️ *******/} +
+ Mapping +
+ {mappingData.map((data, idx) => ( +
+ + value={data.column} + onChange={(value) => onMappingOptionChange({ - pathName: "newColumn", - value: e.target.value, + pathName: "column", + value, idx, }) } - placeholder="Test set name" - /> - ({ + value: column, + lable: column, + })), + ]} /> + {data.column === "create" && ( +
+ + onMappingOptionChange({ + pathName: "newColumn", + value: e.target.value, + idx, + }) + } + placeholder="Test set name" + /> + +
+ )}
- )} -
-
+ ))} - ))} - - - - -
- Preview - {isMapColumnExist ? ( - <> -
-
({ - title: data.column, - dataIndex: data.column, - key: idx, - onHeaderCell: () => ({ - style: {minWidth: 160}, - }), - }))} - dataSource={[ - ...preview.data, - ...(showLastFiveRows ? tableRows.slice(-5) : []), - ]} - scroll={{x: true}} - bordered - pagination={false} - /> - - - ) : ( - - Please select test set to view test set preview. - - )} - - - ) - } + + - return ( - <> - } - footer={ -
- - -
+ {/******* preview almost completed ✔️ *******/} +
+ Preview + {isMapColumnExist ? ( + <> +
+
({ + title: + data.column === "create" + ? data.newColumn + : data.column, + dataIndex: + data.column === "create" + ? data.newColumn + : data.column, + key: idx, + onHeaderCell: () => ({ + style: {minWidth: 160}, + }), + }))} + dataSource={[ + ...preview.data, + ...(showLastFiveRows ? tableRows.slice(-5) : []), + ]} + components={{ + header: { + cell: ResizableTitle, + }, + }} + scroll={{x: true}} + bordered + pagination={false} + /> + + + ) : ( + + Please select test set to view test set preview. + + )} + + } /> From e2f894a628c9eba959dd9d174ff7669e4c589432 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Tue, 3 Dec 2024 15:36:42 +0600 Subject: [PATCH 07/28] fix(frontend): removed comparison function --- .../observability/ObservabilityDashboard.tsx | 49 ++----------------- 1 file changed, 4 insertions(+), 45 deletions(-) diff --git a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx index 11afa3d5e9..422b67405b 100644 --- a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx @@ -502,57 +502,16 @@ const ObservabilityDashboard = () => { const getMatchingTracesByDataKeys = () => { if (!traces?.length) return [] - // step 1: extract data from the trace - skiped children for now - // TODO: get the traces children nodes as well const extractData = traces .filter((trace) => selectedRowKeys.includes(trace.key)) - .flatMap((trace) => { - const {data, key, ...rest} = trace - return {data, key} - }) + .flatMap((trace) => ({data: trace.data, key: trace.key})) - // step 2: compare each array keys with each other to check similarities - const similarObjects = findSimilarObjects(extractData) - if (similarObjects.length > 0) { - setTestsetDrawerData(similarObjects as any) + if (extractData.length > 0) { + setTestsetDrawerData(extractData as any) setIsTestsetDrawerOpen(true) } } - const findSimilarObjects = (array: any[]): any[][] => { - const getKeySignature = (obj: any): string => { - // Function to extract nested keys - const extractKeys = (obj: any, prefix = ""): string[] => - Object.keys(obj).flatMap((key) => { - const fullPath = prefix ? `${prefix}.${key}` : key - - return typeof obj[key] === "object" && - obj[key] !== null && - !Array.isArray(obj[key]) - ? extractKeys(obj[key], fullPath) - : fullPath - }) - - // Return the normalized keys as a string - return extractKeys(obj).sort().join(",") - } - - // Group objects by their key structure and return groups with more than one item - const groups = array.reduce>((acc, item) => { - const keySignature = getKeySignature(item) - acc[keySignature] = acc[keySignature] || [] - acc[keySignature].push(item) - return acc - }, {}) - - const mostSimilarGroup = Object.values(groups).reduce( - (maxGroup, currentGroup) => - currentGroup.length > maxGroup.length ? currentGroup : maxGroup, - [], - ) - return mostSimilarGroup - } - return (
Observability @@ -699,9 +658,9 @@ const ObservabilityDashboard = () => { open={isTestsetDrawerOpen} data={testsetDrawerData} onClose={() => { - setIsTestsetDrawerOpen(false) setSelectedRowKeys([]) setTestsetDrawerData([]) + setIsTestsetDrawerOpen(false) }} /> )} From 3793f5f4b409491308a1eaabc2abfa76a4e96512 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Tue, 3 Dec 2024 17:18:29 +0600 Subject: [PATCH 08/28] improved the UI for better experience --- .../src/components/GenericDrawer/index.tsx | 4 +- .../observability/ObservabilityDashboard.tsx | 6 +- .../observability/drawer/TestsetDrawer.tsx | 252 +++++++++++------- 3 files changed, 161 insertions(+), 101 deletions(-) diff --git a/agenta-web/src/components/GenericDrawer/index.tsx b/agenta-web/src/components/GenericDrawer/index.tsx index d1e35f43e4..323aa76f5d 100644 --- a/agenta-web/src/components/GenericDrawer/index.tsx +++ b/agenta-web/src/components/GenericDrawer/index.tsx @@ -7,11 +7,11 @@ type GenericDrawerProps = { headerExtra?: ReactNode mainContent: ReactNode sideContent?: ReactNode - drawerWidth?: number + initialWidth?: number } & React.ComponentProps const GenericDrawer = ({...props}: GenericDrawerProps) => { - const initialWidth = props.drawerWidth || 1200 + const initialWidth = props.initialWidth || 1200 const [drawerWidth, setDrawerWidth] = useState(initialWidth) return ( diff --git a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx index 422b67405b..4e90496723 100644 --- a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx @@ -14,7 +14,7 @@ import {useAppId} from "@/hooks/useAppId" import {useQueryParam} from "@/hooks/useQuery" import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" import {getNodeById} from "@/lib/helpers/observability_helpers" -import {Filter, FilterConditions, JSSTheme} from "@/lib/Types" +import {Filter, FilterConditions, JSSTheme, KeyValuePair} from "@/lib/Types" import {_AgentaRootsResponse} from "@/services/observability/types" import {ReloadOutlined, SwapOutlined} from "@ant-design/icons" import { @@ -83,7 +83,7 @@ const ObservabilityDashboard = () => { const [isTestsetDrawerOpen, setIsTestsetDrawerOpen] = useState(false) const [selectedRowKeys, setSelectedRowKeys] = useState([]) const [testsetDrawerData, setTestsetDrawerData] = useState< - {key: string; data: Record}[] + {key: string; data: KeyValuePair; id: number}[] >([]) const [columns, setColumns] = useState>([ { @@ -504,7 +504,7 @@ const ObservabilityDashboard = () => { const extractData = traces .filter((trace) => selectedRowKeys.includes(trace.key)) - .flatMap((trace) => ({data: trace.data, key: trace.key})) + .flatMap((trace, idx) => ({data: trace.data, key: trace.key, id: idx + 1})) if (extractData.length > 0) { setTestsetDrawerData(extractData as any) diff --git a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx index 43e23c6058..02d489dae7 100644 --- a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx @@ -28,6 +28,7 @@ import {collectKeyPathsFromObject, getStringOrJson} from "@/lib/helpers/utils" import yaml from "js-yaml" import {useUpdateEffect} from "usehooks-ts" import {ResizableTitle} from "@/components/ServerTable/components" +import useResizeObserver from "@/hooks/useResizeObserver" const useStyles = createUseStyles((theme: JSSTheme) => ({ editor: { @@ -54,7 +55,8 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) type Mapping = {data: string; column: string; newColumn?: string} -type TraceData = {key: string; data: Record} +type TraceData = {key: string; data: KeyValuePair; id: number} +type Preview = {key: string; data: KeyValuePair[]} type Props = { onClose: () => void data: TraceData[] @@ -64,7 +66,11 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { const {appTheme} = useAppTheme() const classes = useStyles() const {testsets: listOfTestsets, isTestsetsLoading} = useLoadTestsetsList() + const elemRef = useResizeObserver((rect) => { + setIsDrawerExtended(rect.width > 640) + }) + const [isDrawerExtended, setIsDrawerExtended] = useState(false) const [isLoading, setIsLoading] = useState(false) const [traceData, setTraceData] = useState(data.length > 0 ? data : []) const [testset, setTestset] = useState({name: "", id: ""}) @@ -75,15 +81,13 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { const [showLastFiveRows, setShowLastFiveRows] = useState(false) const [dataPreview, setDataPreview] = useState(traceData[0]?.key || "") const [mappingData, setMappingData] = useState([]) - const [preview, setPreview] = useState<{key: string; data: KeyValuePair[]}>({ - key: traceData[0]?.key || "", - data: [], - }) + const [preview, setPreview] = useState({key: traceData[0]?.key || "", data: []}) + const isNewTestset = testset.id === "create" + const elementWidth = isDrawerExtended ? 200 * 2 : 200 const isMapColumnExist = mappingData.some((mapping) => - mapping.column === "create" ? !!mapping?.newColumn : !!mapping.column, + mapping.column === "create" || !mapping.column ? !!mapping?.newColumn : !!mapping.column, ) - const isNewTestset = testset.id === "create" // predefind options const customSelectOptions = useMemo( @@ -103,13 +107,7 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { const {value, label} = option try { - if (value === "create" && tableColumns.length > 0) { - setTableColumns([]) - setTableRows([]) - setShowLastFiveRows(false) - setMappingData((prev) => prev.map((item) => ({...item, column: ""}))) - } - + resetStates() setTestset({name: label, id: value}) if (value && value !== "create") { @@ -135,6 +133,10 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { removeTrace[currentIndex] || removeTrace[currentIndex - 1] || removeTrace[0] setDataPreview(nextPreview.key) + + if (dataPreview === preview.key) { + onPreviewOptionChange(nextPreview.key) + } } else { setDataPreview("") } @@ -157,19 +159,29 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { }, [editorFormat, traceData, dataPreview]) const mappingOptions = useMemo(() => { - const traceKeys = collectKeyPathsFromObject({data: traceData[0]?.data}) + const uniquePaths = new Set() - return traceKeys.map((item) => ({value: item, label: item})) - }, [traceData]) + traceData.forEach((traceItem) => { + const traceKeys = collectKeyPathsFromObject(traceItem?.data, "data") + traceKeys.forEach((key) => uniquePaths.add(key)) + }) + + return Array.from(uniquePaths).map((item) => ({value: item})) + }, [data]) useEffect(() => { // auto render mapping component with data if (mappingOptions.length > 0) { - setMappingData(mappingOptions.map((item) => ({data: item.value, column: ""}))) + setMappingData((prevMappingData) => + mappingOptions.map((item, index) => ({ + ...prevMappingData[index], + data: item.value, + })), + ) } }, [mappingOptions]) - const filteredColumnOptions = useMemo(() => { + const columnOptions = useMemo(() => { const selectedColumns = mappingData .map((item) => item.column) .filter((col) => col !== "create") @@ -193,8 +205,13 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { } const onPreviewOptionChange = (value: string) => { - const selectedTraceData = traceData.filter((trace) => trace.key === value) - const newTestsetData = mapAndConvertDataInCsvFormat(selectedTraceData) + let newTestsetData + if (value === "all") { + newTestsetData = mapAndConvertDataInCsvFormat(traceData) + } else { + const selectedTraceData = traceData.filter((trace) => trace.key === value) + newTestsetData = mapAndConvertDataInCsvFormat(selectedTraceData) + } setPreview({key: value, data: newTestsetData}) } @@ -205,6 +222,15 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { } }, [mappingData]) + const resetStates = () => { + setTableColumns([]) + setTableRows([]) + setShowLastFiveRows(false) + setMappingData((prev) => prev.map((item) => ({...item, column: "", newColumn: ""}))) + setPreview({key: traceData[0]?.key || "", data: []}) + setTestsetName("") + } + const mapAndConvertDataInCsvFormat = (traceData: TraceData[]) => { return traceData.map((item) => { const formattedItem: Record = {} @@ -213,11 +239,18 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { const keys = mapping.data.split(".") let value = keys.reduce((acc: any, key) => acc?.[key], item) - const targetKey = mapping.column === "create" ? mapping.newColumn : mapping.column + const targetKey = + mapping.column === "create" || !mapping.column + ? mapping.newColumn + : mapping.column if (targetKey) { formattedItem[targetKey] = - typeof value === "string" ? value : JSON.stringify(value) + value === undefined || value === null + ? "" + : typeof value === "string" + ? value + : JSON.stringify(value) } } @@ -263,7 +296,7 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { destroyOnClose onClose={onClose} expandable - drawerWidth={640} + initialWidth={640} headerExtra="Add to test set" footer={
@@ -279,19 +312,18 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => {
} mainContent={ -
+
Spans selected {traceData.length} - {/******* testset completed ✅ *******/}
Test set
setTestsetName(e.target.value)} placeholder="Test set name" @@ -332,7 +364,6 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => {
- {/******* data-preview completed ✅ *******/}
Data preview @@ -340,25 +371,30 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => {
onMappingOptionChange({ @@ -420,32 +455,31 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { />
- + onMappingOptionChange({ + pathName: "column", + value, + idx, + }) + } + options={[ + ...(testset.id ? customSelectOptions : []), + ...columnOptions?.map((column) => ({ + value: column, + lable: column, + })), + ]} + /> + )} + + {data.column === "create" || isNewTestset ? ( +
onMappingOptionChange({ @@ -454,14 +488,14 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { idx, }) } - placeholder="Test set name" + placeholder="Column name" />
- )} + ) : null}
- {/******* preview almost completed ✔️ *******/}
Preview {isMapColumnExist ? ( <>
({ - title: - data.column === "create" - ? data.newColumn - : data.column, - dataIndex: - data.column === "create" + columns={mappingData.map((data, idx) => { + const columnData = + data.column === "create" || !data.column ? data.newColumn - : data.column, - key: idx, - onHeaderCell: () => ({ - style: {minWidth: 160}, - }), - }))} + : data.column + return { + title: columnData, + dataIndex: columnData, + key: idx, + width: 250, + onHeaderCell: () => ({ + style: {minWidth: 200}, + }), + } + })} dataSource={[ ...preview.data, ...(showLastFiveRows ? tableRows.slice(-5) : []), ]} + rowClassName={(_, index) => { + if (showLastFiveRows) { + const totalRows = + preview.data.length + + tableRows.slice(-5).length + + if ( + index >= + totalRows - tableRows.slice(-5).length + ) { + return "!bg-[#fafafa]" + } + } + return "" + }} components={{ header: { cell: ResizableTitle, }, }} - scroll={{x: true}} + scroll={{x: "max-content"}} bordered pagination={false} /> From 0fd4a2855f72d97ab99dbef5d2a3a0d91b9d25a4 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Tue, 3 Dec 2024 18:17:02 +0600 Subject: [PATCH 09/28] added testseet drawer in trace drawer --- .../observability/drawer/TraceContent.tsx | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx index fab6e3e258..eca6c96a05 100644 --- a/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TraceContent.tsx @@ -11,6 +11,7 @@ import {statusMapper} from "../components/AvatarTreeContent" import {formatCurrency, formatLatency, formatTokenUsage} from "@/lib/helpers/formatters" import StatusRenderer from "../components/StatusRenderer" import AccordionTreePanel from "../components/AccordionTreePanel" +import TestsetDrawer from "./TestsetDrawer" interface TraceContentProps { activeTrace: _AgentaRootsResponse @@ -85,6 +86,11 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { const classes = useStyles() const [tab, setTab] = useState("overview") const {icon, bgColor, color} = statusMapper(activeTrace.node.type) + const [isTestsetDrawerOpen, setIsTestsetDrawerOpen] = useState(false) + + const testsetDrawerData = () => { + return [{data: activeTrace.data, key: activeTrace.key, id: 1}] + } const transformDataInputs = (data: any) => { return Object.keys(data).reduce((acc, curr) => { @@ -260,8 +266,8 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { {activeTrace.node.name} - {/* - {!activeTrace.parent && activeTrace.refs?.application?.id && ( + + {/* {!activeTrace.parent && activeTrace.refs?.application?.id && ( - )} - - */} + @@ -356,6 +365,13 @@ const TraceContent = ({activeTrace}: TraceContentProps) => { /> + {isTestsetDrawerOpen && ( + setIsTestsetDrawerOpen(false)} + /> + )} {/*
Evaluation From 9e085677b159be5263e6c168fbc007e0e680e618 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Tue, 3 Dec 2024 20:11:15 +0600 Subject: [PATCH 10/28] fix(frontend): show mappings after selecting testsets --- .../observability/drawer/TestsetDrawer.tsx | 195 +++++++++--------- 1 file changed, 102 insertions(+), 93 deletions(-) diff --git a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx index 02d489dae7..68d4777167 100644 --- a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx @@ -117,6 +117,15 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { setTableRows(data.csvdata) } } + + if (mappingOptions.length > 0 && value) { + setMappingData((prevMappingData) => + mappingOptions.map((item, index) => ({ + ...prevMappingData[index], + data: item.value, + })), + ) + } } catch (error) { message.error("Failed to laod Test sets!") } @@ -169,18 +178,6 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { return Array.from(uniquePaths).map((item) => ({value: item})) }, [data]) - useEffect(() => { - // auto render mapping component with data - if (mappingOptions.length > 0) { - setMappingData((prevMappingData) => - mappingOptions.map((item, index) => ({ - ...prevMappingData[index], - data: item.value, - })), - ) - } - }, [mappingOptions]) - const columnOptions = useMemo(() => { const selectedColumns = mappingData .map((item) => item.column) @@ -435,99 +432,111 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => {
Mapping -
- {mappingData.map((data, idx) => ( -
- onMappingOptionChange({ - pathName: "column", + pathName: "data", value, idx, }) } - options={[ - ...(testset.id ? customSelectOptions : []), - ...columnOptions?.map((column) => ({ - value: column, - lable: column, - })), - ]} + options={mappingOptions} /> - )} - - {data.column === "create" || isNewTestset ? ( -
- - onMappingOptionChange({ - pathName: "newColumn", - value: e.target.value, - idx, - }) - } - placeholder="Column name" - /> - + +
+ {!isNewTestset && ( + + onMappingOptionChange({ + pathName: "newColumn", + value: e.target.value, + idx, + }) + } + placeholder="Column name" + /> + +
+ ) : null}
- ) : null} -
-
+ ))}
- ))} -
- + + + ) : ( + + Please select a test set to create mappings + + )}
From a822c665cda0f5e47709e8aa3e9ba42d890fa110 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Tue, 3 Dec 2024 21:13:46 +0600 Subject: [PATCH 11/28] refactor(frontend): made the states name more meaningful --- .../observability/ObservabilityDashboard.tsx | 30 ++++--- .../observability/drawer/TestsetDrawer.tsx | 80 ++++++++++--------- .../observability/drawer/TraceContent.tsx | 9 +-- 3 files changed, 60 insertions(+), 59 deletions(-) diff --git a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx index 4e90496723..88500c3bf8 100644 --- a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx @@ -41,7 +41,7 @@ import {convertToCsv, downloadCsv} from "@/lib/helpers/fileManipulations" import {useUpdateEffect} from "usehooks-ts" import {getStringOrJson} from "@/lib/helpers/utils" import ObservabilityContextProvider, {useObservabilityData} from "@/contexts/observability.context" -import TestsetDrawer from "./drawer/TestsetDrawer" +import TestsetDrawer, {TestsetTraceData} from "./drawer/TestsetDrawer" const useStyles = createUseStyles((theme: JSSTheme) => ({ title: { @@ -80,11 +80,8 @@ const ObservabilityDashboard = () => { const [selectedTraceId, setSelectedTraceId] = useQueryParam("trace", "") const [editColumns, setEditColumns] = useState(["span_type", "key", "usage"]) const [isFilterColsDropdownOpen, setIsFilterColsDropdownOpen] = useState(false) - const [isTestsetDrawerOpen, setIsTestsetDrawerOpen] = useState(false) const [selectedRowKeys, setSelectedRowKeys] = useState([]) - const [testsetDrawerData, setTestsetDrawerData] = useState< - {key: string; data: KeyValuePair; id: number}[] - >([]) + const [testsetDrawerData, setTestsetDrawerData] = useState([]) const [columns, setColumns] = useState>([ { title: "ID", @@ -499,16 +496,18 @@ const ObservabilityDashboard = () => { setSort({type, sorted, customRange}) }, []) - const getMatchingTracesByDataKeys = () => { + const getTestsetTraceData = () => { if (!traces?.length) return [] - const extractData = traces - .filter((trace) => selectedRowKeys.includes(trace.key)) - .flatMap((trace, idx) => ({data: trace.data, key: trace.key, id: idx + 1})) + const extractData = traces.reduce((acc, trace, idx) => { + if (selectedRowKeys.includes(trace.key)) { + acc.push({data: trace.data as KeyValuePair, key: trace.key, id: idx + 1}) + } + return acc + }, []) if (extractData.length > 0) { - setTestsetDrawerData(extractData as any) - setIsTestsetDrawerOpen(true) + setTestsetDrawerData(extractData) } } @@ -562,7 +561,7 @@ const ObservabilityDashboard = () => { Export as CSV
- {isTestsetDrawerOpen && ( + {testsetDrawerData.length > 0 && ( 0} data={testsetDrawerData} onClose={() => { - setSelectedRowKeys([]) setTestsetDrawerData([]) - setIsTestsetDrawerOpen(false) + setSelectedRowKeys([]) }} /> )} diff --git a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx index 68d4777167..4c686d02a7 100644 --- a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx @@ -1,4 +1,4 @@ -import React, {useEffect, useMemo, useState} from "react" +import {useMemo, useState} from "react" import GenericDrawer from "@/components/GenericDrawer" import {ArrowRight, PencilSimple, Plus, Trash} from "@phosphor-icons/react" import { @@ -55,11 +55,11 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ })) type Mapping = {data: string; column: string; newColumn?: string} -type TraceData = {key: string; data: KeyValuePair; id: number} type Preview = {key: string; data: KeyValuePair[]} +export type TestsetTraceData = {key: string; data: KeyValuePair; id: number} type Props = { onClose: () => void - data: TraceData[] + data: TestsetTraceData[] } & React.ComponentProps const TestsetDrawer = ({onClose, data, ...props}: Props) => { @@ -74,17 +74,18 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { const [isLoading, setIsLoading] = useState(false) const [traceData, setTraceData] = useState(data.length > 0 ? data : []) const [testset, setTestset] = useState({name: "", id: ""}) - const [testsetName, setTestsetName] = useState("") + const [newTestsetName, setNewTestsetName] = useState("") const [editorFormat, setEditorFormat] = useState("JSON") - const [tableColumns, setTableColumns] = useState([]) - const [tableRows, setTableRows] = useState([]) + const [selectedTestsetColumns, setSelectedTestsetColumns] = useState([]) + const [selectedTestsetRows, setSelectedTestsetRows] = useState([]) const [showLastFiveRows, setShowLastFiveRows] = useState(false) - const [dataPreview, setDataPreview] = useState(traceData[0]?.key || "") + const [rowDataPreview, setRowDataPreview] = useState(traceData[0]?.key || "") const [mappingData, setMappingData] = useState([]) const [preview, setPreview] = useState({key: traceData[0]?.key || "", data: []}) const isNewTestset = testset.id === "create" const elementWidth = isDrawerExtended ? 200 * 2 : 200 + const selectedTestsetTestCases = selectedTestsetRows.slice(-5) const isMapColumnExist = mappingData.some((mapping) => mapping.column === "create" || !mapping.column ? !!mapping?.newColumn : !!mapping.column, ) @@ -113,8 +114,8 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { if (value && value !== "create") { const data = await fetchTestset(value) if (data?.csvdata?.length) { - setTableColumns(Object.keys(data.csvdata[0])) - setTableRows(data.csvdata) + setSelectedTestsetColumns(Object.keys(data.csvdata[0])) + setSelectedTestsetRows(data.csvdata) } } @@ -132,22 +133,22 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { } const onRemoveTraceData = () => { - const removeTrace = traceData.filter((trace) => trace.key !== dataPreview) + const removeTrace = traceData.filter((trace) => trace.key !== rowDataPreview) setTraceData(removeTrace) if (removeTrace.length > 0) { - const currentIndex = traceData.findIndex((trace) => trace.key === dataPreview) + const currentIndex = traceData.findIndex((trace) => trace.key === rowDataPreview) // [currentIndex]: Next option in list | [currentIndex - 1]: Previous option if next doesn't exist | [0]: Default to first option const nextPreview = removeTrace[currentIndex] || removeTrace[currentIndex - 1] || removeTrace[0] - setDataPreview(nextPreview.key) + setRowDataPreview(nextPreview.key) - if (dataPreview === preview.key) { + if (rowDataPreview === preview.key) { onPreviewOptionChange(nextPreview.key) } } else { - setDataPreview("") + setRowDataPreview("") } } @@ -155,7 +156,9 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { if (!traceData?.length) return "" const jsonObject = { - data: traceData.find((trace) => trace?.key === dataPreview)?.data || traceData[0]?.data, + data: + traceData.find((trace) => trace?.key === rowDataPreview)?.data || + traceData[0]?.data, } if (!jsonObject) return "" @@ -165,7 +168,7 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { message.error("Failed to convert JSON to YAML. Please ensure the data is valid.") return getStringOrJson(jsonObject) } - }, [editorFormat, traceData, dataPreview]) + }, [editorFormat, traceData, rowDataPreview]) const mappingOptions = useMemo(() => { const uniquePaths = new Set() @@ -176,14 +179,14 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { }) return Array.from(uniquePaths).map((item) => ({value: item})) - }, [data]) + }, [traceData]) const columnOptions = useMemo(() => { const selectedColumns = mappingData .map((item) => item.column) .filter((col) => col !== "create") - return tableColumns.filter((column) => !selectedColumns.includes(column)) - }, [mappingData, tableColumns]) + return selectedTestsetColumns.filter((column) => !selectedColumns.includes(column)) + }, [mappingData, selectedTestsetColumns]) const onMappingOptionChange = ({ pathName, @@ -220,15 +223,15 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { }, [mappingData]) const resetStates = () => { - setTableColumns([]) - setTableRows([]) + setSelectedTestsetColumns([]) + setSelectedTestsetRows([]) setShowLastFiveRows(false) setMappingData((prev) => prev.map((item) => ({...item, column: "", newColumn: ""}))) setPreview({key: traceData[0]?.key || "", data: []}) - setTestsetName("") + setNewTestsetName("") } - const mapAndConvertDataInCsvFormat = (traceData: TraceData[]) => { + const mapAndConvertDataInCsvFormat = (traceData: TestsetTraceData[]) => { return traceData.map((item) => { const formattedItem: Record = {} @@ -262,17 +265,17 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { const newTestsetData = mapAndConvertDataInCsvFormat(traceData) if (isNewTestset) { - if (!testsetName) { + if (!newTestsetName) { message.error("Please add a Test set name before saving it") return } - await createNewTestset(testsetName, newTestsetData) + await createNewTestset(newTestsetName, newTestsetData) message.success("Test set created successfully") } else { await updateTestset(testset.id as string, testset.name, [ ...newTestsetData, - ...tableRows, + ...selectedTestsetRows, ]) message.success("Test set updated successfully") } @@ -348,8 +351,8 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => {
setTestsetName(e.target.value)} + value={newTestsetName} + onChange={(e) => setNewTestsetName(e.target.value)} placeholder="Test set name" /> {
onMappingOptionChange({ pathName: "data", @@ -490,7 +491,8 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { {!isNewTestset && (
{ - const columnData = - data.column === "create" || !data.column - ? data.newColumn - : data.column - return { - title: columnData, - dataIndex: columnData, - key: idx, - width: 250, - onHeaderCell: () => ({ - style: {minWidth: 200}, - }), - } - })} + columns={tableColumns} dataSource={[ ...preview.data, ...(showLastFiveRows From b3377462e0a5cc24a09b9fb81699c025c4ebce94 Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 5 Dec 2024 00:04:57 +0600 Subject: [PATCH 21/28] fix(frontend): child node selection issue --- .../observability/ObservabilityDashboard.tsx | 12 +++---- .../observability/drawer/TestsetDrawer.tsx | 31 +++++++++++-------- 2 files changed, 23 insertions(+), 20 deletions(-) diff --git a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx index 88500c3bf8..46ac3a2c9c 100644 --- a/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx +++ b/agenta-web/src/components/pages/observability/ObservabilityDashboard.tsx @@ -499,15 +499,13 @@ const ObservabilityDashboard = () => { const getTestsetTraceData = () => { if (!traces?.length) return [] - const extractData = traces.reduce((acc, trace, idx) => { - if (selectedRowKeys.includes(trace.key)) { - acc.push({data: trace.data as KeyValuePair, key: trace.key, id: idx + 1}) - } - return acc - }, []) + const extractData = selectedRowKeys.map((key, idx) => { + const node = getNodeById(traces, key as string) + return {data: node?.data as KeyValuePair, key: node?.key, id: idx + 1} + }) if (extractData.length > 0) { - setTestsetDrawerData(extractData) + setTestsetDrawerData(extractData as TestsetTraceData[]) } } diff --git a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx index ddffd29608..10a9657805 100644 --- a/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx +++ b/agenta-web/src/components/pages/observability/drawer/TestsetDrawer.tsx @@ -92,18 +92,21 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { ) // predefind options - const customSelectOptions = useMemo( - () => [ + const customSelectOptions = (divider = true) => { + return [ {value: "create", label: "Create New"}, - { - value: "divider", - label: , - className: "!p-0 !m-0 !min-h-0.5 !cursor-default", - disabled: true, - }, - ], - [], - ) + ...(divider + ? [ + { + value: "divider", + label: , + className: "!p-0 !m-0 !min-h-0.5 !cursor-default", + disabled: true, + }, + ] + : []), + ] + } const onTestsetOptionChange = async (option: {label: string; value: string}) => { const {value, label} = option @@ -406,7 +409,7 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { } onChange={onTestsetOptionChange} options={[ - ...customSelectOptions, + ...customSelectOptions(listOfTestsets.length > 0), ...listOfTestsets.map((item: testset) => ({ value: item._id, label: item.name, @@ -558,7 +561,9 @@ const TestsetDrawer = ({onClose, data, ...props}: Props) => { } options={[ ...(testset.id - ? customSelectOptions + ? customSelectOptions( + columnOptions.length > 0, + ) : []), ...columnOptions?.map((column) => ({ value: column, From 7df375a21ef3713fc40ea0be22b534fddb63eca2 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Thu, 5 Dec 2024 11:14:06 +0100 Subject: [PATCH 22/28] chore(frontend): move constant export to a different file --- .../NoMobilePageWrapper/NoMobilePageWrapper.tsx | 13 +++---------- .../NoMobilePageWrapper/assets/constants.ts | 2 ++ 2 files changed, 5 insertions(+), 10 deletions(-) create mode 100644 agenta-web/src/components/NoMobilePageWrapper/assets/constants.ts diff --git a/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx b/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx index c8bbbf14d2..cddce9f5f0 100644 --- a/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx +++ b/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx @@ -6,6 +6,7 @@ import {createUseStyles} from "react-jss" import {JSSTheme} from "@/lib/Types" import {Transition} from "@headlessui/react" import {useRouter} from "next/router" +import { MOBILE_UNOPTIMIZED_APP_ROUTES } from "./assets/constants" const useStyles = createUseStyles((theme: JSSTheme) => ({ overlay: { @@ -13,14 +14,6 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ }, })) -// List of routes where the component should be displayed -const APP_ROUTES = [ - "/apps", - "/observability", - "/settings", - "/testsets" -] - const NoMobilePageWrapper: React.FC = ({children}) => { const [dismissed, setDismissed] = useState(false) const [shouldDisplay, setShouldDisplay] = useState(false) @@ -28,9 +21,9 @@ const NoMobilePageWrapper: React.FC = ({children}) => { const {pathname} = useRouter() const observerCallback = useCallback((bounds: DOMRectReadOnly) => { - setShouldDisplay((prevShouldDisplay) => { + setShouldDisplay(() => { if (dismissed) return false // keep hidden if already dismissed by the user - if (!APP_ROUTES.some((route) => pathname.startsWith(route))) return false + if (!MOBILE_UNOPTIMIZED_APP_ROUTES.some((route) => pathname.startsWith(route))) return false return bounds.width < 768 }) diff --git a/agenta-web/src/components/NoMobilePageWrapper/assets/constants.ts b/agenta-web/src/components/NoMobilePageWrapper/assets/constants.ts new file mode 100644 index 0000000000..e3299df819 --- /dev/null +++ b/agenta-web/src/components/NoMobilePageWrapper/assets/constants.ts @@ -0,0 +1,2 @@ +// List of routes where the component should be displayed +export const MOBILE_UNOPTIMIZED_APP_ROUTES = ["/apps", "/observability", "/settings", "/testsets"] From df59d14665e7d80cbf88c2d014c37490831c2e01 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Thu, 5 Dec 2024 11:15:01 +0100 Subject: [PATCH 23/28] enhance(frontend): use screen size token instead of using a hardcoded value --- .../NoMobilePageWrapper/NoMobilePageWrapper.tsx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx b/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx index cddce9f5f0..bbd7c0c591 100644 --- a/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx +++ b/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx @@ -1,5 +1,5 @@ import {type PropsWithChildren, useState, useCallback} from "react" -import {Typography, Button} from "antd" +import {Typography, Button, theme} from "antd" import clsx from "clsx" import useResizeObserver from "@/hooks/useResizeObserver" import {createUseStyles} from "react-jss" @@ -14,20 +14,23 @@ const useStyles = createUseStyles((theme: JSSTheme) => ({ }, })) +const {useToken} = theme + const NoMobilePageWrapper: React.FC = ({children}) => { const [dismissed, setDismissed] = useState(false) const [shouldDisplay, setShouldDisplay] = useState(false) const {overlay} = useStyles() const {pathname} = useRouter() + const {token} = useToken() const observerCallback = useCallback((bounds: DOMRectReadOnly) => { setShouldDisplay(() => { if (dismissed) return false // keep hidden if already dismissed by the user if (!MOBILE_UNOPTIMIZED_APP_ROUTES.some((route) => pathname.startsWith(route))) return false - return bounds.width < 768 + return bounds.width < token.screenMD }) - }, [dismissed, pathname]) + }, [dismissed, pathname, token.screenMD]) useResizeObserver( observerCallback, From 55dc7b3a7d4d513e27deb900b4da9ecc0c8c7ece Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Thu, 5 Dec 2024 11:30:20 +0100 Subject: [PATCH 24/28] chore(frontend): lints new files --- .../NoMobilePageWrapper.tsx | 27 ++++++++++--------- agenta-web/src/hooks/useResizeObserver.ts | 2 +- agenta-web/src/pages/_app.tsx | 9 ++++--- 3 files changed, 21 insertions(+), 17 deletions(-) diff --git a/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx b/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx index bbd7c0c591..2e39bfcba1 100644 --- a/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx +++ b/agenta-web/src/components/NoMobilePageWrapper/NoMobilePageWrapper.tsx @@ -6,7 +6,7 @@ import {createUseStyles} from "react-jss" import {JSSTheme} from "@/lib/Types" import {Transition} from "@headlessui/react" import {useRouter} from "next/router" -import { MOBILE_UNOPTIMIZED_APP_ROUTES } from "./assets/constants" +import {MOBILE_UNOPTIMIZED_APP_ROUTES} from "./assets/constants" const useStyles = createUseStyles((theme: JSSTheme) => ({ overlay: { @@ -23,20 +23,21 @@ const NoMobilePageWrapper: React.FC = ({children}) => { const {pathname} = useRouter() const {token} = useToken() - const observerCallback = useCallback((bounds: DOMRectReadOnly) => { - setShouldDisplay(() => { - if (dismissed) return false // keep hidden if already dismissed by the user - if (!MOBILE_UNOPTIMIZED_APP_ROUTES.some((route) => pathname.startsWith(route))) return false - - return bounds.width < token.screenMD - }) - }, [dismissed, pathname, token.screenMD]) - - useResizeObserver( - observerCallback, - typeof window !== "undefined" ? document.body : undefined, + const observerCallback = useCallback( + (bounds: DOMRectReadOnly) => { + setShouldDisplay(() => { + if (dismissed) return false // keep hidden if already dismissed by the user + if (!MOBILE_UNOPTIMIZED_APP_ROUTES.some((route) => pathname.startsWith(route))) + return false + + return bounds.width < token.screenMD + }) + }, + [dismissed, pathname, token.screenMD], ) + useResizeObserver(observerCallback, typeof window !== "undefined" ? document.body : undefined) + const handleDismiss = () => { setDismissed(true) } diff --git a/agenta-web/src/hooks/useResizeObserver.ts b/agenta-web/src/hooks/useResizeObserver.ts index f303a26cc7..476dd227d8 100644 --- a/agenta-web/src/hooks/useResizeObserver.ts +++ b/agenta-web/src/hooks/useResizeObserver.ts @@ -3,7 +3,7 @@ import {useLayoutEffect, useRef} from "react" const useResizeObserver = ( callback?: (entry: ResizeObserverEntry["contentRect"]) => void, element?: HTMLElement, - ) => { +) => { const ref = useRef(null) useLayoutEffect(() => { diff --git a/agenta-web/src/pages/_app.tsx b/agenta-web/src/pages/_app.tsx index f30098280d..823891aded 100644 --- a/agenta-web/src/pages/_app.tsx +++ b/agenta-web/src/pages/_app.tsx @@ -17,9 +17,12 @@ import "ag-grid-community/styles/ag-grid.css" import "ag-grid-community/styles/ag-theme-alpine.css" import {Inter} from "next/font/google" -const NoMobilePageWrapper = dynamic(() => import("@/components/NoMobilePageWrapper/NoMobilePageWrapper"), { - ssr: false -}) +const NoMobilePageWrapper = dynamic( + () => import("@/components/NoMobilePageWrapper/NoMobilePageWrapper"), + { + ssr: false, + }, +) const inter = Inter({ subsets: ["latin"], From aca6728f8c678a0cf00aa92ad8076e13b7123a8b Mon Sep 17 00:00:00 2001 From: ashrafchowdury Date: Thu, 5 Dec 2024 19:28:20 +0600 Subject: [PATCH 25/28] fix(frontend): logout button --- agenta-web/src/components/Sidebar/Sidebar.tsx | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/agenta-web/src/components/Sidebar/Sidebar.tsx b/agenta-web/src/components/Sidebar/Sidebar.tsx index a90e43fef6..e3c0c403f2 100644 --- a/agenta-web/src/components/Sidebar/Sidebar.tsx +++ b/agenta-web/src/components/Sidebar/Sidebar.tsx @@ -381,21 +381,18 @@ const Sidebar: React.FC = () => { { key: "logout", label: ( -
{ - AlertPopup({ - title: "Logout", - message: - "Are you sure you want to logout?", - onOk: logout, - }) - }} - > +
Logout
), + onClick: () => { + AlertPopup({ + title: "Logout", + message: "Are you sure you want to logout?", + onOk: logout, + }) + }, }, ], selectedKeys: [selectedOrg.id], From df2cf73b83e17f1ecd69a00513007294a0f6c0da Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Thu, 5 Dec 2024 15:21:46 +0100 Subject: [PATCH 26/28] fix(frontend): include missing endpoints to the list of routes to display the new warning screen --- .../components/NoMobilePageWrapper/assets/constants.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/agenta-web/src/components/NoMobilePageWrapper/assets/constants.ts b/agenta-web/src/components/NoMobilePageWrapper/assets/constants.ts index e3299df819..058d997c1f 100644 --- a/agenta-web/src/components/NoMobilePageWrapper/assets/constants.ts +++ b/agenta-web/src/components/NoMobilePageWrapper/assets/constants.ts @@ -1,2 +1,9 @@ // List of routes where the component should be displayed -export const MOBILE_UNOPTIMIZED_APP_ROUTES = ["/apps", "/observability", "/settings", "/testsets"] +export const MOBILE_UNOPTIMIZED_APP_ROUTES = [ + "/apps", + "/observability", + "/settings", + "/testsets", + "/evaluations", + "/workspaces", +] From 76e931e4840623b001d96391c7f5a17f466a6696 Mon Sep 17 00:00:00 2001 From: Arda Erzin Date: Thu, 5 Dec 2024 16:26:55 +0100 Subject: [PATCH 27/28] chore(frontend): use dynamicComponent helper function --- agenta-web/src/pages/_app.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/agenta-web/src/pages/_app.tsx b/agenta-web/src/pages/_app.tsx index 823891aded..c84846caf9 100644 --- a/agenta-web/src/pages/_app.tsx +++ b/agenta-web/src/pages/_app.tsx @@ -9,6 +9,7 @@ import {PostHogProvider} from "posthog-js/react" import "@/styles/globals.css" import Layout from "@/components/Layout/Layout" +import {dynamicComponent} from "@/lib/helpers/dynamic" import ThemeContextProvider from "@/components/Layout/ThemeContextProvider" import AppContextProvider from "@/contexts/app.context" import ProfileContextProvider from "@/contexts/profile.context" @@ -17,12 +18,7 @@ import "ag-grid-community/styles/ag-grid.css" import "ag-grid-community/styles/ag-theme-alpine.css" import {Inter} from "next/font/google" -const NoMobilePageWrapper = dynamic( - () => import("@/components/NoMobilePageWrapper/NoMobilePageWrapper"), - { - ssr: false, - }, -) +const NoMobilePageWrapper = dynamicComponent("NoMobilePageWrapper/NoMobilePageWrapper") const inter = Inter({ subsets: ["latin"], From d141f87607d4c46bf56ff805270793d3117506df Mon Sep 17 00:00:00 2001 From: Juan Pablo Vega Date: Fri, 6 Dec 2024 10:09:24 +0100 Subject: [PATCH 28/28] Add org id and name --- agenta-backend/agenta_backend/routers/projects_router.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/agenta-backend/agenta_backend/routers/projects_router.py b/agenta-backend/agenta_backend/routers/projects_router.py index baed25c6f3..bbc5041ed7 100644 --- a/agenta-backend/agenta_backend/routers/projects_router.py +++ b/agenta-backend/agenta_backend/routers/projects_router.py @@ -14,6 +14,9 @@ class ProjectsResponse(BaseModel): + organization_id: Optional[UUID] = None + organization_name: Optional[str] = None + # is_default_organization: Optional[bool] = None workspace_id: Optional[UUID] = None workspace_name: Optional[str] = None # is_default_workspace: Optional[bool] = None @@ -21,7 +24,7 @@ class ProjectsResponse(BaseModel): project_name: str # is_default_project: bool user_role: Optional[str] = None - is_demo: Optional[bool] = False + is_demo: Optional[bool] = None router = APIRouter() @@ -65,6 +68,8 @@ async def get_projects( projects = [ ProjectsResponse( + organization_id=project_membership.project.organization.id, + organization_name=project_membership.project.organization.name, workspace_id=project_membership.project.workspace.id, workspace_name=project_membership.project.workspace.name, project_id=project_membership.project.id,