Skip to content

Commit

Permalink
Evaluation results dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
csansoon committed Sep 16, 2024
1 parent 40540b4 commit 0e3ed2f
Show file tree
Hide file tree
Showing 27 changed files with 843 additions and 19 deletions.
9 changes: 9 additions & 0 deletions apps/web/src/actions/providerLogs/fetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,12 @@ export const getProviderLogsAction = authProcedure

return result
})

export const getProviderLogAction = authProcedure
.createServerAction()
.input(z.object({ providerLogId: z.number() }))
.handler(async ({ input, ctx }) => {
const { providerLogId } = input
const scope = new ProviderLogsRepository(ctx.workspace.id)
return await scope.find(providerLogId).then((r) => r.unwrap())
})
9 changes: 9 additions & 0 deletions apps/web/src/app/(private)/_data-access/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,15 @@ export const getEvaluationByUuidCached = cache(async (uuid: string) => {
return evaluation
})

export const getEvaluationByIdCached = cache(async (id: number) => {
const { workspace } = await getCurrentUser()
const evaluationScope = new EvaluationsRepository(workspace.id)
const result = await evaluationScope.find(id)
const evaluation = result.unwrap()

return evaluation
})

export const getProviderLogCached = cache(async (uuid: string) => {
const { workspace } = await getCurrentUser()
const scope = new ProviderLogsRepository(workspace.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import {
TableRow,
Text,
} from '@latitude-data/web-ui'
import { formatCostInMillicents } from '$/app/_lib/formatCostInMillicents'
import { formatCostInMillicents } from '$/app/_lib/formatUtils'
import { useNavigate } from '$/hooks/useNavigate'
import { ROUTES } from '$/services/routes'
import useProjects from '$/stores/projects'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { readMetadata } from '@latitude-data/compiler'
import { EvaluationDto } from '@latitude-data/core/browser'
import { ConnectedDocumentWithMetadata } from '@latitude-data/core/repositories'
import { Skeleton, Text } from '@latitude-data/web-ui'
import { formatCostInMillicents } from '$/app/_lib/formatCostInMillicents'
import { formatCostInMillicents } from '$/app/_lib/formatUtils'
import useConnectedDocuments from '$/stores/connectedEvaluations'

export function Stat({ label, value }: { label: string; value?: string }) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useMemo } from 'react'

import { AssistantMessage, Message, MessageRole } from '@latitude-data/compiler'
import { ProviderLog } from '@latitude-data/core/browser'
import { MessageList } from '@latitude-data/web-ui'

export function EvaluationResultMessages({
providerLog,
}: {
providerLog?: ProviderLog
}) {
const messages = useMemo<Message[]>(() => {
if (!providerLog) return [] as Message[]

const responseMessage = {
role: MessageRole.assistant,
content: providerLog.responseText,
toolCalls: providerLog.toolCalls,
} as AssistantMessage

return [...(providerLog.messages as Message[]), responseMessage]
}, [providerLog])

if (!providerLog) return null
return (
<div className='flex flex-col gap-4 py-6 w-full max-h-full overflow-y-auto'>
<MessageList
messages={messages}
messageLayout='vertical'
separator
size='small'
/>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { ReactNode } from 'react'

import { ProviderLog } from '@latitude-data/core/browser'
import { EvaluationResultWithMetadata } from '@latitude-data/core/repositories'
import {
ClickToCopy,
Icon,
Skeleton,
Text,
Tooltip,
} from '@latitude-data/web-ui'
import { formatCostInMillicents } from '$/app/_lib/formatUtils'
import useProviderApiKeys from '$/stores/providerApiKeys'
import { format } from 'date-fns'

function MetadataItem({
label,
value,
loading,
children,
}: {
label: string
value?: string
loading?: boolean
children?: ReactNode
}) {
return (
<div className='flex flex-row justify-between items-center gap-2'>
<Text.H5M color='foreground'>{label}</Text.H5M>
{loading ? (
<Skeleton className='w-12 h-4 bg-muted-foreground/10' />
) : (
<>
{value && (
<Text.H5 align='right' color='foregroundMuted'>
{value}
</Text.H5>
)}
{children}
</>
)}
</div>
)
}

export function EvaluationResultMetadata({
evaluationResult,
providerLog,
}: {
evaluationResult: EvaluationResultWithMetadata
providerLog?: ProviderLog
}) {
const { data: providers, isLoading: providersLoading } = useProviderApiKeys()

return (
<div className='flex flex-col gap-6 py-6 w-full'>
<MetadataItem label='Result id'>
<ClickToCopy copyValue={evaluationResult.id.toString()}>
<Text.H5 align='right' color='foregroundMuted'>
{evaluationResult.id}
</Text.H5>
</ClickToCopy>
</MetadataItem>
<MetadataItem
label='Timestamp'
value={format(evaluationResult.createdAt, 'PPp')}
/>
<MetadataItem
label='Tokens'
loading={!providerLog}
value={providerLog?.tokens.toString()}
/>
<MetadataItem
label='Model'
loading={!providerLog}
value={providerLog?.model ?? 'Unknown'}
/>
<MetadataItem
label='Provider'
loading={!providerLog}
value={
providers.find((p) => p.id === providerLog?.providerId)?.name ??
'Unknown'
}
/>
<MetadataItem label='Cost' loading={!providerLog || providersLoading}>
<Tooltip
side='bottom'
align='end'
delayDuration={250}
trigger={
<div className='flex flex-row items-center gap-x-1'>
<Text.H5 color='foregroundMuted'>
{formatCostInMillicents(evaluationResult.costInMillicents ?? 0)}
</Text.H5>
<Icon name='info' className='text-muted-foreground' />
</div>
}
>
<div className='flex flex-col justify-between'>
<Text.H6 color='background'>
Note: This is just an estimate based on the token usage and your
provider's pricing. Actual cost may vary.
</Text.H6>
</div>
</Tooltip>
</MetadataItem>
<MetadataItem label='Version'>
<ClickToCopy copyValue={evaluationResult.commit.uuid}>
<Text.H5 align='right' color='foregroundMuted'>
{evaluationResult.commit.uuid.split('-')[0]}
</Text.H5>
</ClickToCopy>
</MetadataItem>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
'use client'

import { useState } from 'react'

import { ProviderLog } from '@latitude-data/core/browser'
import { EvaluationResultWithMetadata } from '@latitude-data/core/repositories'
import { TabSelector } from '@latitude-data/web-ui'

import { EvaluationResultMessages } from './Messages'
import { EvaluationResultMetadata } from './Metadata'

export function EvaluationResultInfo({
evaluationResult,
providerLog,
}: {
evaluationResult: EvaluationResultWithMetadata
providerLog?: ProviderLog
}) {
const [selectedTab, setSelectedTab] = useState<string>('metadata')
return (
<div className='w-80 flex-shrink-0 flex flex-col border border-border rounded-lg px-4 pt-6 items-center'>
<TabSelector
options={[
{ label: 'Metadata', value: 'metadata' },
{ label: 'Messages', value: 'messages' },
]}
selected={selectedTab}
onSelect={setSelectedTab}
/>
<div className='flex relative w-full h-full max-h-full max-w-full overflow-auto'>
{selectedTab === 'metadata' && (
<EvaluationResultMetadata
evaluationResult={evaluationResult}
providerLog={providerLog}
/>
)}
{selectedTab === 'messages' && (
<EvaluationResultMessages providerLog={providerLog} />
)}
</div>
</div>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
'use client'

import {
EvaluationDto,
EvaluationResultableType,
} from '@latitude-data/core/browser'
import { EvaluationResultWithMetadata } from '@latitude-data/core/repositories'
import {
Badge,
cn,
RangeBadge,
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
Text,
} from '@latitude-data/web-ui'
import { formatCostInMillicents, relativeTime } from '$/app/_lib/formatUtils'

const ResultCellContent = ({
evaluation,
value,
}: {
evaluation: EvaluationDto
value: unknown
}) => {
if (evaluation.configuration.type === EvaluationResultableType.Boolean) {
return (
<Badge variant={(value as boolean) ? 'success' : 'destructive'}>
{String(value)}
</Badge>
)
}

if (evaluation.configuration.type === EvaluationResultableType.Number) {
const minValue = evaluation.configuration.detail?.range.from ?? 0
const maxValue = evaluation.configuration.detail?.range.to ?? 10

return (
<RangeBadge
value={Number(value)}
minValue={minValue}
maxValue={maxValue}
/>
)
}

return <Text.H4 noWrap>{value as string}</Text.H4>
}

export const EvaluationResultsTable = ({
evaluation,
evaluationResults,
selectedResult,
setSelectedResult,
}: {
evaluation: EvaluationDto
evaluationResults: EvaluationResultWithMetadata[]
selectedResult: EvaluationResultWithMetadata | undefined
setSelectedResult: (log: EvaluationResultWithMetadata | undefined) => void
}) => {
return (
<Table className='table-auto'>
<TableHeader className='sticky top-0 z-10'>
<TableRow>
<TableHead>Time</TableHead>
<TableHead>Version</TableHead>
<TableHead>Result</TableHead>
<TableHead>Cost</TableHead>
<TableHead>Tokens</TableHead>
</TableRow>
</TableHeader>
<TableBody className='max-h-full overflow-y-auto'>
{evaluationResults.map((evaluationResult, idx) => (
<TableRow
key={idx}
onClick={() =>
setSelectedResult(
selectedResult?.id === evaluationResult.id
? undefined
: evaluationResult,
)
}
className={cn(
'cursor-pointer border-b-[0.5px] h-12 max-h-12 border-border',
{
'bg-secondary': selectedResult?.id === evaluationResult.id,
},
)}
>
<TableCell>
<Text.H4 noWrap>
{relativeTime(evaluationResult.createdAt)}
</Text.H4>
</TableCell>
<TableCell>
<div className='flex flex-row gap-2 items-center'>
<Badge
variant={evaluationResult.commit.version ? 'accent' : 'muted'}
shape='square'
>
<Text.H6 noWrap>
{evaluationResult.commit.version
? `v${evaluationResult.commit.version}`
: 'Draft'}
</Text.H6>
</Badge>
<Text.H5>{evaluationResult.commit.title}</Text.H5>
</div>
</TableCell>
<TableCell>
<ResultCellContent
evaluation={evaluation}
value={evaluationResult.result}
/>
</TableCell>
<TableCell>
<Text.H4 noWrap>
{formatCostInMillicents(evaluationResult.costInMillicents || 0)}
</Text.H4>
</TableCell>
<TableCell>
<Text.H4 noWrap>{evaluationResult.tokens}</Text.H4>
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)
}
Loading

0 comments on commit 0e3ed2f

Please sign in to comment.