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

datapoint count and truncate data #294

Merged
merged 2 commits into from
Dec 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 9 additions & 3 deletions app-server/src/db/datapoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ pub struct DatapointView {
id: Uuid,
created_at: DateTime<Utc>,
dataset_id: Uuid,
data: Value,
target: Option<Value>,
data: String,
target: Option<String>,
metadata: Option<Value>,
}

Expand Down Expand Up @@ -95,7 +95,13 @@ pub async fn get_datapoints(
offset: i64,
) -> Result<Vec<DatapointView>> {
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,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SUBSTRING function in SQL should start from index 1, not 0. Update the query to SUBSTRING(data::text, 1, 100) and SUBSTRING(target::text, 1, 100) to correctly truncate the data and target fields.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine in postgres

SUBSTRING(target::text, 0, 100) as target,
metadata,
created_at
FROM dataset_datapoints
WHERE dataset_id = $1
ORDER BY
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider adding authentication and authorization checks in the GET method to ensure that only authorized users can access the datapoint data.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handled in middleware

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,
{
Expand Down
16 changes: 13 additions & 3 deletions frontend/app/api/projects/[projectId]/datasets/route.ts
Original file line number Diff line number Diff line change
@@ -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(
Expand Down Expand Up @@ -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<number>`COALESCE((
SELECT COUNT(*)
FROM ${datasetDatapoints} dp
WHERE dp.dataset_id = datasets.id
), 0)::int`.as('datapointsCount')
}
});

return new Response(JSON.stringify(datasetsData), { status: 200 });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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<string>`SUBSTRING(${evaluationResults.data}::text, 0, 100)`.as('data'),
target: sql<string>`SUBSTRING(${evaluationResults.target}::text, 0, 100)`.as('target'),
executorOutput: evaluationResults.executorOutput,
scores: subQueryScoreCte.cteScores,
traceId: evaluationResults.traceId
})
.from(evaluationResults)
.leftJoin(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand Down Expand Up @@ -70,8 +70,8 @@ async function getEvaluationInfo(
id: evaluationResults.id,
createdAt: evaluationResults.createdAt,
evaluationId: evaluationResults.evaluationId,
data: evaluationResults.data,
target: evaluationResults.target,
data: sql<string>`SUBSTRING(${evaluationResults.data}::text, 0, 100)`.as('data'),
target: sql<string>`SUBSTRING(${evaluationResults.target}::text, 0, 100)`.as('target'),
executorOutput: evaluationResults.executorOutput,
scores: subQueryScoreCte.cteScores,
traceId: evaluationResults.traceId
Expand Down
28 changes: 19 additions & 9 deletions frontend/components/dataset/dataset-panel.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -15,7 +16,7 @@ import { Skeleton } from '../ui/skeleton';
interface DatasetPanelProps {
datasetId: string;
indexedOn: string | null;
datapoint: Datapoint;
datapointId: string;
onClose: () => void;
}

Expand All @@ -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<Record<string, any> | null>(datapoint.data);
const [newData, setNewData] = useState<Record<string, any> | null>(datapoint?.data);
const [newTarget, setNewTarget] = useState<Record<string, any> | null>(
datapoint.target
datapoint?.target
);
const [newMetadata, setNewMetadata] = useState<Record<string, any> | null>(
datapoint.metadata
datapoint?.metadata
);
const [isValidJsonData, setIsValidJsonData] = useState(true);
const [isValidJsonTarget, setIsValidJsonTarget] = useState(true);
Expand All @@ -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: {
Expand Down Expand Up @@ -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 ? (<div className='p-4 space-y-2 h-full w-full'>
<Skeleton className="h-8" />
<Skeleton className="h-8" />
<Skeleton className="h-8" />
</div>) : (
<div className="flex flex-col h-full w-full">
<div className="h-12 flex flex-none space-x-2 px-3 items-center border-b">
<Button
variant={'ghost'}
variant='ghost'
className="px-1"
onClick={async () => {
await saveChanges();
Expand Down
2 changes: 1 addition & 1 deletion frontend/components/dataset/dataset.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ export default function Dataset({ dataset }: DatasetProps) {
<div className="w-full h-full flex">
<DatasetPanel
datasetId={dataset.id}
datapoint={selectedDatapoint}
datapointId={selectedDatapoint.id}
onClose={() => {
handleDatapointSelect(null);
mutate();
Expand Down
13 changes: 9 additions & 4 deletions frontend/components/datasets/datasets.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -33,7 +33,7 @@ export default function Datasets() {
const { projectId } = useProjectContext();

const router = useRouter();
const { data, mutate } = useSWR<PaginatedResponse<Dataset>>(
const { data, mutate } = useSWR<PaginatedResponse<DatasetInfo>>(
`/api/projects/${projectId}/datasets/`,
swrFetcher
);
Expand Down Expand Up @@ -72,7 +72,7 @@ export default function Datasets() {
setIsDeleteDialogOpen(false);
};

const columns: ColumnDef<Dataset>[] = [
const columns: ColumnDef<DatasetInfo>[] = [
{
cell: ({ row }) => <Mono>{row.original.id}</Mono>,
size: 300,
Expand All @@ -83,6 +83,11 @@ export default function Datasets() {
header: 'name',
size: 300
},
{
accessorKey: 'datapointsCount',
header: 'Datapoints Count',
size: 300
},
{
header: 'Created at',
accessorKey: 'createdAt',
Expand All @@ -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) => (
Expand Down
4 changes: 4 additions & 0 deletions frontend/lib/dataset/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ export interface Dataset {
indexedOn: string | null;
}

export interface DatasetInfo extends Dataset {
datapointsCount: number;
}

export interface Datapoint {
id: string;
createdAt: string;
Expand Down
6 changes: 3 additions & 3 deletions frontend/lib/evaluation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,9 @@ export type EvaluationDatapointPreview = {
evaluationId: string;
createdAt: string;
scores?: Record<string, any>;
data: Record<string, any>;
target: Record<string, any>;
executorOutput: Record<string, any>;
data: any;
target: any;
executorOutput: any;
traceId: string;
};

Expand Down
Loading