From a11f5377a011cb4c46e1722da16a5fb4614e751f Mon Sep 17 00:00:00 2001 From: Devansh Mahant <86195162+devansh-m12@users.noreply.github.com> Date: Fri, 27 Dec 2024 16:48:13 +0530 Subject: [PATCH 1/2] added datapoint count on dataset table (#288) * eval time progression (#210) * initial work to compare evals * remove unnecessary div * design --------- Co-authored-by: Din * added datapoint count on dataset table * added types and empty check --------- Co-authored-by: Dinmukhamed Mailibay <47117969+dinmukhamedm@users.noreply.github.com> Co-authored-by: skull8888888 Co-authored-by: Din --- .../api/projects/[projectId]/datasets/route.ts | 16 +++++++++++++--- frontend/components/datasets/datasets.tsx | 13 +++++++++---- frontend/lib/dataset/types.ts | 4 ++++ 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/frontend/app/api/projects/[projectId]/datasets/route.ts b/frontend/app/api/projects/[projectId]/datasets/route.ts index 72502ffb..e627fc91 100644 --- a/frontend/app/api/projects/[projectId]/datasets/route.ts +++ b/frontend/app/api/projects/[projectId]/datasets/route.ts @@ -1,8 +1,8 @@ -import { and, desc, eq, inArray } from 'drizzle-orm'; +import { and, desc, eq, getTableColumns, inArray, sql } from 'drizzle-orm'; import { NextRequest } from 'next/server'; import { db } from '@/lib/db/drizzle'; -import { datasets } from '@/lib/db/migrations/schema'; +import { datasetDatapoints,datasets } from '@/lib/db/migrations/schema'; import { paginatedGet } from '@/lib/db/utils'; export async function POST( @@ -42,12 +42,22 @@ export async function GET( const pageSize = parseInt(req.nextUrl.searchParams.get('pageSize') ?? '50') || 50; const filters = [eq(datasets.projectId, projectId)]; + const { ...columns } = getTableColumns(datasets); + const datasetsData = await paginatedGet({ table: datasets, pageNumber, pageSize, filters, - orderBy: desc(datasets.createdAt) + orderBy: desc(datasets.createdAt), + columns: { + ...columns, + datapointsCount: sql`COALESCE(( + SELECT COUNT(*) + FROM ${datasetDatapoints} dp + WHERE dp.dataset_id = datasets.id + ), 0)::int`.as('datapointsCount') + } }); return new Response(JSON.stringify(datasetsData), { status: 200 }); diff --git a/frontend/components/datasets/datasets.tsx b/frontend/components/datasets/datasets.tsx index cd25a701..599950d7 100644 --- a/frontend/components/datasets/datasets.tsx +++ b/frontend/components/datasets/datasets.tsx @@ -17,7 +17,7 @@ import { DialogTrigger } from '@/components/ui/dialog'; import { useProjectContext } from '@/contexts/project-context'; -import { Dataset } from '@/lib/dataset/types'; +import { DatasetInfo } from '@/lib/dataset/types'; import { useToast } from '@/lib/hooks/use-toast'; import { PaginatedResponse } from '@/lib/types'; import { swrFetcher } from '@/lib/utils'; @@ -33,7 +33,7 @@ export default function Datasets() { const { projectId } = useProjectContext(); const router = useRouter(); - const { data, mutate } = useSWR>( + const { data, mutate } = useSWR>( `/api/projects/${projectId}/datasets/`, swrFetcher ); @@ -72,7 +72,7 @@ export default function Datasets() { setIsDeleteDialogOpen(false); }; - const columns: ColumnDef[] = [ + const columns: ColumnDef[] = [ { cell: ({ row }) => {row.original.id}, size: 300, @@ -83,6 +83,11 @@ export default function Datasets() { header: 'name', size: 300 }, + { + accessorKey: 'datapointsCount', + header: 'Datapoints Count', + size: 300 + }, { header: 'Created at', accessorKey: 'createdAt', @@ -107,7 +112,7 @@ export default function Datasets() { onRowClick={(row) => { router.push(`/project/${projectId}/datasets/${row.original.id}`); }} - getRowId={(row: Dataset) => row.id} + getRowId={(row: DatasetInfo) => row.id} columns={columns} data={data?.items} selectionPanel={(selectedRowIds) => ( diff --git a/frontend/lib/dataset/types.ts b/frontend/lib/dataset/types.ts index 20fe0582..e68e2bbd 100644 --- a/frontend/lib/dataset/types.ts +++ b/frontend/lib/dataset/types.ts @@ -5,6 +5,10 @@ export interface Dataset { indexedOn: string | null; } +export interface DatasetInfo extends Dataset { + datapointsCount: number; +} + export interface Datapoint { id: string; createdAt: string; From 247fc66f9a04867b6c6efa62cfed65c5445e2464 Mon Sep 17 00:00:00 2001 From: Dinmukhamed Mailibay <47117969+dinmukhamedm@users.noreply.github.com> Date: Fri, 27 Dec 2024 16:59:46 +0500 Subject: [PATCH 2/2] truncate data in datapoints and eval-datapoints (#293) * truncate data in datapoints and eval-datapoints * added skeleton --------- Co-authored-by: Robert Kim --- app-server/src/db/datapoints.rs | 12 ++++++-- .../datapoints/[datapointId]/route.ts | 20 +++++++++++++ .../evaluations/[evaluationId]/route.ts | 7 ++--- .../evaluations/[evaluationId]/page.tsx | 6 ++-- frontend/components/dataset/dataset-panel.tsx | 28 +++++++++++++------ frontend/components/dataset/dataset.tsx | 2 +- frontend/lib/evaluation/types.ts | 6 ++-- 7 files changed, 58 insertions(+), 23 deletions(-) diff --git a/app-server/src/db/datapoints.rs b/app-server/src/db/datapoints.rs index e94a33d5..d423ce8d 100644 --- a/app-server/src/db/datapoints.rs +++ b/app-server/src/db/datapoints.rs @@ -13,8 +13,8 @@ pub struct DatapointView { id: Uuid, created_at: DateTime, dataset_id: Uuid, - data: Value, - target: Option, + data: String, + target: Option, metadata: Option, } @@ -95,7 +95,13 @@ pub async fn get_datapoints( offset: i64, ) -> Result> { let datapoints = sqlx::query_as::<_, DatapointView>( - "SELECT id, dataset_id, data, target, metadata, created_at + "SELECT + id, + dataset_id, + SUBSTRING(data::text, 0, 100) as data, + SUBSTRING(target::text, 0, 100) as target, + metadata, + created_at FROM dataset_datapoints WHERE dataset_id = $1 ORDER BY diff --git a/frontend/app/api/projects/[projectId]/datasets/[datasetId]/datapoints/[datapointId]/route.ts b/frontend/app/api/projects/[projectId]/datasets/[datasetId]/datapoints/[datapointId]/route.ts index b16f3ded..acdee08f 100644 --- a/frontend/app/api/projects/[projectId]/datasets/[datasetId]/datapoints/[datapointId]/route.ts +++ b/frontend/app/api/projects/[projectId]/datasets/[datasetId]/datapoints/[datapointId]/route.ts @@ -7,6 +7,26 @@ import { db } from '@/lib/db/drizzle'; import { datasetDatapoints } from '@/lib/db/migrations/schema'; import { fetcher } from '@/lib/utils'; +export async function GET( + req: Request, + { + params + }: { params: { projectId: string; datasetId: string; datapointId: string } } +) { + const datapoint = await db.query.datasetDatapoints.findFirst({ + where: and( + eq(datasetDatapoints.id, params.datapointId), + eq(datasetDatapoints.datasetId, params.datasetId) + ) + }); + + if (!datapoint) { + return new Response('Datapoint not found', { status: 404 }); + } + + return new Response(JSON.stringify(datapoint), { status: 200 }); +} + export async function POST( req: Request, { diff --git a/frontend/app/api/projects/[projectId]/evaluations/[evaluationId]/route.ts b/frontend/app/api/projects/[projectId]/evaluations/[evaluationId]/route.ts index 8fc73385..5ac5b733 100644 --- a/frontend/app/api/projects/[projectId]/evaluations/[evaluationId]/route.ts +++ b/frontend/app/api/projects/[projectId]/evaluations/[evaluationId]/route.ts @@ -3,7 +3,7 @@ import { and, asc, eq, sql } from 'drizzle-orm'; import { db } from '@/lib/db/drizzle'; import { evaluationResults, - evaluations,evaluationScores + evaluations, evaluationScores } from '@/lib/db/migrations/schema'; export async function GET( @@ -39,11 +39,10 @@ export async function GET( id: evaluationResults.id, createdAt: evaluationResults.createdAt, evaluationId: evaluationResults.evaluationId, - data: evaluationResults.data, - target: evaluationResults.target, + data: sql`SUBSTRING(${evaluationResults.data}::text, 0, 100)`.as('data'), + target: sql`SUBSTRING(${evaluationResults.target}::text, 0, 100)`.as('target'), executorOutput: evaluationResults.executorOutput, scores: subQueryScoreCte.cteScores, - traceId: evaluationResults.traceId }) .from(evaluationResults) .leftJoin( diff --git a/frontend/app/project/[projectId]/evaluations/[evaluationId]/page.tsx b/frontend/app/project/[projectId]/evaluations/[evaluationId]/page.tsx index 963de31c..41d14990 100644 --- a/frontend/app/project/[projectId]/evaluations/[evaluationId]/page.tsx +++ b/frontend/app/project/[projectId]/evaluations/[evaluationId]/page.tsx @@ -6,7 +6,7 @@ import Evaluation from '@/components/evaluation/evaluation'; import { db } from '@/lib/db/drizzle'; import { evaluationResults, - evaluations,evaluationScores + evaluations, evaluationScores } from '@/lib/db/migrations/schema'; import { EvaluationResultsInfo } from '@/lib/evaluation/types'; @@ -70,8 +70,8 @@ async function getEvaluationInfo( id: evaluationResults.id, createdAt: evaluationResults.createdAt, evaluationId: evaluationResults.evaluationId, - data: evaluationResults.data, - target: evaluationResults.target, + data: sql`SUBSTRING(${evaluationResults.data}::text, 0, 100)`.as('data'), + target: sql`SUBSTRING(${evaluationResults.target}::text, 0, 100)`.as('target'), executorOutput: evaluationResults.executorOutput, scores: subQueryScoreCte.cteScores, traceId: evaluationResults.traceId diff --git a/frontend/components/dataset/dataset-panel.tsx b/frontend/components/dataset/dataset-panel.tsx index edca6b26..b88591e2 100644 --- a/frontend/components/dataset/dataset-panel.tsx +++ b/frontend/components/dataset/dataset-panel.tsx @@ -1,9 +1,10 @@ import { ChevronsRight, Loader2 } from 'lucide-react'; import { useEffect, useRef, useState } from 'react'; +import useSWR from 'swr'; import { useProjectContext } from '@/contexts/project-context'; -import { Datapoint } from '@/lib/dataset/types'; import { useToast } from '@/lib/hooks/use-toast'; +import { swrFetcher } from '@/lib/utils'; import { Button } from '../ui/button'; import Formatter from '../ui/formatter'; @@ -15,7 +16,7 @@ import { Skeleton } from '../ui/skeleton'; interface DatasetPanelProps { datasetId: string; indexedOn: string | null; - datapoint: Datapoint; + datapointId: string; onClose: () => void; } @@ -24,17 +25,21 @@ const AUTO_SAVE_TIMEOUT_MS = 750; export default function DatasetPanel({ datasetId, indexedOn, - datapoint, + datapointId, onClose, }: DatasetPanelProps) { const { projectId } = useProjectContext(); + const { data: datapoint, isLoading } = useSWR( + `/api/projects/${projectId}/datasets/${datasetId}/datapoints/${datapointId}`, + swrFetcher + ); // datapoint is DatasetDatapoint, i.e. result of one execution on a data point - const [newData, setNewData] = useState | null>(datapoint.data); + const [newData, setNewData] = useState | null>(datapoint?.data); const [newTarget, setNewTarget] = useState | null>( - datapoint.target + datapoint?.target ); const [newMetadata, setNewMetadata] = useState | null>( - datapoint.metadata + datapoint?.metadata ); const [isValidJsonData, setIsValidJsonData] = useState(true); const [isValidJsonTarget, setIsValidJsonTarget] = useState(true); @@ -52,7 +57,7 @@ export default function DatasetPanel({ } setSaving(true); const res = await fetch( - `/api/projects/${projectId}/datasets/${datasetId}/datapoints/${datapoint.id}`, + `/api/projects/${projectId}/datasets/${datasetId}/datapoints/${datapointId}`, { method: 'POST', headers: { @@ -92,16 +97,21 @@ export default function DatasetPanel({ }, [newData, newTarget, newMetadata]); useEffect(() => { + if (!datapoint) return; setNewData(datapoint.data); setNewTarget(datapoint.target); setNewMetadata(datapoint.metadata); }, [datapoint]); - return ( + return isLoading ? (
+ + + +
) : (