From 5eacfe1602837991bd998e0949535ed413185d1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9s?= Date: Thu, 26 Sep 2024 11:41:58 +0200 Subject: [PATCH] Show original document log from a evaluation log (#277) * Refactor refetch evaluation refetch stats to put each stats in their own compoment. The problem we had is that the catch all `useRefetchStats` hook we had was triggering for example numeric mean in non numeric evaluations. The reason is the way SWR stores work by default the store fetch data when it's mounted. This could be solved by disabling fetching on mount but I think is more correct to put each metric a listener to the `evaluationStatus` websocket event * Show in a modal document log metadata and messages associated with a evalaution result --- .../_components/Content/index.tsx | 51 ------- .../Content/useEvaluationStatus.ts | 72 ---------- .../_components/Content/useRefetchStats.ts | 79 ----------- .../EvaluationResultInfo/Metadata.tsx | 28 ++-- .../EvaluationResultInfo/index.tsx | 133 ++++++++++++++---- .../EvaluationStatusBanner/index.tsx | 53 ++++++- .../_components/EvaluationResults/index.tsx | 8 +- .../BigNumberPanels/MeanValuePanel/index.tsx | 11 +- .../BigNumberPanels/ModalValuePanel/index.tsx | 7 +- .../BigNumberPanels/TotalsPanels/index.tsx | 7 +- .../Charts/Numerical/CostOverResults.tsx | 16 ++- .../Charts/Numerical/ResultsOverTime.tsx | 11 +- .../_lib/useEvaluationStatusEvent.ts | 27 ++++ .../evaluations/[evaluationId]/layout.tsx | 12 +- .../DocumentLogs/DocumentLogInfo/Metadata.tsx | 2 +- .../DocumentLogs/DocumentLogInfo/index.tsx | 57 ++++++-- .../logs/_components/DocumentLogs/index.tsx | 10 +- .../src/app/api/documentLogs/[id]/route.ts | 40 ++++++ .../evaluationResultsCounters.ts | 2 +- .../evaluationResultsMeanValue.ts | 2 +- .../evaluationResultsModalValue.ts | 2 +- .../fetchDocumentLogWithMetadata.ts | 29 ++++ .../core/src/services/documentLogs/index.ts | 1 + .../computeEvaluationResultsWithMetadata.ts | 1 - packages/web-ui/src/ds/atoms/Icons/index.tsx | 2 + 25 files changed, 381 insertions(+), 282 deletions(-) delete mode 100644 apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/index.tsx delete mode 100644 apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/useEvaluationStatus.ts delete mode 100644 apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/useRefetchStats.ts create mode 100644 apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_lib/useEvaluationStatusEvent.ts create mode 100644 apps/web/src/app/api/documentLogs/[id]/route.ts create mode 100644 packages/core/src/services/documentLogs/fetchDocumentLogWithMetadata.ts diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/index.tsx deleted file mode 100644 index 0706c0b5b..000000000 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/index.tsx +++ /dev/null @@ -1,51 +0,0 @@ -'use client' - -import { - Commit, - EvaluationAggregationTotals, - EvaluationDto, - EvaluationMeanValue, - EvaluationModalValue, -} from '@latitude-data/core/browser' -import { EvaluationResultWithMetadata } from '@latitude-data/core/repositories' - -import { EvaluationResults } from '../EvaluationResults' -import { MetricsSummary } from '../MetricsSummary' -import { useEvaluationStatus } from './useEvaluationStatus' - -export default function Content({ - commit, - evaluation, - evaluationResults, - documentUuid, - aggregationTotals, - isNumeric, - meanOrModal, -}: { - commit: Commit - evaluation: EvaluationDto - documentUuid: string - evaluationResults: EvaluationResultWithMetadata[] - aggregationTotals: EvaluationAggregationTotals - isNumeric: T - meanOrModal: T extends true ? EvaluationMeanValue : EvaluationModalValue -}) { - const { jobs } = useEvaluationStatus({ evaluation }) - return ( - <> - - - - ) -} diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/useEvaluationStatus.ts b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/useEvaluationStatus.ts deleted file mode 100644 index 68e7850f9..000000000 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/useEvaluationStatus.ts +++ /dev/null @@ -1,72 +0,0 @@ -import { useCallback, useEffect, useRef, useState } from 'react' - -import { Evaluation } from '@latitude-data/core/browser' -import { useCurrentDocument } from '@latitude-data/web-ui' -import { - useSockets, - type EventArgs, -} from '$/components/Providers/WebsocketsProvider/useSockets' - -import { isEvaluationRunDone } from '../../_lib/isEvaluationRunDone' -import { useRefetchStats } from './useRefetchStats' - -const DISAPERING_IN_MS = 5000 - -export function useEvaluationStatus({ - evaluation, -}: { - evaluation: Evaluation -}) { - const timeoutRef = useRef(null) - const [jobs, setJobs] = useState[]>([]) - const document = useCurrentDocument() - const { refetchStats } = useRefetchStats({ evaluation, document }) - const onMessage = useCallback( - (args: EventArgs<'evaluationStatus'>) => { - if (evaluation.id !== args.evaluationId) return - if (document.documentUuid !== args.documentUuid) return - - const done = isEvaluationRunDone(args) - - if (done) { - refetchStats() - } - - setJobs((prevJobs) => { - const jobIndex = prevJobs.findIndex( - (job) => job.batchId === args.batchId, - ) - - if (jobIndex === -1) { - return [...prevJobs, args] - } else { - const newJobs = [...prevJobs] - newJobs[jobIndex] = args - - if (done) { - setTimeout(() => { - setJobs((currentJobs) => - currentJobs.filter((job) => job.batchId !== args.batchId), - ) - }, DISAPERING_IN_MS) - } - - return newJobs - } - }) - }, - [evaluation.id, document.documentUuid, refetchStats], - ) - - useEffect(() => { - return () => { - if (timeoutRef.current) { - clearTimeout(timeoutRef.current) - } - } - }, []) - - useSockets({ event: 'evaluationStatus', onMessage }) - - return { jobs } -} diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/useRefetchStats.ts b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/useRefetchStats.ts deleted file mode 100644 index d0899f45d..000000000 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/Content/useRefetchStats.ts +++ /dev/null @@ -1,79 +0,0 @@ -import { useCallback } from 'react' - -import { - DocumentVersion, - Evaluation, - EvaluationResultableType, -} from '@latitude-data/core/browser' -import { useCurrentCommit } from '@latitude-data/web-ui' -import useEvaluationResultsCounters from '$/stores/evaluationResultCharts/evaluationResultsCounters' -import useEvaluationResultsMeanValue from '$/stores/evaluationResultCharts/evaluationResultsMeanValue' -import useEvaluationResultsModalValue from '$/stores/evaluationResultCharts/evaluationResultsModalValue' -import useAverageResultsAndCostOverCommit from '$/stores/evaluationResultCharts/numericalResults/averageResultAndCostOverCommitStore' -import useAverageResultOverTime from '$/stores/evaluationResultCharts/numericalResults/averageResultOverTimeStore' - -export function useRefetchStats({ - evaluation, - document, -}: { - evaluation: Evaluation - document: DocumentVersion -}) { - const { commit } = useCurrentCommit() - const evaluationId = evaluation.id - const commitUuid = commit.uuid - const documentUuid = document.documentUuid - const isNumeric = - evaluation.configuration.type === EvaluationResultableType.Number - - const { refetch: refetchAverageResulstAndCostsOverCommit } = - useAverageResultsAndCostOverCommit({ - evaluation, - documentUuid, - }) - const { refetch: refetchAverageResultOverTime } = useAverageResultOverTime({ - evaluation, - documentUuid, - }) - const { refetch: refetchMean } = useEvaluationResultsMeanValue({ - commitUuid, - documentUuid, - evaluationId, - }) - const { refetch: refetchModal } = useEvaluationResultsModalValue({ - commitUuid, - documentUuid, - evaluationId, - }) - const { refetch: refetchTotals } = useEvaluationResultsCounters({ - commitUuid, - documentUuid, - evaluationId, - }) - - const refetchStats = useCallback(() => { - console.log('refetchStats') - - Promise.all([ - refetchTotals(), - ...(isNumeric - ? [ - refetchMean(), - refetchAverageResulstAndCostsOverCommit(), - refetchAverageResultOverTime(), - ] - : [refetchModal()]), - ]) - }, [ - isNumeric, - refetchMean, - refetchModal, - refetchTotals, - refetchAverageResulstAndCostsOverCommit, - refetchAverageResultOverTime, - ]) - - return { - refetchStats, - } -} diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/Metadata.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/Metadata.tsx index c18a34b4c..49540c8b8 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/Metadata.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/Metadata.tsx @@ -15,6 +15,20 @@ import { format } from 'date-fns' import { ResultCellContent } from '../EvaluationResultsTable' +function getReasonFromProviderLog(providerLog?: ProviderLogDto) { + if (!providerLog) return '-' + + try { + const response = JSON.parse(providerLog?.response) + if (response) { + return response.reason || '-' + } + return '-' + } catch (e) { + return '-' + } +} + function MetadataItem({ label, stacked = false, @@ -138,17 +152,3 @@ export function EvaluationResultMetadata({ ) } - -function getReasonFromProviderLog(providerLog?: ProviderLogDto) { - if (!providerLog) return '-' - - try { - const response = JSON.parse(providerLog?.response) - if (response) { - return response.reason || '-' - } - return '-' - } catch (e) { - return '-' - } -} diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/index.tsx index 56464cb5b..be006dac8 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationResultInfo/index.tsx @@ -1,14 +1,79 @@ -'use client' - -import { useState } from 'react' +import { useCallback, useState } from 'react' import { EvaluationDto, ProviderLogDto } from '@latitude-data/core/browser' -import { EvaluationResultWithMetadata } from '@latitude-data/core/repositories' -import { TabSelector } from '@latitude-data/web-ui' +import { + DocumentLogWithMetadata, + EvaluationResultWithMetadata, +} from '@latitude-data/core/repositories' +import { + Button, + Icon, + Modal, + ReactStateDispatch, + TabSelector, +} from '@latitude-data/web-ui' +import useProviderLogs from '$/stores/providerLogs' +import useSWR from 'swr' +import { DocumentLogInfo } from '../../../../../logs/_components/DocumentLogs/DocumentLogInfo' import { EvaluationResultMessages } from './Messages' import { EvaluationResultMetadata } from './Metadata' +type MaybeDocumentLog = number | null | undefined + +function useFetchDocumentLog({ documentLogId }: { documentLogId: number }) { + const { + data: documentLog, + isLoading, + error, + } = useSWR( + ['documentLogs', documentLogId], + useCallback(async () => { + const response = await fetch(`/api/documentLogs/${documentLogId}`, { + credentials: 'include', + }) + + if (!response.ok) { + const error = await response.json() + throw new Error(error.message) + } + + return response.json() + }, [documentLogId]), + ) + return { documentLog, isLoading, error } +} + +export default function DocumentLogInfoModal({ + documentLogId, + onOpenChange, +}: { + documentLogId: number + onOpenChange: ReactStateDispatch +}) { + const { documentLog, isLoading, error } = useFetchDocumentLog({ + documentLogId, + }) + const { data: providerLogs } = useProviderLogs({ + documentLogUuid: documentLog?.uuid, + }) + return ( + onOpenChange(null)} + title='Document Log Info' + description='Detais of original document log' + > + + + ) +} + export function EvaluationResultInfo({ evaluation, evaluationResult, @@ -19,28 +84,44 @@ export function EvaluationResultInfo({ providerLog?: ProviderLogDto }) { const [selectedTab, setSelectedTab] = useState('metadata') + const [selected, setSelected] = useState(null) + const onClickOpen = useCallback(async () => { + setSelected(evaluationResult.documentLogId) + }, [evaluationResult.documentLogId]) return ( -
- -
- {selectedTab === 'metadata' && ( - - )} - {selectedTab === 'messages' && ( - - )} + <> +
+ +
+ {selectedTab === 'metadata' && ( + + )} + {selectedTab === 'messages' && ( + + )} + +
-
+ {selected ? ( + + ) : null} + ) } diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationStatusBanner/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationStatusBanner/index.tsx index 03920171d..0e9b0a97f 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationStatusBanner/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/EvaluationStatusBanner/index.tsx @@ -1,14 +1,61 @@ 'use client' +import { useEffect, useRef, useState } from 'react' + import { ProgressIndicator } from '@latitude-data/web-ui' -import { isEvaluationRunDone } from '$/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_lib/isEvaluationRunDone' import { type EventArgs } from '$/components/Providers/WebsocketsProvider/useSockets' +import { isEvaluationRunDone } from '../../../_lib/isEvaluationRunDone' +import { useEvaluationStatusEvent } from '../../../_lib/useEvaluationStatusEvent' + +const DISAPERING_IN_MS = 5000 + export function EvaluationStatusBanner({ - jobs, + documentUuid, + evaluationId, }: { - jobs: EventArgs<'evaluationStatus'>[] + evaluationId: number + documentUuid: string }) { + const timeoutRef = useRef(null) + const [jobs, setJobs] = useState[]>([]) + useEvaluationStatusEvent({ + evaluationId, + documentUuid, + onStatusChange: (args) => { + setJobs((prevJobs) => { + const jobIndex = prevJobs.findIndex( + (job) => job.batchId === args.batchId, + ) + + if (jobIndex === -1) { + return [...prevJobs, args] + } else { + const newJobs = [...prevJobs] + newJobs[jobIndex] = args + + if (isEvaluationRunDone(args)) { + setTimeout(() => { + setJobs((currentJobs) => + currentJobs.filter((job) => job.batchId !== args.batchId), + ) + }, DISAPERING_IN_MS) + } + + return newJobs + } + }) + }, + }) + + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current) + } + } + }, []) + return ( <> {jobs.map((job) => ( diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/index.tsx index cd1d6a43f..91bb72847 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/EvaluationResults/index.tsx @@ -27,11 +27,9 @@ import { EvaluationStatusBanner } from './EvaluationStatusBanner' export function EvaluationResults({ evaluation, evaluationResults: serverData, - jobs, }: { evaluation: EvaluationDto evaluationResults: EvaluationResultWithMetadata[] - jobs: EventArgs<'evaluationStatus'>[] }) { const { project } = useCurrentProject() const { commit } = useCurrentCommit() @@ -72,7 +70,10 @@ export function EvaluationResults({ return (
Evaluation Results - +
{evaluationResults.length === 0 && ( @@ -107,6 +108,7 @@ export function EvaluationResults({
{selectedResult && ( refetch(), [refetch]) + useEvaluationStatusEvent({ + evaluationId: evaluation.id, + documentUuid, + onStatusChange, + }) const config = evaluation.configuration.detail! const defaultMinValue = config.range.from const defaultMaxValue = config.range.to diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/BigNumberPanels/ModalValuePanel/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/BigNumberPanels/ModalValuePanel/index.tsx index ceb6033c1..29b823444 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/BigNumberPanels/ModalValuePanel/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/BigNumberPanels/ModalValuePanel/index.tsx @@ -1,9 +1,12 @@ 'use client' +import { useCallback } from 'react' + import { EvaluationModalValue } from '@latitude-data/core/browser' import { Text } from '@latitude-data/web-ui' import useEvaluationResultsModalValue from '$/stores/evaluationResultCharts/evaluationResultsModalValue' +import { useEvaluationStatusEvent } from '../../../../_lib/useEvaluationStatusEvent' import Panel from '../Panel' export default function ModalValuePanel({ @@ -17,7 +20,7 @@ export default function ModalValuePanel({ evaluationId: number modal: EvaluationModalValue }) { - const { data } = useEvaluationResultsModalValue( + const { data, refetch } = useEvaluationResultsModalValue( { commitUuid, documentUuid, @@ -27,6 +30,8 @@ export default function ModalValuePanel({ fallbackData: modal, }, ) + const onStatusChange = useCallback(() => refetch(), [refetch]) + useEvaluationStatusEvent({ evaluationId, documentUuid, onStatusChange }) return ( refetch(), [refetch]) + useEvaluationStatusEvent({ evaluationId, documentUuid, onStatusChange }) const cost = data?.costInMillicents === undefined ? '-' diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/Charts/Numerical/CostOverResults.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/Charts/Numerical/CostOverResults.tsx index f6557c9f1..6dbe1c07a 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/Charts/Numerical/CostOverResults.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/Charts/Numerical/CostOverResults.tsx @@ -1,6 +1,6 @@ 'use client' -import { useMemo } from 'react' +import { useCallback, useMemo } from 'react' import { Evaluation } from '@latitude-data/core/browser' import { @@ -11,6 +11,7 @@ import { } from '@latitude-data/web-ui' import useAverageResultsAndCostOverCommit from '$/stores/evaluationResultCharts/numericalResults/averageResultAndCostOverCommitStore' +import { useEvaluationStatusEvent } from '../../../../_lib/useEvaluationStatusEvent' import { ChartWrapper, NoData } from '../ChartContainer' export function CostOverResultsChart({ @@ -20,11 +21,18 @@ export function CostOverResultsChart({ evaluation: Evaluation documentUuid: string }) { - const { isLoading, error, data } = useAverageResultsAndCostOverCommit({ - evaluation, + const { isLoading, error, data, refetch } = + useAverageResultsAndCostOverCommit({ + evaluation, + documentUuid, + }) + const { commit } = useCurrentCommit() + const onStatusChange = useCallback(() => refetch(), [refetch]) + useEvaluationStatusEvent({ + evaluationId: evaluation.id, documentUuid, + onStatusChange, }) - const { commit } = useCurrentCommit() const parsedData = useMemo( () => diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/Charts/Numerical/ResultsOverTime.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/Charts/Numerical/ResultsOverTime.tsx index 400625ff6..de7701478 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/Charts/Numerical/ResultsOverTime.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_components/MetricsSummary/Charts/Numerical/ResultsOverTime.tsx @@ -1,11 +1,12 @@ 'use client' -import { useMemo } from 'react' +import { useCallback, useMemo } from 'react' import { Evaluation } from '@latitude-data/core/browser' import { AreaChart, Text } from '@latitude-data/web-ui' import useAverageResultOverTime from '$/stores/evaluationResultCharts/numericalResults/averageResultOverTimeStore' +import { useEvaluationStatusEvent } from '../../../../_lib/useEvaluationStatusEvent' import { ChartWrapper, NoData } from '../ChartContainer' const formatDate = (date: number) => { @@ -22,10 +23,16 @@ export function ResultOverTimeChart({ evaluation: Evaluation documentUuid: string }) { - const { isLoading, error, data } = useAverageResultOverTime({ + const { isLoading, error, data, refetch } = useAverageResultOverTime({ evaluation, documentUuid, }) + const onStatusChange = useCallback(() => refetch(), [refetch]) + useEvaluationStatusEvent({ + evaluationId: evaluation.id, + documentUuid, + onStatusChange, + }) const minDate = useMemo(() => { if (!data || data.length === 0) return 0 diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_lib/useEvaluationStatusEvent.ts b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_lib/useEvaluationStatusEvent.ts new file mode 100644 index 000000000..accb19456 --- /dev/null +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/evaluations/[evaluationId]/_lib/useEvaluationStatusEvent.ts @@ -0,0 +1,27 @@ +import { useCallback } from 'react' + +import { + EventArgs, + useSockets, +} from '$/components/Providers/WebsocketsProvider/useSockets' + +export function useEvaluationStatusEvent({ + evaluationId, + documentUuid, + onStatusChange, +}: { + evaluationId: number + documentUuid: string + onStatusChange: (args: EventArgs<'evaluationStatus'>) => void +}) { + const onMessage = useCallback( + (args: EventArgs<'evaluationStatus'>) => { + if (evaluationId !== args.evaluationId) return + if (documentUuid !== args.documentUuid) return + + onStatusChange(args) + }, + [evaluationId, documentUuid, onStatusChange], + ) + useSockets({ event: 'evaluationStatus', onMessage }) +} 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 afa9d0f94..431925feb 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 @@ -24,7 +24,8 @@ import { ROUTES } from '$/services/routes' import Link from 'next/link' import { Actions } from './_components/Actions' -import Content from './_components/Content' +import { EvaluationResults } from './_components/EvaluationResults' +import { MetricsSummary } from './_components/MetricsSummary' const TYPE_TEXT: Record = { [EvaluationResultableType.Text]: 'Text', @@ -179,15 +180,18 @@ export default async function ConnectedEvaluationLayout({ /> } /> - +
) } diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/Metadata.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/Metadata.tsx index f439d6c50..a280ee222 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/Metadata.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/Metadata.tsx @@ -13,7 +13,7 @@ import { formatCostInMillicents, formatDuration } from '$/app/_lib/formatUtils' import useProviderApiKeys from '$/stores/providerApiKeys' import { format } from 'date-fns' -function MetadataItem({ +export function MetadataItem({ label, value, loading, diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/index.tsx index 8a5eb676b..41fe73e01 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/DocumentLogInfo/index.tsx @@ -4,21 +4,38 @@ import { useState } from 'react' import { ProviderLogDto } from '@latitude-data/core/browser' import { DocumentLogWithMetadata } from '@latitude-data/core/repositories' -import { TabSelector } from '@latitude-data/web-ui' +import { Alert, TabSelector } from '@latitude-data/web-ui' import { DocumentLogMessages } from './Messages' -import { DocumentLogMetadata } from './Metadata' +import { DocumentLogMetadata, MetadataItem } from './Metadata' + +function DocumentLogMetadataLoading() { + return ( +
+ + + + + + +
+ ) +} export function DocumentLogInfo({ documentLog, providerLogs, + isLoading = false, + error, }: { documentLog: DocumentLogWithMetadata providerLogs?: ProviderLogDto[] + isLoading?: boolean + error?: Error }) { const [selectedTab, setSelectedTab] = useState('metadata') return ( -
+ <>
- {selectedTab === 'metadata' && ( - - )} - {selectedTab === 'messages' && ( - + {isLoading ? ( + + ) : ( + <> + {!error ? ( + <> + {selectedTab === 'metadata' && ( + + )} + {selectedTab === 'messages' && ( + + )} + + ) : ( + + )} + )}
-
+ ) } diff --git a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/index.tsx b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/index.tsx index 02ad672da..f29464659 100644 --- a/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/index.tsx +++ b/apps/web/src/app/(private)/projects/[projectId]/versions/[commitUuid]/documents/[documentUuid]/logs/_components/DocumentLogs/index.tsx @@ -30,10 +30,12 @@ export function DocumentLogs({ />
{selectedLog && ( - +
+ +
)}
) diff --git a/apps/web/src/app/api/documentLogs/[id]/route.ts b/apps/web/src/app/api/documentLogs/[id]/route.ts new file mode 100644 index 000000000..522662314 --- /dev/null +++ b/apps/web/src/app/api/documentLogs/[id]/route.ts @@ -0,0 +1,40 @@ +import { Workspace } from '@latitude-data/core/browser' +import { database } from '@latitude-data/core/client' +import { fetchDocumentLogWithMetadata } from '@latitude-data/core/services/documentLogs/fetchDocumentLogWithMetadata' +import { authHandler } from '$/middlewares/authHandler' +import { errorHandler } from '$/middlewares/errorHandler' +import { NextRequest, NextResponse } from 'next/server' + +export const GET = errorHandler( + authHandler( + async ( + _: NextRequest, + { + params, + workspace, + }: { + params: { id: number } + workspace: Workspace + }, + ) => { + const id = params.id + + if (!id) { + return NextResponse.json( + { message: `Document Log not found with ID: ${id}` }, + { status: 404 }, + ) + } + + const documentLog = await fetchDocumentLogWithMetadata( + { + workspaceId: workspace.id, + documentLogId: id, + }, + database, + ).then((res) => res.unwrap()) + + return NextResponse.json(documentLog, { status: 200 }) + }, + ), +) diff --git a/apps/web/src/stores/evaluationResultCharts/evaluationResultsCounters.ts b/apps/web/src/stores/evaluationResultCharts/evaluationResultsCounters.ts index 2352e0228..36e1b7b9f 100644 --- a/apps/web/src/stores/evaluationResultCharts/evaluationResultsCounters.ts +++ b/apps/web/src/stores/evaluationResultCharts/evaluationResultsCounters.ts @@ -35,7 +35,7 @@ export default function useEvaluationResultsCounters( return null } return data - }, [commitUuid, documentUuid, evaluationId, project.id]) + }, [commitUuid, documentUuid, evaluationId, project.id, toast]) const { data, isLoading, error, mutate } = useSWR( ['evaluationResultsCounters', commitUuid, documentUuid, evaluationId], fetcher, diff --git a/apps/web/src/stores/evaluationResultCharts/evaluationResultsMeanValue.ts b/apps/web/src/stores/evaluationResultCharts/evaluationResultsMeanValue.ts index 89db73114..9b87f8aa7 100644 --- a/apps/web/src/stores/evaluationResultCharts/evaluationResultsMeanValue.ts +++ b/apps/web/src/stores/evaluationResultCharts/evaluationResultsMeanValue.ts @@ -36,7 +36,7 @@ export default function useEvaluationResultsMeanValue( } return data - }, [commitUuid, documentUuid, evaluationId, project.id]) + }, [commitUuid, documentUuid, evaluationId, project.id, toast]) const { data, isLoading, error, mutate } = useSWR( ['evaluationResultsMeanQuery', commitUuid, documentUuid, evaluationId], fetcher, diff --git a/apps/web/src/stores/evaluationResultCharts/evaluationResultsModalValue.ts b/apps/web/src/stores/evaluationResultCharts/evaluationResultsModalValue.ts index 9ed184334..ca25b69b4 100644 --- a/apps/web/src/stores/evaluationResultCharts/evaluationResultsModalValue.ts +++ b/apps/web/src/stores/evaluationResultCharts/evaluationResultsModalValue.ts @@ -36,7 +36,7 @@ export default function useEvaluationResultsModalValue( } return data - }, [commitUuid, documentUuid, evaluationId, project.id]) + }, [commitUuid, documentUuid, evaluationId, project.id, toast]) const { data, isLoading, error, mutate } = useSWR( ['evaluationResultsModalQuery', commitUuid, documentUuid, evaluationId], fetcher, diff --git a/packages/core/src/services/documentLogs/fetchDocumentLogWithMetadata.ts b/packages/core/src/services/documentLogs/fetchDocumentLogWithMetadata.ts new file mode 100644 index 000000000..753117b5a --- /dev/null +++ b/packages/core/src/services/documentLogs/fetchDocumentLogWithMetadata.ts @@ -0,0 +1,29 @@ +import { eq } from 'drizzle-orm' + +import { database } from '../../client' +import { NotFoundError, Result, TypedResult } from '../../lib' +import { DocumentLogWithMetadata } from '../../repositories/documentLogsRepository' +import { createDocumentLogQuery } from './_createDocumentLogQuery' + +export async function fetchDocumentLogWithMetadata( + { + workspaceId, + documentLogId, + }: { + workspaceId: number + documentLogId: number + }, + db = database, +): Promise> { + const { scope, baseQuery } = createDocumentLogQuery(workspaceId, db) + const logs = await baseQuery.where(eq(scope.id, documentLogId)).limit(1) + const documentLog = logs[0] + + if (!documentLog) { + return Result.error( + new NotFoundError(`Document Log not found with ID: ${documentLogId}`), + ) + } + + return Result.ok(documentLog) +} diff --git a/packages/core/src/services/documentLogs/index.ts b/packages/core/src/services/documentLogs/index.ts index 7f8a6f4b1..b90681ae4 100644 --- a/packages/core/src/services/documentLogs/index.ts +++ b/packages/core/src/services/documentLogs/index.ts @@ -2,3 +2,4 @@ export * from './create' export * from './addMessages' export * from './computeDocumentLogWithMetadata' export * from './computeDocumentLogsWithMetadata' +export * from './fetchDocumentLogWithMetadata' diff --git a/packages/core/src/services/evaluationResults/computeEvaluationResultsWithMetadata.ts b/packages/core/src/services/evaluationResults/computeEvaluationResultsWithMetadata.ts index d8780cb78..f568c669e 100644 --- a/packages/core/src/services/evaluationResults/computeEvaluationResultsWithMetadata.ts +++ b/packages/core/src/services/evaluationResults/computeEvaluationResultsWithMetadata.ts @@ -27,7 +27,6 @@ export async function computeEvaluationResultsWithMetadata( ): Promise> { const { evaluationResultsScope, documentLogsScope, baseQuery } = createEvaluationResultQuery(workspaceId, db) - const query = baseQuery .where( and( diff --git a/packages/web-ui/src/ds/atoms/Icons/index.tsx b/packages/web-ui/src/ds/atoms/Icons/index.tsx index ccfb146ad..d3f81c83b 100644 --- a/packages/web-ui/src/ds/atoms/Icons/index.tsx +++ b/packages/web-ui/src/ds/atoms/Icons/index.tsx @@ -1,4 +1,5 @@ import { + ArrowRightIcon, CheckCircle2, ChevronDown, ChevronLeft, @@ -70,6 +71,7 @@ const Icons = { externalLink: ExternalLink, pencil: Pencil, refresh: RefreshCcw, + arrowRight: ArrowRightIcon, } export type IconName = keyof typeof Icons