diff --git a/apps/web/src/actions/evaluations/computeEvaluationResultsWithMetadata.ts b/apps/web/src/actions/evaluations/computeEvaluationResultsWithMetadata.ts new file mode 100644 index 000000000..1520853b9 --- /dev/null +++ b/apps/web/src/actions/evaluations/computeEvaluationResultsWithMetadata.ts @@ -0,0 +1,40 @@ +'use server' + +import { + CommitsRepository, + EvaluationsRepository, +} from '@latitude-data/core/repositories' +import { computeEvaluationResultsWithMetadata } from '@latitude-data/core/services/evaluationResults/computeEvaluationResultsWithMetadata' +import { z } from 'zod' + +import { withProject } from '../procedures' + +export const computeEvaluationResultsWithMetadataAction = withProject + .createServerAction() + .input( + z.object({ + evaluationId: z.number(), + documentUuid: z.string(), + commitUuid: z.string(), + }), + ) + .handler(async ({ input, ctx }) => { + const { documentUuid } = input + const { workspace, project } = ctx + const commitsScope = new CommitsRepository(workspace.id) + const evaluationScope = new EvaluationsRepository(workspace.id) + const evaluation = await evaluationScope + .find(input.evaluationId) + .then((r) => r.unwrap()) + const commit = await commitsScope + .getCommitByUuid({ projectId: project.id, uuid: input.commitUuid }) + .then((r) => r.unwrap()) + + return await computeEvaluationResultsWithMetadata({ + workspaceId: ctx.workspace.id, + evaluation, + documentUuid, + draft: commit, + limit: 1000, + }).then((r) => r.unwrap()) + }) diff --git a/apps/web/src/actions/evaluations/runBatch.ts b/apps/web/src/actions/evaluations/runBatch.ts index b15d8d7f0..ed3696099 100644 --- a/apps/web/src/actions/evaluations/runBatch.ts +++ b/apps/web/src/actions/evaluations/runBatch.ts @@ -10,11 +10,11 @@ import { nanoid } from 'nanoid' import { z } from 'zod' import { createServerActionProcedure } from 'zsa' -import { widthDocument } from '../procedures' +import { withDocument } from '../procedures' const USER_DECIDED_TO_IGNORE_THIS_PARAMETER = -1 -const withDataset = createServerActionProcedure(widthDocument) +const withDataset = createServerActionProcedure(withDocument) .input(z.object({ datasetId: z.number() })) .handler(async ({ input, ctx }) => { const datasetsRepo = new DatasetsRepository(ctx.workspace.id) diff --git a/apps/web/src/actions/procedures/index.ts b/apps/web/src/actions/procedures/index.ts index c5ae1fabd..32512117c 100644 --- a/apps/web/src/actions/procedures/index.ts +++ b/apps/web/src/actions/procedures/index.ts @@ -54,7 +54,7 @@ export const withProject = createServerActionProcedure(authProcedure) return { ...ctx, project } }) -export const widthDocument = createServerActionProcedure(withProject) +export const withDocument = createServerActionProcedure(withProject) .input(z.object({ commitUuid: z.string(), documentUuid: z.string() })) .handler(async ({ input, ctx }) => { const repo = new DocumentVersionsRepository(ctx.workspace.id) diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/Messages.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/Messages.tsx index 3d76def6c..38c540076 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/Messages.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/Messages.tsx @@ -25,6 +25,7 @@ export function EvaluationResultMessages({ return (
(undefined) + const document = useCurrentDocument() + const { project } = useCurrentProject() + const { commit } = useCurrentCommit() + const { data: evaluationResults, mutate } = useEvaluationResultsWithMetadata( + { + evaluationId: evaluation.id, + documentUuid: document.documentUuid, + commitUuid: commit.uuid, + projectId: project.id, + }, + { + fallbackData: serverData, + }, + ) + + useEffect(() => { + const interval = setInterval(() => { + mutate() + }, FIVE_SECONDS) + + return () => clearInterval(interval) + }, [mutate]) const { data: providerLog } = useProviderLog(selectedResult?.providerLogId) return ( diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/layout.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/layout.tsx index 293d97ccb..49c28dab3 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/layout.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/layout.tsx @@ -40,6 +40,7 @@ export default async function ConnectedEvaluationLayout({ evaluation, documentUuid: params.documentUuid, draft: commit, + limit: 1000, }).then((r) => r.unwrap()) return ( diff --git a/apps/web/src/stores/evaluationResultsWithMetadata.ts b/apps/web/src/stores/evaluationResultsWithMetadata.ts new file mode 100644 index 000000000..760099fe5 --- /dev/null +++ b/apps/web/src/stores/evaluationResultsWithMetadata.ts @@ -0,0 +1,51 @@ +import { useMemo } from 'react' + +import { EvaluationResultWithMetadata } from '@latitude-data/core/repositories' +import { useToast } from '@latitude-data/web-ui' +import { computeEvaluationResultsWithMetadataAction } from '$/actions/evaluations/computeEvaluationResultsWithMetadata' +import useSWR, { SWRConfiguration } from 'swr' + +const EMPTY_ARRAY: [] = [] +export default function useEvaluationResultsWithMetadata( + { + evaluationId, + documentUuid, + commitUuid, + projectId, + }: { + evaluationId: number + documentUuid: string + commitUuid: string + projectId: number + }, + opts: SWRConfiguration, +) { + const { toast } = useToast() + const { data = EMPTY_ARRAY, ...rest } = useSWR< + EvaluationResultWithMetadata[] + >( + ['evaluationResults', evaluationId, documentUuid, commitUuid, projectId], + async () => { + const [data, error] = await computeEvaluationResultsWithMetadataAction({ + evaluationId, + documentUuid, + commitUuid, + projectId, + }) + + if (error) { + toast({ + title: 'Error fetching evaluations', + description: error.formErrors?.[0] || error.message, + variant: 'destructive', + }) + throw error + } + + return data + }, + opts, + ) + + return useMemo(() => ({ data, ...rest }), [data, rest]) +} diff --git a/packages/core/src/services/evaluationResults/computeEvaluationResultsWithMetadata.ts b/packages/core/src/services/evaluationResults/computeEvaluationResultsWithMetadata.ts index 2c31443de..d8780cb78 100644 --- a/packages/core/src/services/evaluationResults/computeEvaluationResultsWithMetadata.ts +++ b/packages/core/src/services/evaluationResults/computeEvaluationResultsWithMetadata.ts @@ -15,18 +15,20 @@ export async function computeEvaluationResultsWithMetadata( evaluation, documentUuid, draft, + limit, }: { workspaceId: number evaluation: Evaluation documentUuid: string draft?: Commit + limit?: number }, db = database, ): Promise> { const { evaluationResultsScope, documentLogsScope, baseQuery } = createEvaluationResultQuery(workspaceId, db) - const result = await baseQuery + const query = baseQuery .where( and( eq(evaluationResultsScope.evaluationId, evaluation.id), @@ -36,5 +38,7 @@ export async function computeEvaluationResultsWithMetadata( ) .orderBy(desc(evaluationResultsScope.createdAt)) + const result = await (limit ? query.limit(limit) : query) + return Result.ok(result) } diff --git a/packages/jobs/src/job-definitions/batchEvaluations/runDocumentJob.test.ts b/packages/jobs/src/job-definitions/batchEvaluations/runDocumentJob.test.ts index 7c31072f8..d24ce70ea 100644 --- a/packages/jobs/src/job-definitions/batchEvaluations/runDocumentJob.test.ts +++ b/packages/jobs/src/job-definitions/batchEvaluations/runDocumentJob.test.ts @@ -97,13 +97,16 @@ describe('runDocumentJob', () => { expect( mocks.queues.defaultQueue.jobs.enqueueRunEvaluationJob, - ).toHaveBeenCalledWith({ - workspaceId: workspace.id, - documentUuid: document.documentUuid, - documentLogUuid: 'log1', - evaluationId: evaluation.id, - batchId: 'batch1', - }) + ).toHaveBeenCalledWith( + { + workspaceId: workspace.id, + documentUuid: document.documentUuid, + documentLogUuid: 'log1', + evaluationId: evaluation.id, + batchId: 'batch1', + }, + { lifo: true }, + ) expect(ProgressTracker.prototype.incrementErrors).not.toHaveBeenCalled() expect(ProgressTracker.prototype.decrementTotal).not.toHaveBeenCalled() diff --git a/packages/jobs/src/job-definitions/batchEvaluations/runDocumentJob.ts b/packages/jobs/src/job-definitions/batchEvaluations/runDocumentJob.ts index 2513d8dac..4770308ad 100644 --- a/packages/jobs/src/job-definitions/batchEvaluations/runDocumentJob.ts +++ b/packages/jobs/src/job-definitions/batchEvaluations/runDocumentJob.ts @@ -61,13 +61,16 @@ export const runDocumentJob = async (job: Job) => { const queues = await setupJobs() // Enqueue the evaluation job - await queues.defaultQueue.jobs.enqueueRunEvaluationJob({ - workspaceId, - documentUuid: document.documentUuid, - documentLogUuid: result.documentLogUuid, - evaluationId, - batchId, - }) + await queues.defaultQueue.jobs.enqueueRunEvaluationJob( + { + workspaceId, + documentUuid: document.documentUuid, + documentLogUuid: result.documentLogUuid, + evaluationId, + batchId, + }, + { lifo: true }, + ) } catch (error) { if (env.NODE_ENV !== 'production') { console.error(error) diff --git a/packages/web-ui/src/ds/molecules/Chat/MessageList/index.tsx b/packages/web-ui/src/ds/molecules/Chat/MessageList/index.tsx index d1683bffa..8cdb6ab62 100644 --- a/packages/web-ui/src/ds/molecules/Chat/MessageList/index.tsx +++ b/packages/web-ui/src/ds/molecules/Chat/MessageList/index.tsx @@ -1,6 +1,10 @@ +'use client' + +import { Fragment, useState } from 'react' + import { Message as ConversationMessage } from '@latitude-data/compiler' -import { Fragment } from 'react/jsx-runtime' +import { Button } from '../../../atoms' import { Message, MessageProps } from '../Message' export function MessageList({ @@ -8,29 +12,66 @@ export function MessageList({ variant, messageLayout, separator = false, + collapsed = false, size, }: { messages: ConversationMessage[] variant?: MessageProps['variant'] messageLayout?: MessageProps['layout'] + collapsed?: boolean size?: MessageProps['size'] separator?: boolean }) { - return messages.map((message, index) => ( - - {separator && index > 0 && ( -
+ const [isCollapsed, setIsCollapsed] = useState( + collapsed && messages.length > 1, + ) + + const visibleMessages = isCollapsed ? messages.slice(-1) : messages + const hiddenMessagesCount = messages.length - visibleMessages.length + + return ( +
+ {isCollapsed && messages.length > 1 && ( +
+
+ +
+
+
+ +
+
+ )} + {visibleMessages.map((message, index) => ( + + {separator && index > 0 && ( +
+ )} + + + ))} + {!isCollapsed && messages.length > 1 && ( +
+ +
)} - - - )) +
+ ) }