Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test set drawer checkpoint-2 #2373

Merged
merged 19 commits into from
Dec 26, 2024
Merged
Changes from 5 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
1159897
refactor(frontend): divided code into different parts for better read…
ashrafchowdury Dec 12, 2024
0908e2c
enhance(frontend): dynamically importing the testset drawer
ashrafchowdury Dec 12, 2024
b299377
feat(forntend): made the span data editable
ashrafchowdury Dec 13, 2024
61620f6
feat(frontend): added auto map column names
ashrafchowdury Dec 13, 2024
bad0fff
enhance(frontend): added confirmation modal for adding new columns
ashrafchowdury Dec 15, 2024
7108890
feat(frontend): memorized last used configurations
ashrafchowdury Dec 16, 2024
d043d39
fix(frontend): editor data updates
ashrafchowdury Dec 16, 2024
fd7eede
fix(frontend): editor code update in yaml format
ashrafchowdury Dec 16, 2024
75cdabb
enhance(frontend): improved code performance
ashrafchowdury Dec 17, 2024
1e76c21
fix(frontend): confirm modal UI
ashrafchowdury Dec 17, 2024
c211190
Merge branch 'dev' of https://github.com/Agenta-AI/agenta into feat/t…
ashrafchowdury Dec 18, 2024
d52e98f
fix(frontend): added auto-complete on the column input
ashrafchowdury Dec 18, 2024
a2b6e2b
enhance(frontend): added spans different structures warning message
ashrafchowdury Dec 19, 2024
3b68d35
Merge branch 'dev' of https://github.com/Agenta-AI/agenta into feat/t…
ashrafchowdury Dec 20, 2024
ebeea8f
enhance(frontend): added support for duplicate columns
ashrafchowdury Dec 20, 2024
edd6b06
Merge branch dev of https://github.com/Agenta-AI/agenta int
ashrafchowdury Dec 20, 2024
a3d30b8
fix(frontend): type
ashrafchowdury Dec 23, 2024
33de4e4
enhance(frontend): added a new hook to replace useUpdateEffect hook
ashrafchowdury Dec 23, 2024
784a7bb
resolved merge conflict
ashrafchowdury Dec 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import {useCallback, useMemo, useState} from "react"
import GenericDrawer from "@/components/GenericDrawer"
import {ArrowRight, FloppyDiskBack, PencilSimple, Plus, Trash} from "@phosphor-icons/react"
import {
ArrowRight,
FloppyDiskBack,
PencilSimple,
Plus,
Trash,
WarningCircle,
} from "@phosphor-icons/react"
import {
Button,
Checkbox,
Expand All @@ -12,6 +19,7 @@ import {
Select,
Table,
Typography,
AutoComplete,
} from "antd"
import CopyButton from "@/components/CopyButton/CopyButton"
import {useAppTheme} from "@/components/Layout/ThemeContextProvider"
Expand Down Expand Up @@ -54,6 +62,7 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {
const [preview, setPreview] = useState<Preview>({key: traceData[0]?.key || "", data: []})
const [hasDuplicateColumns, setHasDuplicateColumns] = useState(false)
const [isConfirmSave, setIsConfirmSave] = useState(false)
const [isDifferStructureExist, setIsDifferStructureExist] = useState(false)

const isNewTestset = testset.id === "create"
const elementWidth = isDrawerExtended ? 200 * 2 : 200
Expand Down Expand Up @@ -88,9 +97,30 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {
if (data.length > 0) {
setTraceData(data)
setRowDataPreview(data[0]?.key || "")

const hasDiffer = hasStructuralDifference(data)
setIsDifferStructureExist(hasDiffer)
}
}, [data])

const hasStructuralDifference = useCallback(
(trace: TestsetTraceData[]): boolean => {
if (trace.length <= 1) return false

const referencePaths = collectKeyPathsFromObject(trace[0].data).sort().join(",")

for (let i = 1; i < trace.length; i++) {
const currentPaths = collectKeyPathsFromObject(trace[i].data).sort().join(",")

if (currentPaths !== referencePaths) {
return true
}
}
return false
},
[collectKeyPathsFromObject],
)

// predefind options
const customSelectOptions = useCallback((divider = true) => {
return [
Expand Down Expand Up @@ -191,7 +221,7 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {
const updatedColumns = new Set([
...selectedTestsetColumns.map((col) => col.column),
...newMappedData
.filter((item) => !testsetColumnsSet.has(item.column.toLowerCase()))
.filter((item) => item.column !== "create" && item.column)
.map((item) => item.column),
])

Expand All @@ -210,10 +240,10 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {
}, [traceData, testset])

const columnOptions = useMemo(() => {
const selectedColumns = mappingData
.map((item) => item.column)
.filter((col) => col !== "create")
return selectedTestsetColumns.filter(({column}) => !selectedColumns.includes(column))
return selectedTestsetColumns?.map(({column}) => ({
value: column,
lable: column,
ashrafchowdury marked this conversation as resolved.
Show resolved Hide resolved
}))
}, [mappingData, selectedTestsetColumns])

const onMappingOptionChange = ({
Expand Down Expand Up @@ -245,10 +275,10 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {
}

useUpdateEffect(() => {
ashrafchowdury marked this conversation as resolved.
Show resolved Hide resolved
const duplicatesExist = hasDuplicateColumnNames()
setHasDuplicateColumns(duplicatesExist)
const hasInvalidMappings = hasInvalidColumnMappings()
setHasDuplicateColumns(hasInvalidMappings)

if (!duplicatesExist && isMapColumnExist) {
if (!hasInvalidMappings && isMapColumnExist) {
onPreviewOptionChange(preview.key)
}
}, [mappingData])
Expand All @@ -264,30 +294,73 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {

const mapAndConvertDataInCsvFormat = useCallback(
(traceData: TestsetTraceData[], type: "preview" | "export") => {
// First identify duplicate columns and their data paths
const duplicateColumnMap = new Map<string, string[]>()
mappingData.forEach((mapping) => {
const targetKey =
mapping.column === "create" || !mapping.column
? mapping.newColumn
: mapping.column

if (targetKey) {
if (!duplicateColumnMap.has(targetKey)) {
duplicateColumnMap.set(targetKey, [mapping.data])
} else {
duplicateColumnMap.get(targetKey)!.push(mapping.data)
}
}
})

// Get columns that have duplicate mappings
const duplicateColumns = new Map(
Array.from(duplicateColumnMap.entries()).filter(([_, paths]) => paths.length > 1),
)

const formattedData = traceData.map((item) => {
const formattedItem: Record<string, any> = {}

// Handle non-duplicate columns first
for (const mapping of mappingData) {
const keys = mapping.data.split(".")
let value = keys.reduce((acc: any, key) => acc?.[key], item)

const targetKey =
mapping.column === "create" || !mapping.column
? mapping.newColumn
: mapping.column

if (targetKey) {
formattedItem[targetKey] =
value === undefined || value === null
if (!targetKey || duplicateColumns.has(targetKey)) {
continue // Skip duplicate columns for now
}

const keys = mapping.data.split(".")
let value = keys.reduce((acc: any, key) => acc?.[key], item)

formattedItem[targetKey] =
value === undefined || value === null
? ""
: typeof value === "string"
? value
: JSON.stringify(value)
}

// Handle duplicate columns
duplicateColumns.forEach((dataPaths, columnName) => {
const values = dataPaths
.map((path) => {
const keys = path.split(".")
const value = keys.reduce((acc: any, key) => acc?.[key], item)
return value === undefined || value === null
? ""
: typeof value === "string"
? value
: JSON.stringify(value)
}
}
})
.filter((val) => val !== "") // Remove empty values

formattedItem[columnName] = values.length > 0 ? values.join(" | ") : ""
})

for (const {column} of selectedTestsetColumns) {
if (!(column in formattedItem)) {
// Add empty values for missing columns
for (const {column, isNew} of selectedTestsetColumns) {
if (!(column in formattedItem) && !isNew) {
formattedItem[column] = ""
}
}
Expand Down Expand Up @@ -346,52 +419,66 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {
}
}

const hasDuplicateColumnNames = useCallback(() => {
const seenValues = new Set<string>()
const hasInvalidColumnMappings = useCallback(() => {
const columnMappings = new Map<string, Set<string>>() // Map of column name to set of paths

return mappingData.some((item) => {
const columnValues = [item.column, item.newColumn]
.filter(Boolean)
.filter((value) => value !== "create")
const columnName =
item.column === "create" || !item.column ? item.newColumn : item.column
if (!columnName || columnName === "create") return false

const span = item.data

return columnValues.some((value) => {
if (seenValues.has(value as string)) return true
seenValues.add(value as string)
if (!columnMappings.has(columnName)) {
columnMappings.set(columnName, new Set([span]))
return false
})
}

const existingSpans = columnMappings.get(columnName)!
if (existingSpans.has(span)) {
return true
}

existingSpans.add(span)
return false
})
}, [mappingData])

const tableColumns = useMemo(() => {
const mappedColumns = mappingData.map((data, idx) => {
const columnData =
data.column === "create" || !data.column ? data.newColumn : data.column

return {
// Get unique column names while preserving order
const uniqueColumns = new Set<string>()
const mappedColumns = mappingData
.map((data) => {
const columnData =
data.column === "create" || !data.column ? data.newColumn : data.column
return columnData
})
.filter((columnData) => {
if (!columnData || uniqueColumns.has(columnData)) return false
uniqueColumns.add(columnData)
return true
})
.map((columnData) => ({
title: columnData,
dataIndex: columnData,
key: idx,
key: columnData,
width: 250,
onHeaderCell: () => ({style: {minWidth: 200}}),
}
})
}))

const testsetColumns = showLastFiveRows
? selectedTestsetColumns.map((item) => ({
title: item.column,
dataIndex: item.column,
key: item.column,
width: 250,
onHeaderCell: () => ({style: {minWidth: 200}}),
}))
? selectedTestsetColumns
.filter((item) => !uniqueColumns.has(item.column))
.map((item) => ({
title: item.column,
dataIndex: item.column,
key: item.column,
width: 250,
onHeaderCell: () => ({style: {minWidth: 200}}),
}))
: []

// Remove duplicate columns and filter out columns without dataIndex
return [...mappedColumns, ...testsetColumns].filter(
(column, index, self) =>
column.dataIndex &&
self.findIndex((c) => c.dataIndex === column.dataIndex) === index,
)
return [...mappedColumns, ...testsetColumns]
}, [mappingData, selectedTestsetColumns, showLastFiveRows])

const onSaveEditedTrace = () => {
Expand Down Expand Up @@ -482,6 +569,16 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {
}
mainContent={
<section ref={elemRef} className="w-full flex flex-col gap-6">
{isDifferStructureExist && (
<Typography.Text
className="mb-1 flex items-center gap-1"
type="warning"
>
<WarningCircle size={16} /> Some of the selected spans have a
different structure than the others.
</Typography.Text>
)}

<Typography.Text className={classes.drawerHeading}>
Spans selected {traceData.length}
</Typography.Text>
Expand Down Expand Up @@ -668,36 +765,42 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {
options={[
...(testset.id
? customSelectOptions(
columnOptions.length > 0,
selectedTestsetColumns.length >
0,
)
: []),
...columnOptions?.map(({column}) => ({
value: column,
lable: column,
})),
...columnOptions,
]}
/>

{data.column === "create" ? (
<div className="w-full relative">
<Input
style={{width: "100%"}}
value={data.newColumn || ""}
onChange={(e) =>
onMappingOptionChange({
pathName: "newColumn",
value: e.target.value,
idx,
})
}
placeholder="Column name"
/>
<PencilSimple
size={14}
className="absolute top-[8px] right-2"
/>
</div>
) : null}
{data.column === "create" && (
<AutoComplete
style={{width: "100%"}}
options={columnOptions}
onSelect={(value) =>
onMappingOptionChange({
pathName: "newColumn",
value,
idx,
})
}
onChange={(value) =>
onMappingOptionChange({
pathName: "newColumn",
value,
idx,
})
}
placeholder="Column name"
filterOption={(inputValue, option) =>
option!.value
.toUpperCase()
.indexOf(
inputValue.toUpperCase(),
) !== -1
}
/>
)}
</div>

<Button
Expand Down Expand Up @@ -815,7 +918,7 @@ const TestsetDrawer = ({onClose, data, ...props}: TestsetDrawerProps) => {
>
<div className="flex flex-col gap-4 my-4">
<Typography.Text>
You have created new columns. Do you want to add them to the
You have created new columns. Do you want to add them to the{" "}
<span className="font-bold">{testset.name}</span> test set?
</Typography.Text>

Expand Down